From 74e20a8e8e8098de799fb5bc4ca928dd65c2794e Mon Sep 17 00:00:00 2001 From: cmk-bonobo Date: Mon, 9 Mar 2026 16:06:42 +0000 Subject: [PATCH] MKP 2.2.0 released, bugfixes for rules with no levels set --- README.md | 2 ++ mailcow/agent_based/mailcow_domains.py | 15 +++++---- mailcow/agent_based/mailcow_info.py | 39 +++++++++++------------ mailcow/agent_based/mailcow_mailboxes.py | 3 ++ mkp/Mailcow-2.2.0.mkp | Bin 0 -> 13355 bytes 5 files changed, 33 insertions(+), 26 deletions(-) create mode 100644 mkp/Mailcow-2.2.0.mkp diff --git a/README.md b/README.md index ac248cb..9e69783 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,8 @@ Apply your changes ## Version history: +2026/03/09 2.2.0 Bugfixes for rules without levels + 2025/03/30 2.1.0 Migration to new plugin API done 2024/01/26 1.2.0 Added password store option for the API key, added version check diff --git a/mailcow/agent_based/mailcow_domains.py b/mailcow/agent_based/mailcow_domains.py index 6e3b871..8de3b1a 100644 --- a/mailcow/agent_based/mailcow_domains.py +++ b/mailcow/agent_based/mailcow_domains.py @@ -19,6 +19,9 @@ def get_state_upper( levels: tuple[int | float, int | float], value: int | float ) -> State: """returns OK/WARN/CRIT depending on the given parameters""" + if levels is None: + # a rule with no levels set is active, assume OK state + return State.OK warn, crit = levels if value >= crit: return State.CRIT @@ -117,7 +120,7 @@ def check_mailcow_domains(item, params, section): percent_storage_used_for_messages = domain[12] # create (main) service for used storage (domain quota) - _type, levels = params["levels_mailcow_domains_quota_used"] + _level_type, levels = params["levels_mailcow_domains_quota_used"] state_quota = get_state_upper(levels, percent_storage_used_for_messages) # create graph for used quota @@ -142,28 +145,28 @@ def check_mailcow_domains(item, params, section): yield Result(state=State.OK, notice=notice) # create service for number of configured mailboxes (percent) - _type, levels = params["levels_mailcow_domains_mailboxes_used"] + _level_type, levels = params["levels_mailcow_domains_mailboxes_used"] state_mailboxes = get_state_upper(levels, percent_used_mailboxes) yield Metric("mailcow_domains_mailboxes", percent_used_mailboxes, levels=levels) notice = f"Used mailboxes: {render.percent(percent_used_mailboxes)}, {number_of_mailboxes} of {max_number_of_mailboxes} in use" yield Result(state=state_mailboxes, notice=notice) # create service for number of configured aliases (percent) - _type, levels = params["levels_mailcow_domains_aliases_used"] + _level_type, levels = params["levels_mailcow_domains_aliases_used"] state_aliases = get_state_upper(levels, percent_used_aliases) yield Metric("mailcow_domains_aliases", percent_used_aliases, levels=levels) notice = f"Used aliases: {render.percent(percent_used_aliases)}, {number_of_aliases} of {max_number_of_aliases} in use" yield Result(state=state_aliases, notice=notice) # create service for number of messages - _type, levels = params["levels_mailcow_domains_num_messages"] + _level_type, levels = params["levels_mailcow_domains_num_messages"] state_messages = get_state_upper(levels, total_number_of_messages) yield Metric("mailcow_domains_messages", total_number_of_messages, levels=levels) notice = f"Number of messages: {total_number_of_messages}" yield Result(state=state_messages, notice=notice) # create service for number of configured aliases (absolute) - _type, levels = params["levels_mailcow_domains_num_aliases"] + _level_type, levels = params["levels_mailcow_domains_num_aliases"] state_aliases = get_state_upper(levels, number_of_aliases) yield Metric("mailcow_domains_configured_aliases", number_of_aliases, levels=levels) notice = ( @@ -172,7 +175,7 @@ def check_mailcow_domains(item, params, section): yield Result(state=state_aliases, notice=notice) # create service for number of configured mailboxes (absolute) - _type, levels = params["levels_mailcow_domains_num_mailboxes"] + _level_type, levels = params["levels_mailcow_domains_num_mailboxes"] state_mailboxes = get_state_upper(levels, number_of_mailboxes) yield Metric( "mailcow_domains_configured_mailboxes", number_of_mailboxes, levels=levels diff --git a/mailcow/agent_based/mailcow_info.py b/mailcow/agent_based/mailcow_info.py index a7c208f..7e44c62 100644 --- a/mailcow/agent_based/mailcow_info.py +++ b/mailcow/agent_based/mailcow_info.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 # pylint: disable=line-too-long, simplifiable-if-statement, missing-module-docstring, too-many-locals + # import necessary elements from API version 2 from cmk.agent_based.v2 import ( AgentSection, @@ -16,6 +17,9 @@ def get_state_upper( levels: tuple[int | float, int | float], value: int | float ) -> State: """returns OK/WARN/CRIT depending on the given parameters""" + if levels is None: + # a rule with no levels set is active, assume OK state + return State.OK warn, crit = levels if value >= crit: return State.CRIT @@ -62,9 +66,9 @@ def discover_mailcow_info(section): def check_mailcow_info(params, section): """the check function""" # get thresholds - _type, levels_num_domains = params["levels_num_domains"] - _type, levels_num_mailboxes = params["levels_num_mailboxes"] - _type, levels_num_global_messages = params["levels_num_global_messages"] + _level_type, levels_num_domains = params["levels_num_domains"] + _level_type, levels_num_mailboxes = params["levels_num_mailboxes"] + _level_type, levels_num_global_messages = params["levels_num_global_messages"] # get all section data version: str = section["version"] git_version: str = section["git_version"] @@ -74,17 +78,6 @@ def check_mailcow_info(params, section): num_mailboxes: int = section["num_mailboxes"] num_global_messages: int = section["num_global_messages"] - # create graphs for number of domains, mailboxes and messages - yield Metric(name="mc_num_domains", value=num_domains, levels=levels_num_domains) - yield Metric( - name="mc_num_mailboxes", value=num_mailboxes, levels=levels_num_mailboxes - ) - yield Metric( - name="mc_num_global_messages", - value=num_global_messages, - levels=levels_num_global_messages, - ) - # create overall result if check_version_enabled: if update_available: @@ -100,22 +93,28 @@ def check_mailcow_info(params, section): yield Result(state=state, summary=summary, details=details) # Create result for number of domains - warn, crit = levels_num_domains - state = get_state_upper((warn, crit), num_domains) + state = get_state_upper(levels_num_domains, num_domains) + yield Metric(name="mc_num_domains", value=num_domains, levels=levels_num_domains) notice = f"Number of domains: {num_domains}" if state != State.OK: yield Result(state=state, notice=notice) # Create result for number of mailboxes - warn, crit = levels_num_mailboxes - state = get_state_upper((warn, crit), num_mailboxes) + state = get_state_upper(levels_num_mailboxes, num_mailboxes) + yield Metric( + name="mc_num_mailboxes", value=num_mailboxes, levels=levels_num_mailboxes + ) notice = f"Number of mailboxes: {num_mailboxes}" if state != State.OK: yield Result(state=state, notice=notice) # Create result for number of global messages - warn, crit = levels_num_global_messages - state = get_state_upper((warn, crit), num_global_messages) + state = get_state_upper(levels_num_global_messages, num_global_messages) + yield Metric( + name="mc_num_global_messages", + value=num_global_messages, + levels=levels_num_global_messages, + ) notice = f"Number of messages: {num_global_messages}" if state != State.OK: yield Result(state=state, notice=notice) diff --git a/mailcow/agent_based/mailcow_mailboxes.py b/mailcow/agent_based/mailcow_mailboxes.py index a48cdb3..d85d538 100644 --- a/mailcow/agent_based/mailcow_mailboxes.py +++ b/mailcow/agent_based/mailcow_mailboxes.py @@ -20,6 +20,9 @@ def get_state_upper( levels: tuple[int | float, int | float], value: int | float ) -> State: """returns OK/WARN/CRIT depending on the given parameters""" + if levels is None: + # a rule with no levels set is active, assume OK state + return State.OK warn, crit = levels if value >= crit: return State.CRIT diff --git a/mkp/Mailcow-2.2.0.mkp b/mkp/Mailcow-2.2.0.mkp new file mode 100644 index 0000000000000000000000000000000000000000..28d198524f1af0a7fe9197492833281589d5ea08 GIT binary patch literal 13355 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

m3_+!ZblG4Jk`L#G zYWQlfUVl*38#-WA-D|wj^+Owo6LwpO*wCPbRmTA~a zY@VYeQ7t~@!&csg3Y1F@ATDtMDM^u2}Eg zv;=otDpqIlSTEz`uHa8@TSGgq=4x^|E>t1&`_*=sD6n0OOGa+0m{X|z%@Q^s^s#_{V4RxuL z#Fzo+@lZIVxrO})l)GQrx}9&jv0$2$@qzo3pDhId_`pJZJ_bWlJ2YPgW2tPZ^=Jw~ zxDNAD9A#9_ih~6jsfOYpY+OkAm2Li})ee+UDK0yl*@7Iwe}>w(wGz)=uFgh0b*tDb zwy=*jI1=jBw*m8=pBF!I2fi`r|3J@wVz0Kt9=nRO?5;W@%=GyCZ~sbaa01b{77!`+dGK<(ltO1XoM<3dHSWAdDsu3|mmYecTLnRBib91nDc*eh>tyQD4Rp zR}61;{o7Th_KUwJvinj05w3fy@4g2Od>#aH0VXSLjn*pwlpSY%q lw_Dd9vp4HP-G5%M^0u3O0RIpC%RdD{4hwB^f?0us{SQHsgMt76 literal 0 HcmV?d00001