MKP 3.2.0 released, fixed bugs when users are deleted and rules without levels are active

This commit is contained in:
2026-03-09 11:25:48 +00:00
parent bf470d5d6c
commit 7f58381fd3
9 changed files with 87 additions and 40 deletions

BIN
.README.md.swp Normal file

Binary file not shown.

View File

@@ -1,11 +1,16 @@
# Nextcloud CheckMK Special Agent
Monitors various aspects of Nextcloud instances like state, quota and disk usage of all users, number of apps with available updates, database php opcache hit rate and so on.
Gives additional information regarding versions of Nextcloud, database, number of storages and active users etc.
Tested only with Nextcloud versions 29/30
Tested only with Nextcloud versions 29/30/31/32
Tested only with MariaDB as database backend
The whole plugin is now migrated to the new plugin API version, introduced with CheckMK 2.3
Feel free to report other working environments.
Upgrade from older MKPs (before 3.1.1):
If you upgrade from a already installed MKP version before 3.1.0, you have to re-create your rules for the "Nextcloud Server Information" (reason: some parameter changes combined with the migration to the new plugin API).
@@ -18,7 +23,7 @@ Hint: It is always a good idea to create screenshots from all other Nextcloud ru
5. Re-create your rules with the previously saved information from steps 1 and 2
6. Apply your changes
Hint: You have to create an app password now for accessing your Nextcloud instance. For this to accomplish login to your Nextcloud server with an administrative user account. Go to "Personal Settings|Security" and take note of the section "Devices & Sessions". Create the app password via clicking the button "create new app password". You may use this password explicitly within the rule or store it first in the password safe of CheckMK.
Hint: You have to create an app password now for accessing your Nextcloud instance. For this to accomplish login to your Nextcloud server with an administrative user account. Go to "Personal Settings|Security" and take note of the section "Devices & Sessions". Create the app password via clicking the button "create new app password". You may use this password explicitly within the rule or store it first in the password safe of CheckMK (recommended).
General installation instructions:
@@ -40,7 +45,8 @@ Version History:
--
|Date|Version|Changes|
|----|-------|-------|
|2025/03/30|3.1.1|Migration to the new plugin API version finished, (hopefully) ready for CheckMK version 2.4
|2026/03/09|3.2.0|Fixed bugs when: Users are deleted, quotas not set, rules with no levels are active|
|2025/03/30|3.1.1|Migration to the new plugin API version finished, (hopefully) ready for CheckMK version 2.4|
|2023/01/13|2.5.2|Repackaged only to set the minimum required version back to 2.1.0p1, due to serveral user requests|
|2023/01/12|2.5.1|Added versions for apps with available updates|
|2023/01/12|2.4.1|Removed Parameter "token", switched to parameter "app password" only|

BIN
mkp/Nextcloud-3.2.0.mkp Normal file

Binary file not shown.

View File

@@ -2,6 +2,7 @@
# pylint: disable=missing-module-docstring, unused-argument, missing-function-docstring
# pylint: disable=line-too-long
from typing import Final
from collections.abc import Mapping
from typing import NotRequired, TypedDict
@@ -18,6 +19,8 @@ from cmk.agent_based.v2 import (
DiscoveryResult,
)
NO_LEVELS: Final[str] = "no_levels"
class _ItemData(TypedDict):
dbtype: NotRequired[str]
@@ -49,14 +52,18 @@ def check_nextcloud_database(params, section) -> CheckResult:
size = section[key]["size"]
dbtype = section[key]["dbtype"]
version = section[key]["version"]
_level_type, levels = params["levels_database_opcache_hit_rate"]
level_type, levels = params["levels_database_opcache_hit_rate"]
# create graph for opcache hit rate
if level_type == NO_LEVELS:
yield Metric("nc_database_opcache_hit_rate", opcache_hit_rate)
state = State.OK
else:
yield Metric(
"nc_database_opcache_hit_rate", opcache_hit_rate, levels=levels
)
state = get_state_lower(levels, opcache_hit_rate)
# create graph for database size
yield Metric("nc_database_size", size)
state = get_state_lower(levels, opcache_hit_rate)
summary = f"PHP OPCache hit rate: {render.percent(opcache_hit_rate)}"
if opcache_hit_rate == 0:
state = State.UNKNOWN

View File

@@ -3,6 +3,7 @@
# pylint: disable=line-too-long, too-many-branches, too-many-locals, too-many-statements
from datetime import datetime
from typing import Final
from cmk.agent_based.v2 import (
AgentSection,
@@ -17,6 +18,7 @@ from cmk.agent_based.v2 import (
DiscoveryResult,
)
NO_LEVELS: Final[str] = "no_levels"
def get_state_upper(levels, value):
warn, crit = levels
@@ -43,8 +45,8 @@ def discover_nextcloud_info(section) -> DiscoveryResult:
def check_nextcloud_info(params, section) -> CheckResult:
for key in section:
if key == "nextcloud":
_level_type, levels_free_space = params["levels_free_space"]
_level_type, levels_number_of_files = params["levels_number_of_files"]
level_type_free_space, levels_free_space = params["levels_free_space"]
level_type_number_of_files, levels_number_of_files = params["levels_number_of_files"]
# update infos are available only from Nextcloud version 28 onwards
try:
last_update = section[key]["last_update"]
@@ -89,19 +91,28 @@ def check_nextcloud_info(params, section) -> CheckResult:
# Create result for free space on disk
# Levels for free space are given in GBytes, we have to adjust this here
if level_type_free_space != NO_LEVELS:
warn, crit = levels_free_space
warn = warn * 1024 * 1024 * 1024
crit = crit * 1024 * 1024 * 1024
state = get_state_lower((warn, crit), free_space)
# create graph for free space on disk
yield Metric("nc_free_space", free_space, levels=(warn, crit))
else:
# a rule with no levels set is active, assume OK state
state = State.OK
yield Metric("nc_free_space", free_space)
notice = f"Remaining free space on disk: {render.bytes(free_space)}"
if state != State.OK:
yield Result(state=state, notice=notice)
# Create result for number of files
if level_type_number_of_files != NO_LEVELS:
warn, crit = levels_number_of_files
state = get_state_upper((warn, crit), num_files)
else:
# a rule with no levels set is active, assume OK state
state = State.OK
notice = f"Number of files: {num_files}"
if state != State.OK:
yield Result(state=state, notice=notice)
@@ -139,14 +150,22 @@ def check_nextcloud_info(params, section) -> CheckResult:
else:
app_versions = ""
# create graphs for number of apps
_level_type, levels = params["levels_apps_with_updates_available"]
level_type, levels = params["levels_apps_with_updates_available"]
yield Metric("nc_num_apps_installed", num_apps_installed)
if level_type != NO_LEVELS:
yield Metric(
"nc_apps_with_updates_available",
num_apps_with_updates_available,
levels=levels,
)
state = get_state_upper(levels, num_apps_with_updates_available)
else:
# a rule with no levels set is active, assume OK state
yield Metric(
"nc_apps_with_updates_available",
num_apps_with_updates_available,
)
state = State.OK
if app_versions == "":
notice = f"Number of installed apps: {num_apps_installed}\nNumber of apps with updates available: {num_apps_with_updates_available}"
else:

View File

@@ -3,7 +3,7 @@
# pylint: disable=line-too-long, too-many-locals
from time import time
from pprint import pprint
from typing import Final
from cmk.agent_based.v2 import (
AgentSection,
@@ -18,6 +18,7 @@ from cmk.agent_based.v2 import (
DiscoveryResult,
)
NO_LEVELS: Final[str] = "no_levels"
def get_state_upper(levels, value):
warn, crit = levels
@@ -43,6 +44,10 @@ def discover_nextcloud_users(section) -> DiscoveryResult:
def check_nextcloud_users(item, params, section) -> CheckResult:
attr = section.get(item)
if not attr:
yield Result(state=State.UNKNOWN, summary="User not found anymore, maybe it has been deleted.")
return
userid = item
quota_used_percent = section[item][0]
quota_used_bytes = section[item][1]
@@ -56,8 +61,8 @@ def check_nextcloud_users(item, params, section) -> CheckResult:
quota_is_set = False
else:
quota_is_set = True
_level_type, levels_quota_used = params["levels_users_quota_used"]
_level_type, levels_free_space = params["levels_users_free_space"]
level_type_quota_used, levels_quota_used = params["levels_users_quota_used"]
level_type_free_space, levels_free_space = params["levels_users_free_space"]
if (last_login_human == "never") or (not quota_is_set):
details = f"User ID is '{userid}', Last login: {last_login_human}"
summary = (
@@ -65,19 +70,29 @@ def check_nextcloud_users(item, params, section) -> CheckResult:
)
else:
# Levels are given in MBytes, we have to adjust this here
if level_type_free_space != NO_LEVELS:
warn, crit = levels_free_space
warn = warn * 1024 * 1024
crit = crit * 1024 * 1024
state = get_state_lower((warn, crit), free_space)
yield Metric("nc_users_free_space", free_space, levels=(warn, crit))
else:
# a rule with no levels set is active, so assume OK state
state = State.OK
yield Metric("nc_users_free_space", free_space)
details = f"User ID is '{userid}'\nLast login: {last_login_human} ({last_login_since} ago)\nFree space: {render.bytes(free_space)}"
summary = f"Used quota of '{display_name}' is {render.percent(quota_used_percent)}, {render.bytes(quota_used_bytes)}/{render.bytes(quota_total_bytes)} used"
notice = f"Remaining free space: {render.bytes(free_space)}"
yield Metric("nc_users_free_space", free_space, levels=(warn, crit))
if state != State.OK:
yield Result(state=state, notice=notice)
yield Metric("nc_users_free_space", free_space, levels=(warn, crit))
if level_type_quota_used != NO_LEVELS:
yield Metric("nc_users_quota_used", quota_used_percent, levels=levels_quota_used)
state = get_state_upper(levels_quota_used, quota_used_percent)
else:
# a rule with no levels set is active, so assume OK state
yield Metric("nc_users_quota_used", quota_used_percent)
state = State.OK
yield Result(state=state, summary=summary, details=details)

View File

@@ -4,7 +4,7 @@ catalog: unsorted
license: GPL
distribution: check_mk
description:
Works with Nextcloud version 25/26/27/28/29/30 (use at your own risk with lower versions).
Works with Nextcloud version 25/26/27/28/29/30/31/32 (use at your own risk with lower versions).
Tested only with mariab as underlying database.
You have to use a username/app password combination to get access to the Nextcloud API.
You can create this app password within the personal settings of an administrative user in Nextcloud.

View File

@@ -4,9 +4,9 @@ catalog: unsorted
license: GPL
distribution: check_mk
description:
Works with Nextcloud version 25/26/27/28/29/30 (use at your own risk with lower versions).
Works with Nextcloud version 25/26/27/28/29/30/31/32 (use at your own risk with lower versions).
Tested only with mariab as underlying database.
You have to use a username/app password combination to get access to the Nextcloud API.
You have to use a username/app password combination to get access to the Nextcloud API (including user info).
You can create this app password within the personal settings of an administrative user in Nextcloud.
Got to security settings and create a new app password.
The user must not be secured with 2FA.

View File

@@ -4,7 +4,7 @@ catalog: unsorted
license: GPL
distribution: check_mk
description:
Works with Nextcloud version 25/26/27/28/29/30 (use at your own risk with lower versions).
Works with Nextcloud version 25/26/27/28/29/30/31/32 (use at your own risk with lower versions).
Tested only with mariab as underlying database.
You have to use a username/app password combination to get access to the Nextcloud API.
You can create this app password within the personal settings of an administrative user in Nextcloud.