initial upload of version 3.1.1 which uses the new plugin API
This commit is contained in:
105
nextcloud/agent_based/nextcloud_database.py
Normal file
105
nextcloud/agent_based/nextcloud_database.py
Normal file
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env python3
|
||||
# pylint: disable=missing-module-docstring, unused-argument, missing-function-docstring
|
||||
# pylint: disable=line-too-long
|
||||
|
||||
from collections.abc import Mapping
|
||||
from typing import NotRequired, TypedDict
|
||||
|
||||
from cmk.agent_based.v2 import (
|
||||
AgentSection,
|
||||
CheckPlugin,
|
||||
CheckResult,
|
||||
Metric,
|
||||
render,
|
||||
Result,
|
||||
Service,
|
||||
State,
|
||||
StringTable,
|
||||
DiscoveryResult,
|
||||
)
|
||||
|
||||
|
||||
class _ItemData(TypedDict):
|
||||
dbtype: NotRequired[str]
|
||||
version: NotRequired[str]
|
||||
size: NotRequired[int]
|
||||
opcache_hit_rate: NotRequired[float]
|
||||
|
||||
|
||||
Section = Mapping[str, _ItemData]
|
||||
|
||||
|
||||
def get_state_lower(levels, value):
|
||||
warn, crit = levels
|
||||
if value < crit:
|
||||
return State.CRIT
|
||||
if value < warn:
|
||||
return State.WARN
|
||||
return State.OK
|
||||
|
||||
|
||||
def discover_nextcloud_database(section) -> DiscoveryResult:
|
||||
yield Service()
|
||||
|
||||
|
||||
def check_nextcloud_database(params, section) -> CheckResult:
|
||||
for key in section:
|
||||
if key == "database":
|
||||
opcache_hit_rate = section[key]["opcache_hit_rate"]
|
||||
size = section[key]["size"]
|
||||
dbtype = section[key]["dbtype"]
|
||||
version = section[key]["version"]
|
||||
_level_type, levels = params["levels_database_opcache_hit_rate"]
|
||||
# create graph for opcache hit rate
|
||||
yield Metric(
|
||||
"nc_database_opcache_hit_rate", opcache_hit_rate, levels=levels
|
||||
)
|
||||
# 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
|
||||
summary = "PHP OPCache hit rate: 0% - please check if opcache_get_status is enabled"
|
||||
details = f"\nDatabase type: {dbtype}\nDatabase version: {version}\nDatabase size: {render.bytes(size)}"
|
||||
yield Result(state=state, summary=summary, details=details)
|
||||
|
||||
|
||||
def parse_nextcloud_database(string_table: StringTable) -> Section:
|
||||
parsed_data = {"database": {}}
|
||||
params_list = [
|
||||
"NC_Database_Type",
|
||||
"NC_Database_Version",
|
||||
"NC_Database_Size",
|
||||
"NC_OPCache_Hit_Rate",
|
||||
]
|
||||
for line in string_table:
|
||||
if line[0] in params_list:
|
||||
param = line[0]
|
||||
value = line[1]
|
||||
if param == "NC_Database_Type":
|
||||
parsed_data["database"]["dbtype"] = value
|
||||
elif param == "NC_Database_Version":
|
||||
parsed_data["database"]["version"] = value
|
||||
elif param == "NC_Database_Size":
|
||||
parsed_data["database"]["size"] = int(value)
|
||||
elif param == "NC_OPCache_Hit_Rate":
|
||||
parsed_data["database"]["opcache_hit_rate"] = float(value)
|
||||
return parsed_data
|
||||
|
||||
|
||||
agent_section_nextcloud_database = AgentSection(
|
||||
name="nextcloud_database",
|
||||
parse_function=parse_nextcloud_database,
|
||||
)
|
||||
|
||||
check_plugin_nextcloud_database = CheckPlugin(
|
||||
name="nextcloud_database",
|
||||
service_name="Nextcloud Database",
|
||||
discovery_function=discover_nextcloud_database,
|
||||
check_function=check_nextcloud_database,
|
||||
check_default_parameters={
|
||||
"levels_database_opcache_hit_rate": ("fixed", (95.0, 85.0)),
|
||||
},
|
||||
check_ruleset_name="nextcloud_database",
|
||||
)
|
||||
252
nextcloud/agent_based/nextcloud_info.py
Normal file
252
nextcloud/agent_based/nextcloud_info.py
Normal file
@@ -0,0 +1,252 @@
|
||||
#!/usr/bin/env python3
|
||||
# pylint: disable=missing-module-docstring, unused-argument, missing-function-docstring
|
||||
# pylint: disable=line-too-long, too-many-branches, too-many-locals, too-many-statements
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from cmk.agent_based.v2 import (
|
||||
AgentSection,
|
||||
CheckPlugin,
|
||||
CheckResult,
|
||||
Metric,
|
||||
render,
|
||||
Result,
|
||||
Service,
|
||||
State,
|
||||
StringTable,
|
||||
DiscoveryResult,
|
||||
)
|
||||
|
||||
|
||||
def get_state_upper(levels, value):
|
||||
warn, crit = levels
|
||||
if value >= crit:
|
||||
return State.CRIT
|
||||
if value >= warn:
|
||||
return State.WARN
|
||||
return State.OK
|
||||
|
||||
|
||||
def get_state_lower(levels, value):
|
||||
warn, crit = levels
|
||||
if value < crit:
|
||||
return State.CRIT
|
||||
if value < warn:
|
||||
return State.WARN
|
||||
return State.OK
|
||||
|
||||
|
||||
def discover_nextcloud_info(section) -> DiscoveryResult:
|
||||
yield Service()
|
||||
|
||||
|
||||
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"]
|
||||
# update infos are available only from Nextcloud version 28 onwards
|
||||
try:
|
||||
last_update = section[key]["last_update"]
|
||||
update_available = section[key]["update_available"]
|
||||
last_update_human = datetime.fromtimestamp(last_update)
|
||||
except KeyError:
|
||||
last_update = (
|
||||
"Update information not available, update to at least version 28"
|
||||
)
|
||||
update_available = "False"
|
||||
last_update_human = "Information not available"
|
||||
status = section[key]["status"]
|
||||
free_space = section[key]["freespace"]
|
||||
version = section[key]["version"]
|
||||
php_version = section[key]["php_version"]
|
||||
webserver = section[key]["webserver"]
|
||||
num_files = section[key]["number_files"]
|
||||
num_shares = section[key]["number_shares"]
|
||||
|
||||
# create graph for number of files and shares
|
||||
yield Metric("nc_num_files", num_files)
|
||||
yield Metric("nc_num_shares", num_shares)
|
||||
|
||||
# create overall result
|
||||
summary = f"Status is {status}, Last update check: {last_update_human}"
|
||||
details = f"Nextcloud version: {version}\nPHP version: {php_version}\nWebserver: {webserver}\n\nNumber of files: {num_files}\nNumber of shares: {num_shares}\n\nFree space on disk: {render.bytes(free_space)}\n"
|
||||
if status == "ok":
|
||||
state = State.OK
|
||||
else:
|
||||
state = State.CRIT
|
||||
yield Result(state=state, summary=summary, details=details)
|
||||
|
||||
# Create result for available updates
|
||||
if update_available != "False":
|
||||
state = State.WARN
|
||||
notice = "Update is available"
|
||||
else:
|
||||
state = State.OK
|
||||
notice = "No update available"
|
||||
if state != State.OK:
|
||||
yield Result(state=state, notice=notice)
|
||||
|
||||
# Create result for free space on disk
|
||||
# Levels for free space are given in GBytes, we have to adjust this here
|
||||
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))
|
||||
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
|
||||
warn, crit = levels_number_of_files
|
||||
state = get_state_upper((warn, crit), num_files)
|
||||
notice = f"Number of files: {num_files}"
|
||||
if state != State.OK:
|
||||
yield Result(state=state, notice=notice)
|
||||
elif key == "users":
|
||||
num_users = section[key]["number"]
|
||||
num_active_last1hour = section[key]["active_last1hour"]
|
||||
num_active_last1day = section[key]["active_last1day"]
|
||||
num_active_last5min = section[key]["active_last5min"]
|
||||
# create graphs for number of users
|
||||
yield Metric("nc_num_users", num_users)
|
||||
yield Metric("nc_active_users_last_1hour", num_active_last1hour)
|
||||
yield Metric("nc_active_users_last_1day", num_active_last1day)
|
||||
yield Metric("nc_active_users_last_5min", num_active_last5min)
|
||||
notice = f"Number of users: {num_users}\n\nActive users last 5 min: {num_active_last5min}\nActive user since last hour: {num_active_last1hour}\nActive users since last day: {num_active_last1day}"
|
||||
yield Result(state=State.OK, notice=notice)
|
||||
elif key == "storages":
|
||||
num_storages = section[key]["number"]
|
||||
num_storages_home = section[key]["number_home"]
|
||||
num_storages_local = section[key]["number_local"]
|
||||
num_storages_other = section[key]["number_other"]
|
||||
# create graphs for number of storages
|
||||
yield Metric("nc_num_storages", num_storages)
|
||||
yield Metric("nc_num_storages_home", num_storages_home)
|
||||
yield Metric("nc_num_storages_local", num_storages_local)
|
||||
yield Metric("nc_num_storages_other", num_storages_other)
|
||||
notice = f"Number of storages: {num_storages}\nNumber of home/local/other storages: {num_storages_home}/{num_storages_local}/{num_storages_other}"
|
||||
yield Result(state=State.OK, notice=notice)
|
||||
elif key == "apps":
|
||||
# Workaround for Nextcloud 28, "apps" info is not always available
|
||||
try:
|
||||
num_apps_installed = section[key]["installed"]
|
||||
num_apps_with_updates_available = section[key]["with_updates_available"]
|
||||
if "app_versions" in section[key]:
|
||||
app_versions = section[key]["app_versions"]
|
||||
else:
|
||||
app_versions = ""
|
||||
# create graphs for number of apps
|
||||
_level_type, levels = params["levels_apps_with_updates_available"]
|
||||
yield Metric("nc_num_apps_installed", num_apps_installed)
|
||||
yield Metric(
|
||||
"nc_apps_with_updates_available",
|
||||
num_apps_with_updates_available,
|
||||
levels=levels,
|
||||
)
|
||||
state = get_state_upper(levels, num_apps_with_updates_available)
|
||||
if app_versions == "":
|
||||
notice = f"Number of installed apps: {num_apps_installed}\nNumber of apps with updates available: {num_apps_with_updates_available}"
|
||||
else:
|
||||
notice = f"Number of installed apps: {num_apps_installed}\nNumber of apps with updates available: {num_apps_with_updates_available}\nNew app versions available: {app_versions}"
|
||||
yield Result(state=state, notice=notice)
|
||||
except KeyError:
|
||||
# TBD
|
||||
pass
|
||||
|
||||
|
||||
def parse_nextcloud_info(string_table: StringTable) -> dict:
|
||||
parsed_data = {
|
||||
"nextcloud": {},
|
||||
"storages": {},
|
||||
"apps": {},
|
||||
"users": {},
|
||||
}
|
||||
params_list = [
|
||||
"NC_Version",
|
||||
"NC_Freespace",
|
||||
"NC_Status",
|
||||
"NC_Last_Update",
|
||||
"NC_Update_Available",
|
||||
"NC_Webserver",
|
||||
"NC_PHP_Version",
|
||||
"NC_Num_Users",
|
||||
"NC_Num_Files",
|
||||
"NC_Num_Shares",
|
||||
"NC_Num_Storages",
|
||||
"NC_Num_Storages_Home",
|
||||
"NC_Num_Storages_Local",
|
||||
"NC_Num_Storages_Other",
|
||||
"NC_Num_Apps_Installed",
|
||||
"NC_Num_Apps_Updates_Available",
|
||||
"NC_Apps_With_Updates_Available",
|
||||
"NC_Active_Users_Last_5Min",
|
||||
"NC_Active_Users_Last_1Hour",
|
||||
"NC_Active_Users_Last_1Day",
|
||||
]
|
||||
for line in string_table:
|
||||
if line[0] in params_list:
|
||||
param = line[0]
|
||||
value = line[1]
|
||||
if param == "NC_Version":
|
||||
parsed_data["nextcloud"]["version"] = value
|
||||
elif param == "NC_Freespace":
|
||||
parsed_data["nextcloud"]["freespace"] = float(value)
|
||||
elif param == "NC_Status":
|
||||
parsed_data["nextcloud"]["status"] = value
|
||||
elif param == "NC_Last_Update":
|
||||
parsed_data["nextcloud"]["last_update"] = int(value)
|
||||
elif param == "NC_Update_Available":
|
||||
parsed_data["nextcloud"]["update_available"] = value
|
||||
elif param == "NC_Webserver":
|
||||
parsed_data["nextcloud"]["webserver"] = value
|
||||
elif param == "NC_PHP_Version":
|
||||
parsed_data["nextcloud"]["php_version"] = value
|
||||
elif param == "NC_Num_Files":
|
||||
parsed_data["nextcloud"]["number_files"] = int(value)
|
||||
elif param == "NC_Num_Shares":
|
||||
parsed_data["nextcloud"]["number_shares"] = int(value)
|
||||
elif param == "NC_Num_Storages":
|
||||
parsed_data["storages"]["number"] = int(value)
|
||||
elif param == "NC_Num_Storages_Home":
|
||||
parsed_data["storages"]["number_home"] = int(value)
|
||||
elif param == "NC_Num_Storages_Local":
|
||||
parsed_data["storages"]["number_local"] = int(value)
|
||||
elif param == "NC_Num_Storages_Other":
|
||||
parsed_data["storages"]["number_other"] = int(value)
|
||||
elif param == "NC_Num_Apps_Installed":
|
||||
parsed_data["apps"]["installed"] = int(value)
|
||||
elif param == "NC_Num_Apps_Updates_Available":
|
||||
parsed_data["apps"]["with_updates_available"] = int(value)
|
||||
elif param == "NC_Apps_With_Updates_Available":
|
||||
parsed_data["apps"]["app_versions"] = value
|
||||
elif param == "NC_Num_Users":
|
||||
parsed_data["users"]["number"] = int(value)
|
||||
elif param == "NC_Active_Users_Last_5Min":
|
||||
parsed_data["users"]["active_last5min"] = int(value)
|
||||
elif param == "NC_Active_Users_Last_1Hour":
|
||||
parsed_data["users"]["active_last1hour"] = int(value)
|
||||
elif param == "NC_Active_Users_Last_1Day":
|
||||
parsed_data["users"]["active_last1day"] = int(value)
|
||||
return parsed_data
|
||||
|
||||
|
||||
agent_section_nextcloud_info = AgentSection(
|
||||
name="nextcloud_info",
|
||||
parse_function=parse_nextcloud_info,
|
||||
)
|
||||
|
||||
check_plugin_nextcloud_info = CheckPlugin(
|
||||
name="nextcloud_info",
|
||||
service_name="Nextcloud Info",
|
||||
discovery_function=discover_nextcloud_info,
|
||||
check_function=check_nextcloud_info,
|
||||
check_default_parameters={
|
||||
"levels_apps_with_updates_available": ("fixed", (1, 2)),
|
||||
"levels_free_space": ("fixed", (8.0, 4.0)),
|
||||
"levels_number_of_files": ("fixed", (100_000, 250_000)),
|
||||
},
|
||||
check_ruleset_name="nextcloud_info",
|
||||
)
|
||||
131
nextcloud/agent_based/nextcloud_users.py
Normal file
131
nextcloud/agent_based/nextcloud_users.py
Normal file
@@ -0,0 +1,131 @@
|
||||
#!/usr/bin/env python3
|
||||
# pylint: disable=missing-module-docstring, unused-argument, missing-function-docstring
|
||||
# pylint: disable=line-too-long, too-many-locals
|
||||
|
||||
from time import time
|
||||
|
||||
from cmk.agent_based.v2 import (
|
||||
AgentSection,
|
||||
CheckPlugin,
|
||||
CheckResult,
|
||||
Metric,
|
||||
render,
|
||||
Result,
|
||||
Service,
|
||||
State,
|
||||
StringTable,
|
||||
DiscoveryResult,
|
||||
)
|
||||
|
||||
|
||||
def get_state_upper(levels, value):
|
||||
warn, crit = levels
|
||||
if value >= crit:
|
||||
return State.CRIT
|
||||
if value >= warn:
|
||||
return State.WARN
|
||||
return State.OK
|
||||
|
||||
|
||||
def get_state_lower(levels, value):
|
||||
warn, crit = levels
|
||||
if value < crit:
|
||||
return State.CRIT
|
||||
if value < warn:
|
||||
return State.WARN
|
||||
return State.OK
|
||||
|
||||
|
||||
def discover_nextcloud_users(section) -> DiscoveryResult:
|
||||
for key in section:
|
||||
yield Service(item=key)
|
||||
|
||||
|
||||
def check_nextcloud_users(item, params, section) -> CheckResult:
|
||||
userid = item
|
||||
quota_used_percent = section[item][0]
|
||||
quota_used_bytes = section[item][1]
|
||||
quota_total_bytes = section[item][2]
|
||||
display_name = section[item][3]
|
||||
last_login_human = section[item][4]
|
||||
last_login_since = section[item][5]
|
||||
free_space = quota_total_bytes - quota_used_bytes
|
||||
# print(free_space)
|
||||
_level_type, levels_quota_used = params["levels_users_quota_used"]
|
||||
_level_type, levels_free_space = params["levels_users_free_space"]
|
||||
if last_login_human == "never":
|
||||
quota_used_percent = 0
|
||||
details = f"User ID is '{userid}', Last login: {last_login_human}"
|
||||
summary = (
|
||||
f"Used quota of '{display_name}' can't be calculated yet (never logged in)"
|
||||
)
|
||||
else:
|
||||
# Levels are given in MBytes, we have to adjust this here
|
||||
warn, crit = levels_free_space
|
||||
warn = warn * 1024 * 1024
|
||||
crit = crit * 1024 * 1024
|
||||
state = get_state_lower((warn, crit), 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_quota_used", quota_used_percent, levels=levels_quota_used)
|
||||
state = get_state_upper(levels_quota_used, quota_used_percent)
|
||||
yield Result(state=state, summary=summary, details=details)
|
||||
|
||||
|
||||
def parse_nextcloud_users(string_table: StringTable) -> dict:
|
||||
# Raw output from check:
|
||||
# userid;displayname;lastLogin;quota_free;quota_quota;quota_relative;quota_total;quota_used
|
||||
# str;str;int(milli seconds since epoch);int(bytes);int(bytes);float(percent);int(bytes);int(bytes)
|
||||
parsed_data = {}
|
||||
for line in string_table:
|
||||
userid = line[0]
|
||||
display_name = line[1]
|
||||
last_login = int(line[2]) / 1000
|
||||
if last_login == 0:
|
||||
# user never logged in
|
||||
last_login_human = "never"
|
||||
last_login_since = "never"
|
||||
else:
|
||||
# user logged in at least once
|
||||
curr_time = int(time())
|
||||
login_diff = curr_time - last_login
|
||||
last_login_human = render.datetime(last_login)
|
||||
last_login_since = render.timespan(login_diff)
|
||||
quota_quota = int(line[4])
|
||||
if quota_quota == -3:
|
||||
# TBD, no quota set for user
|
||||
pass
|
||||
quota_relative = float(line[5])
|
||||
quota_total = float(line[6])
|
||||
quota_used = float(line[7])
|
||||
parsed_data[f"{userid}"] = [
|
||||
quota_relative,
|
||||
quota_used,
|
||||
quota_total,
|
||||
display_name,
|
||||
last_login_human,
|
||||
last_login_since,
|
||||
]
|
||||
return parsed_data
|
||||
|
||||
|
||||
agent_section_nextcloud_users = AgentSection(
|
||||
name="nextcloud_users",
|
||||
parse_function=parse_nextcloud_users,
|
||||
)
|
||||
|
||||
check_plugin_nextcloud_users = CheckPlugin(
|
||||
name="nextcloud_users",
|
||||
service_name="Nextcloud User %s",
|
||||
discovery_function=discover_nextcloud_users,
|
||||
check_function=check_nextcloud_users,
|
||||
check_default_parameters={
|
||||
"levels_users_quota_used": ("fixed", (65.0, 85.0)),
|
||||
"levels_users_free_space": ("fixed", (256.0, 128.0)),
|
||||
},
|
||||
check_ruleset_name="nextcloud_users",
|
||||
)
|
||||
16
nextcloud/checkman/nextcloud_database
Normal file
16
nextcloud/checkman/nextcloud_database
Normal file
@@ -0,0 +1,16 @@
|
||||
title: Nextcloud: Database information
|
||||
agents: linux
|
||||
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).
|
||||
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.
|
||||
Got to security settings and create a new app password.
|
||||
The user must not be secured with 2FA.
|
||||
Shows several information about a Nextcloud database (type/version/size).
|
||||
The check will raise WARN/CRIT if Database PHP OPcache hit rate is below the configurable levels.
|
||||
inventory:
|
||||
one service is created (with several details)
|
||||
20
nextcloud/checkman/nextcloud_info
Normal file
20
nextcloud/checkman/nextcloud_info
Normal file
@@ -0,0 +1,20 @@
|
||||
title: Nextcloud: Various information
|
||||
agents: linux
|
||||
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).
|
||||
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.
|
||||
Got to security settings and create a new app password.
|
||||
The user must not be secured with 2FA.
|
||||
Shows several information about a Nextcloud instance, e.g. number of files/storages/(active)users, free space on disk.
|
||||
The check will raise CRIT if the Nextcloud instance is not in "ok" state.
|
||||
The check will raise WARN if there is an update available for Nextcloud.
|
||||
The check will raise WARN/CRIT if free space on disk is below the configurable levels.
|
||||
The check will raise WARN/CRIT if the number of installed apps with available updates is above the configurable levels.
|
||||
The check will raise WARN/CRIT if the number of files is above the configurable levels.
|
||||
inventory:
|
||||
one service is created (with several details)
|
||||
20
nextcloud/checkman/nextcloud_users
Normal file
20
nextcloud/checkman/nextcloud_users
Normal file
@@ -0,0 +1,20 @@
|
||||
title: Nextcloud: Quota and Storage Usage of Users
|
||||
agents: linux
|
||||
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).
|
||||
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.
|
||||
Got to security settings and create a new app password.
|
||||
The user must not be secured with 2FA.
|
||||
Shows the usage of storage quota used by each user in percent of the maximum allowed quota.
|
||||
Shows the remaining free space for each user based on his/her max quota and used space.
|
||||
The check will raise WARN/CRIT if quota usage is above the configurable levels.
|
||||
The check will raise WARN/CRIT if free space is below the configurable levels.
|
||||
item:
|
||||
userid
|
||||
inventory:
|
||||
one service is created for each user
|
||||
198
nextcloud/graphing/graph_nextcloud.py
Normal file
198
nextcloud/graphing/graph_nextcloud.py
Normal file
@@ -0,0 +1,198 @@
|
||||
#!/usr/bin/env python3
|
||||
"""graphing and metrics definitions"""
|
||||
|
||||
from cmk.graphing.v1 import Title
|
||||
from cmk.graphing.v1.graphs import Graph, MinimalRange
|
||||
from cmk.graphing.v1.metrics import Color, DecimalNotation, SINotation, Metric, Unit
|
||||
from cmk.graphing.v1.perfometers import Open, Closed, FocusRange, Perfometer, Stacked
|
||||
|
||||
|
||||
metric_nc_num_users = Metric(
|
||||
name="nc_num_users",
|
||||
title=Title("Number of Users"),
|
||||
unit=Unit(DecimalNotation("")),
|
||||
color=Color.PURPLE,
|
||||
)
|
||||
|
||||
metric_nc_num_shares = Metric(
|
||||
name="nc_num_shares",
|
||||
title=Title("Number of Shares"),
|
||||
unit=Unit(DecimalNotation("")),
|
||||
color=Color.BROWN,
|
||||
)
|
||||
|
||||
metric_nc_num_storages = Metric(
|
||||
name="nc_num_storages",
|
||||
title=Title("Number of Storages"),
|
||||
unit=Unit(DecimalNotation("")),
|
||||
color=Color.BLUE,
|
||||
)
|
||||
|
||||
metric_nc_num_storages_home = Metric(
|
||||
name="nc_num_storages_home",
|
||||
title=Title("Number of Home Storages"),
|
||||
unit=Unit(DecimalNotation("")),
|
||||
color=Color.GREEN,
|
||||
)
|
||||
|
||||
metric_nc_num_storages_local = Metric(
|
||||
name="nc_num_storages_local",
|
||||
title=Title("Number of Local Storages"),
|
||||
unit=Unit(DecimalNotation("")),
|
||||
color=Color.DARK_GREEN,
|
||||
)
|
||||
|
||||
|
||||
metric_nc_num_storages_other = Metric(
|
||||
name="nc_num_storages_other",
|
||||
title=Title("Number of Other Storages"),
|
||||
unit=Unit(DecimalNotation("")),
|
||||
color=Color.LIGHT_GREEN,
|
||||
)
|
||||
|
||||
metric_nc_num_files = Metric(
|
||||
name="nc_num_files",
|
||||
title=Title("Number of Files"),
|
||||
unit=Unit(SINotation("")),
|
||||
color=Color.RED,
|
||||
)
|
||||
|
||||
metric_nc_num_apps_installed = Metric(
|
||||
name="nc_num_apps_installed",
|
||||
title=Title("Number of installed Apps"),
|
||||
unit=Unit(DecimalNotation("")),
|
||||
color=Color.GREEN,
|
||||
)
|
||||
|
||||
metric_nc_apps_with_updates_available = Metric(
|
||||
name="nc_apps_with_updates_available",
|
||||
title=Title("Number of installed Apps with Updates Available"),
|
||||
unit=Unit(DecimalNotation("")),
|
||||
color=Color.RED,
|
||||
)
|
||||
|
||||
metric_nc_active_users_last_5min = Metric(
|
||||
name="nc_active_users_last_5min",
|
||||
title=Title("Number of Active Users in the last 5 minutes"),
|
||||
unit=Unit(DecimalNotation("")),
|
||||
color=Color.LIGHT_RED,
|
||||
)
|
||||
|
||||
metric_nc_active_users_last_1hour = Metric(
|
||||
name="nc_active_users_last_1hour",
|
||||
title=Title("Number of Active Users in the last 1 hour"),
|
||||
unit=Unit(DecimalNotation("")),
|
||||
color=Color.LIGHT_GREEN,
|
||||
)
|
||||
|
||||
metric_nc_active_users_last_1day = Metric(
|
||||
name="nc_active_users_last_1day",
|
||||
title=Title("Number of Active Users in the last 1 day"),
|
||||
unit=Unit(DecimalNotation("")),
|
||||
color=Color.LIGHT_BLUE,
|
||||
)
|
||||
|
||||
metric_nc_users_free_space = Metric(
|
||||
name="nc_users_free_space",
|
||||
title=Title("Free Space of User"),
|
||||
unit=Unit(SINotation("bytes")),
|
||||
color=Color.LIGHT_BLUE,
|
||||
)
|
||||
|
||||
metric_nc_free_space = Metric(
|
||||
name="nc_free_space",
|
||||
title=Title("Free Space on Disk"),
|
||||
unit=Unit(SINotation("bytes")),
|
||||
color=Color.DARK_BLUE,
|
||||
)
|
||||
|
||||
metric_nc_database_size = Metric(
|
||||
name="nc_database_size",
|
||||
title=Title("Database Size"),
|
||||
unit=Unit(SINotation("bytes")),
|
||||
color=Color.GREEN,
|
||||
)
|
||||
|
||||
metric_nc_database_opcache_hit_rate = Metric(
|
||||
name="nc_database_opcache_hit_rate",
|
||||
title=Title("Database PHP OPCache Hit Rate"),
|
||||
unit=Unit(DecimalNotation("%")),
|
||||
color=Color.LIGHT_BLUE,
|
||||
)
|
||||
|
||||
|
||||
metric_nc_users_quota_used = Metric(
|
||||
name="nc_users_quota_used",
|
||||
title=Title("Quota used"),
|
||||
unit=Unit(DecimalNotation("%")),
|
||||
color=Color.BLUE,
|
||||
)
|
||||
|
||||
|
||||
graph_nc_number_of_users_shares_storages = Graph(
|
||||
name="number_of_users_shares_storages_combined",
|
||||
title=Title("Number of Users, Shares and Storages"),
|
||||
# names here refer to the metric names above
|
||||
simple_lines=["nc_num_users", "nc_num_shares", "nc_num_storages"],
|
||||
minimal_range=MinimalRange(0, 200),
|
||||
)
|
||||
|
||||
graph_nc_number_of_storage_types = Graph(
|
||||
name="number_of_storage_types_combined",
|
||||
title=Title("Number of Storage Types"),
|
||||
# names here refer to the metric names above
|
||||
simple_lines=[
|
||||
"nc_num_storages_home",
|
||||
"nc_num_storages_local",
|
||||
"nc_num_storages_other",
|
||||
],
|
||||
minimal_range=MinimalRange(0, 100),
|
||||
)
|
||||
|
||||
graph_nc_number_of_active_users = Graph(
|
||||
name="number_of_active_users_combined",
|
||||
title=Title("Number of Active Users"),
|
||||
# names here refer to the metric names above
|
||||
simple_lines=[
|
||||
"nc_active_users_last_5min",
|
||||
"nc_active_users_last_1hour",
|
||||
"nc_active_users_last_1day",
|
||||
],
|
||||
minimal_range=MinimalRange(0, 100),
|
||||
)
|
||||
|
||||
|
||||
perfometer_nc_database_op_cache_hit_rate = Perfometer(
|
||||
name="nc_database_opcache_hit_rate",
|
||||
focus_range=FocusRange(
|
||||
Closed(0),
|
||||
Closed(100),
|
||||
),
|
||||
segments=("nc_database_opcache_hit_rate",),
|
||||
)
|
||||
|
||||
perfometer_nc_database_size = Perfometer(
|
||||
name="nc_database_size",
|
||||
focus_range=FocusRange(Open(0.0), Open(500.0)),
|
||||
segments=["nc_database_size"],
|
||||
)
|
||||
|
||||
perfometer_nc_quota_used = Perfometer(
|
||||
name="nc_users_quota_used",
|
||||
focus_range=FocusRange(Closed(0), Closed(100)),
|
||||
segments=["nc_users_quota_used"],
|
||||
)
|
||||
|
||||
perfometer_nc_info = Stacked(
|
||||
name="nc_info",
|
||||
lower=Perfometer(
|
||||
name="nc_num_users",
|
||||
focus_range=FocusRange(Closed(0), Open(200)),
|
||||
segments=["nc_num_users"],
|
||||
),
|
||||
upper=Perfometer(
|
||||
name="nc_num_apps_installed",
|
||||
focus_range=FocusRange(Closed(0), Open(100)),
|
||||
segments=["nc_num_apps_installed"],
|
||||
),
|
||||
)
|
||||
312
nextcloud/libexec/agent_nextcloud
Executable file
312
nextcloud/libexec/agent_nextcloud
Executable file
@@ -0,0 +1,312 @@
|
||||
#!/usr/bin/env python3
|
||||
# pylint: disable=missing-module-docstring, missing-class-docstring, missing-function-docstring
|
||||
# pylint: disable=too-many-branches, line-too-long, too-many-statements
|
||||
# pylint: disable=too-many-arguments, too-many-positional-arguments, too-many-locals
|
||||
|
||||
import json
|
||||
import argparse
|
||||
import sys
|
||||
import requests
|
||||
import urllib3
|
||||
from requests.structures import CaseInsensitiveDict
|
||||
|
||||
NC_API_ENDPOINT = "ocs/v2.php/apps/serverinfo/api/v1/info?format=json"
|
||||
NC_API_ENDPOINT_ALL_USERS = "ocs/v1.php/cloud/users?format=json"
|
||||
NC_API_ENDPOINT_USER = "ocs/v1.php/cloud/users"
|
||||
|
||||
|
||||
def get_args() -> argparse.Namespace:
|
||||
parser: argparse.ArgumentParser = argparse.ArgumentParser(
|
||||
description="Nextcloud server parameters"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--hostname", required=True, type=str, help="Hostname or IP of nextcloud server"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--username", required=True, type=str, help="User with admin privileges"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--password", required=True, type=str, help="App password for the user"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--port",
|
||||
required=False,
|
||||
type=int,
|
||||
help="Port where the server is listening on (if not HTTP/HTTPS)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--folder",
|
||||
required=False,
|
||||
type=str,
|
||||
help="Folder if not installed in web root directory",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-https",
|
||||
required=False,
|
||||
default=False,
|
||||
help="Disable HTTPS",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-cert-check",
|
||||
required=False,
|
||||
default=False,
|
||||
help="Disable certificate checks",
|
||||
action="store_true",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
||||
def create_url(endpoint, hostname, protocol, port, folder):
|
||||
# these parameters are needed, otherwise no information about updates regarding apps and Nextcloud itself are reported (since version 28)
|
||||
params = "skipApps=false&skipUpdate=false"
|
||||
if folder == "":
|
||||
url = f"{protocol}://{hostname}:{port}/{endpoint}"
|
||||
else:
|
||||
url = f"{protocol}://{hostname}:{port}/{folder}/{endpoint}"
|
||||
if endpoint == NC_API_ENDPOINT:
|
||||
url = f"{url}&{params}"
|
||||
return url
|
||||
|
||||
|
||||
def create_url_user(user, endpoint, hostname, protocol, port, folder):
|
||||
params = "format=json"
|
||||
if folder == "":
|
||||
url = f"{protocol}://{hostname}:{port}/{endpoint}/{user}?{params}"
|
||||
else:
|
||||
url = f"{protocol}://{hostname}:{port}/{folder}/{endpoint}/{user}?{params}"
|
||||
return url
|
||||
|
||||
|
||||
def get_session(username, secret):
|
||||
session = requests.session()
|
||||
session.cookies.set("SameSite", "Strict")
|
||||
session.auth = (username, secret)
|
||||
session.headers["Accept"] = "application/json"
|
||||
return session
|
||||
|
||||
|
||||
def get_data(session, url, verify):
|
||||
data = {}
|
||||
headers = CaseInsensitiveDict()
|
||||
headers["Accept"] = "application/json"
|
||||
response = session.get(url, headers=headers, verify=verify)
|
||||
status = response.status_code
|
||||
if status == 200:
|
||||
jsdata = response.text
|
||||
data = json.loads(jsdata) # returns a dictionary
|
||||
return data
|
||||
if status == 503:
|
||||
# this code is reported when maintenance mode is on
|
||||
sys.stderr.write(
|
||||
f"Request response code is {response.status_code} with URL {url}, maybe maintenance mode is on?\n"
|
||||
)
|
||||
sys.exit(1)
|
||||
else:
|
||||
sys.stderr.write(
|
||||
f"Request response code is {response.status_code} with URL {url}\n"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def get_data_all_users(session, url, verify):
|
||||
headers = CaseInsensitiveDict()
|
||||
headers["Accept"] = "application/json"
|
||||
headers["OCS-APIRequest"] = "true"
|
||||
response = session.get(url, headers=headers, verify=verify)
|
||||
status = response.status_code
|
||||
if status == 200:
|
||||
jsdata = response.text
|
||||
data = json.loads(jsdata) # returns a dictionary
|
||||
return data
|
||||
sys.stderr.write(
|
||||
f"Request response code is {response.status_code} with URL {url}\n"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def get_data_user(session, url, verify):
|
||||
headers = CaseInsensitiveDict()
|
||||
headers["Accept"] = "application/json"
|
||||
headers["OCS-APIRequest"] = "true"
|
||||
response = session.get(url, headers=headers, verify=verify)
|
||||
status = response.status_code
|
||||
if status == 200:
|
||||
jsdata = response.text
|
||||
data = json.loads(jsdata) # returns a dictionary
|
||||
return data
|
||||
|
||||
sys.stderr.write(
|
||||
f"Request response code is {response.status_code} with URL {url}\n"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def do_cmk_output(data):
|
||||
apps_with_updates_available = {}
|
||||
str_apps_with_updates_available = ""
|
||||
print("<<<nextcloud_info:sep(59)>>>")
|
||||
print(f"NC_Version;{data['ocs']['data']['nextcloud']['system']['version']}")
|
||||
print(f"NC_Freespace;{data['ocs']['data']['nextcloud']['system']['freespace']}")
|
||||
print(f"NC_Status;{data['ocs']['meta']['status']}")
|
||||
# This update info is available only from version 28 onwards, so the key "update" does not exist in all versions before
|
||||
try:
|
||||
print(
|
||||
f"NC_Last_Update;{data['ocs']['data']['nextcloud']['system']['update']['lastupdatedat']}"
|
||||
)
|
||||
print(
|
||||
f"NC_Update_Available;{data['ocs']['data']['nextcloud']['system']['update']['available']}"
|
||||
)
|
||||
except KeyError:
|
||||
# TBD
|
||||
pass
|
||||
print(f"NC_Num_Users;{data['ocs']['data']['nextcloud']['storage']['num_users']}")
|
||||
print(f"NC_Num_Files;{data['ocs']['data']['nextcloud']['storage']['num_files']}")
|
||||
print(f"NC_Num_Shares;{data['ocs']['data']['nextcloud']['shares']['num_shares']}")
|
||||
print(
|
||||
f"NC_Num_Storages;{data['ocs']['data']['nextcloud']['storage']['num_storages']}"
|
||||
)
|
||||
print(
|
||||
f"NC_Num_Storages_Home;{data['ocs']['data']['nextcloud']['storage']['num_storages_home']}"
|
||||
)
|
||||
print(
|
||||
f"NC_Num_Storages_Local;{data['ocs']['data']['nextcloud']['storage']['num_storages_local']}"
|
||||
)
|
||||
print(
|
||||
f"NC_Num_Storages_Other;{data['ocs']['data']['nextcloud']['storage']['num_storages_other']}"
|
||||
)
|
||||
# Workaround for Nextcloud 28.0.1 (KeyError "apps")
|
||||
try:
|
||||
print(
|
||||
f"NC_Num_Apps_Installed;{data['ocs']['data']['nextcloud']['system']['apps']['num_installed']}"
|
||||
)
|
||||
print(
|
||||
f"NC_Num_Apps_Updates_Available;{data['ocs']['data']['nextcloud']['system']['apps']['num_updates_available']}"
|
||||
)
|
||||
apps_with_updates_available = data["ocs"]["data"]["nextcloud"]["system"][
|
||||
"apps"
|
||||
]["app_updates"]
|
||||
if apps_with_updates_available:
|
||||
for app, version in apps_with_updates_available.items():
|
||||
str_apps_with_updates_available = (
|
||||
str_apps_with_updates_available + app + "/" + version + " "
|
||||
)
|
||||
print(
|
||||
f"NC_Apps_With_Updates_Available;{str_apps_with_updates_available}"
|
||||
)
|
||||
except KeyError:
|
||||
# TBD
|
||||
pass
|
||||
print(
|
||||
f"NC_Active_Users_Last_5Min;{data['ocs']['data']['activeUsers']['last5minutes']}"
|
||||
)
|
||||
print(
|
||||
f"NC_Active_Users_Last_1Hour;{data['ocs']['data']['activeUsers']['last1hour']}"
|
||||
)
|
||||
print(
|
||||
f"NC_Active_Users_Last_1Day;{data['ocs']['data']['activeUsers']['last24hours']}"
|
||||
)
|
||||
print(f"NC_Webserver;{data['ocs']['data']['server']['webserver']}")
|
||||
print(f"NC_PHP_Version;{data['ocs']['data']['server']['php']['version']}")
|
||||
|
||||
print("<<<nextcloud_database:sep(59)>>>")
|
||||
print(f"NC_Database_Type;{data['ocs']['data']['server']['database']['type']}")
|
||||
print(f"NC_Database_Version;{data['ocs']['data']['server']['database']['version']}")
|
||||
print(f"NC_Database_Size;{data['ocs']['data']['server']['database']['size']}")
|
||||
# opcache entry does not exist if opcache_get_status is disabled by server settings
|
||||
# thanks to Marcus Klein from Iteratio to report (and solve!) this bug
|
||||
if data["ocs"]["data"]["server"]["php"]["opcache"]:
|
||||
print(
|
||||
f"NC_OPCache_Hit_Rate;{data['ocs']['data']['server']['php']['opcache']['opcache_statistics']['opcache_hit_rate']}"
|
||||
)
|
||||
else:
|
||||
print("NC_OPCache_Hit_Rate;0")
|
||||
|
||||
|
||||
def do_cmk_output_all_users(session, data, verify, hostname, protocol, port, folder):
|
||||
print("<<<nextcloud_users:sep(59)>>>")
|
||||
for user in data["ocs"]["data"]["users"]:
|
||||
nc_url = create_url_user(
|
||||
user, NC_API_ENDPOINT_USER, hostname, protocol, port, folder
|
||||
)
|
||||
user_data = get_data_user(session, nc_url, verify)
|
||||
userid = user_data["ocs"]["data"]["id"]
|
||||
displayname = user_data["ocs"]["data"]["displayname"]
|
||||
lastlogin = int(user_data["ocs"]["data"]["lastLogin"])
|
||||
if lastlogin == 0:
|
||||
# user has never logged in
|
||||
quota_free = -1
|
||||
quota_quota = -1
|
||||
quota_relative = -1
|
||||
quota_total = -1
|
||||
quota_used = -1
|
||||
else:
|
||||
quota_free = user_data["ocs"]["data"]["quota"]["free"]
|
||||
# quota_quota == -3 --> unlimited
|
||||
quota_quota = user_data["ocs"]["data"]["quota"]["quota"]
|
||||
# quota_relative = used * 100 / (free + used)
|
||||
quota_relative = user_data["ocs"]["data"]["quota"]["relative"]
|
||||
quota_total = user_data["ocs"]["data"]["quota"]["total"]
|
||||
quota_used = user_data["ocs"]["data"]["quota"]["used"]
|
||||
print(
|
||||
f"{userid};{displayname};{lastlogin};{quota_free};{quota_quota};{quota_relative};{quota_total};{quota_used}"
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
# get and check parameters
|
||||
params: argparse.Namespace = get_args()
|
||||
if params.hostname is None:
|
||||
sys.stderr.write("No hostname given.\n")
|
||||
sys.exit(1)
|
||||
if params.username is None:
|
||||
sys.stderr.write("No username given.\n")
|
||||
sys.exit(1)
|
||||
if params.password is None:
|
||||
sys.stderr.write("No app password given.\n")
|
||||
sys.exit(1)
|
||||
hostname = params.hostname
|
||||
username = params.username
|
||||
pwd = params.password
|
||||
if params.folder is None:
|
||||
folder = ""
|
||||
else:
|
||||
folder = params.folder
|
||||
if params.no_cert_check:
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
verify = False
|
||||
else:
|
||||
verify = True
|
||||
if params.port is None:
|
||||
if params.no_https:
|
||||
protocol = "http"
|
||||
port = "80"
|
||||
else:
|
||||
protocol = "https"
|
||||
port = "443"
|
||||
else:
|
||||
if params.no_https:
|
||||
protocol = "http"
|
||||
else:
|
||||
protocol = "https"
|
||||
port = params.port
|
||||
if protocol == "http" and port == "443":
|
||||
sys.stderr.write("Combining HTTP with port 443 is not supported.\n")
|
||||
sys.exit(1)
|
||||
if protocol == "https" and port == "80":
|
||||
sys.stderr.write("Combining HTTPS with port 80 is not supported.\n")
|
||||
sys.exit(1)
|
||||
|
||||
# create session
|
||||
session = get_session(username, pwd)
|
||||
nc_url = create_url(NC_API_ENDPOINT, hostname, protocol, port, folder)
|
||||
nc_data = get_data(session, nc_url, verify)
|
||||
do_cmk_output(nc_data)
|
||||
nc_url = create_url(NC_API_ENDPOINT_ALL_USERS, hostname, protocol, port, folder)
|
||||
nc_data = get_data_all_users(session, nc_url, verify)
|
||||
do_cmk_output_all_users(session, nc_data, verify, hostname, protocol, port, folder)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
45
nextcloud/rulesets/nextcloud_database_rules.py
Normal file
45
nextcloud/rulesets/nextcloud_database_rules.py
Normal file
@@ -0,0 +1,45 @@
|
||||
#!/user/bin/env python3
|
||||
"""parameter form ruleset for Nextcloud databases"""
|
||||
|
||||
from cmk.rulesets.v1 import Title
|
||||
from cmk.rulesets.v1.form_specs import (
|
||||
DefaultValue,
|
||||
DictElement,
|
||||
Dictionary,
|
||||
Float,
|
||||
LevelDirection,
|
||||
SimpleLevels,
|
||||
)
|
||||
from cmk.rulesets.v1.rule_specs import HostCondition, CheckParameters, Topic
|
||||
|
||||
|
||||
# function name should begin with an underscore to limit it's visibility
|
||||
def _parameter_form():
|
||||
return Dictionary(
|
||||
elements={
|
||||
"levels_database_opcache_hit_rate": DictElement(
|
||||
parameter_form=SimpleLevels(
|
||||
title=Title("Levels for database PHP op cache hit rate"),
|
||||
form_spec_template=Float(),
|
||||
level_direction=LevelDirection.LOWER,
|
||||
prefill_fixed_levels=DefaultValue(value=(95.0, 85.0)),
|
||||
),
|
||||
required=True,
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
# name must begin with "rule_spec_", should refer to the used check plugin
|
||||
# must be an instance of "CheckParameters"
|
||||
rule_spec_nextcloud_database = CheckParameters(
|
||||
# "name" should be the same as the check plugin
|
||||
name="nextcloud_database",
|
||||
# the title is shown in the GUI
|
||||
title=Title("Levels for database PHP op cache hit rate"),
|
||||
# this ruleset can be found under Setup|Service monitoring rules|Applications...
|
||||
topic=Topic.APPLICATIONS,
|
||||
# define the name of the function which creates the GUI elements
|
||||
parameter_form=_parameter_form,
|
||||
condition=HostCondition(),
|
||||
)
|
||||
66
nextcloud/rulesets/nextcloud_info_rules.py
Normal file
66
nextcloud/rulesets/nextcloud_info_rules.py
Normal file
@@ -0,0 +1,66 @@
|
||||
#!/user/bin/env python3
|
||||
"""parameter form ruleset for Nextcloud systems"""
|
||||
|
||||
from cmk.rulesets.v1 import Title
|
||||
from cmk.rulesets.v1.form_specs import (
|
||||
DefaultValue,
|
||||
DictElement,
|
||||
Dictionary,
|
||||
Float,
|
||||
LevelDirection,
|
||||
SimpleLevels,
|
||||
Integer,
|
||||
)
|
||||
from cmk.rulesets.v1.rule_specs import CheckParameters, Topic, HostCondition
|
||||
|
||||
|
||||
# function name should begin with an underscore to limit it's visibility
|
||||
def _parameter_form():
|
||||
return Dictionary(
|
||||
elements={
|
||||
"levels_apps_with_updates_available": DictElement(
|
||||
parameter_form=SimpleLevels(
|
||||
title=Title("Number of installed apps with updates available"),
|
||||
form_spec_template=Integer(),
|
||||
level_direction=LevelDirection.UPPER,
|
||||
prefill_fixed_levels=DefaultValue(value=(1, 2)),
|
||||
),
|
||||
required=True,
|
||||
),
|
||||
"levels_free_space": DictElement(
|
||||
parameter_form=SimpleLevels(
|
||||
title=Title("Levels for free disk space overall"),
|
||||
form_spec_template=Float(unit_symbol="GBytes"),
|
||||
level_direction=LevelDirection.LOWER,
|
||||
prefill_fixed_levels=DefaultValue(value=(8.0, 4.0)),
|
||||
),
|
||||
required=True,
|
||||
),
|
||||
"levels_number_of_files": DictElement(
|
||||
parameter_form=SimpleLevels(
|
||||
title=Title("Number of files overall"),
|
||||
form_spec_template=Integer(),
|
||||
level_direction=LevelDirection.UPPER,
|
||||
prefill_fixed_levels=DefaultValue(value=(100_000, 250_000)),
|
||||
),
|
||||
required=True,
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
# name must begin with "rule_spec_", should refer to the used check plugin
|
||||
# must be an instance of "CheckParameters"
|
||||
rule_spec_nextcloud_info = CheckParameters(
|
||||
# "name" should be the same as the check plugin
|
||||
name="nextcloud_info",
|
||||
# the title is shown in the GUI
|
||||
title=Title("Various parameters for Nextcloud systems"),
|
||||
# this ruleset can be found under Setup|Service monitoring rules|Applications...
|
||||
topic=Topic.APPLICATIONS,
|
||||
# define the name of the function which creates the GUI elements
|
||||
parameter_form=_parameter_form,
|
||||
# define the label in the GUI where you can restrict the
|
||||
# settings to one or mor specific storages (item)
|
||||
condition=HostCondition(),
|
||||
)
|
||||
105
nextcloud/rulesets/nextcloud_params.py
Normal file
105
nextcloud/rulesets/nextcloud_params.py
Normal file
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env python3
|
||||
# pylint: disable=line-too-long, missing-module-docstring
|
||||
|
||||
from cmk.rulesets.v1 import Help, Label, Title
|
||||
from cmk.rulesets.v1.form_specs import (
|
||||
BooleanChoice,
|
||||
DictElement,
|
||||
Dictionary,
|
||||
migrate_to_password,
|
||||
Password,
|
||||
String,
|
||||
Integer,
|
||||
validators,
|
||||
InputHint,
|
||||
)
|
||||
from cmk.rulesets.v1.rule_specs import SpecialAgent, Topic
|
||||
|
||||
|
||||
def _form_spec_special_agent_nextcloud() -> Dictionary:
|
||||
return Dictionary(
|
||||
title=Title("Nextcloud Server Information"),
|
||||
help_text=Help("Checking Nextcloud servers via API"),
|
||||
elements={
|
||||
"hostname": DictElement(
|
||||
required=True,
|
||||
parameter_form=String(
|
||||
title=Title("Hostname"),
|
||||
help_text=Help(
|
||||
"Hostname of Nextcloud server (bare FQDN or IP), mandatory, eg. nextcloud.yourdomain.tld"
|
||||
),
|
||||
custom_validate=(validators.LengthInRange(min_value=1),),
|
||||
prefill=InputHint("nextcloud.yourdomain.tld"),
|
||||
),
|
||||
),
|
||||
"username": DictElement(
|
||||
required=True,
|
||||
parameter_form=String(
|
||||
title=Title("Username"),
|
||||
help_text=Help("Username with administrative rights, mandatory"),
|
||||
custom_validate=(validators.LengthInRange(min_value=1),),
|
||||
prefill=InputHint("admin"),
|
||||
),
|
||||
),
|
||||
"password": DictElement(
|
||||
required=True,
|
||||
parameter_form=Password(
|
||||
title=Title("App Password"),
|
||||
help_text=Help(
|
||||
"Specify app password, mandatory, use Personal Settings|Security|Devices and Sessions within the NC UI to create one for the given user"
|
||||
),
|
||||
custom_validate=(validators.LengthInRange(min_value=1),),
|
||||
migrate=migrate_to_password,
|
||||
),
|
||||
),
|
||||
"port": DictElement(
|
||||
required=False,
|
||||
parameter_form=Integer(
|
||||
title=Title("Port"),
|
||||
help_text=Help(
|
||||
"Specify port if not listening to HTTP(S), optional"
|
||||
),
|
||||
prefill=InputHint(8080),
|
||||
custom_validate=(validators.NetworkPort(),),
|
||||
),
|
||||
),
|
||||
"folder": DictElement(
|
||||
required=False,
|
||||
parameter_form=String(
|
||||
title=Title("Folder"),
|
||||
help_text=Help(
|
||||
"Specify subfolder if your Nextcloud instance is not installed in the web root, no trailing/leading slashes, optional"
|
||||
),
|
||||
prefill=InputHint("nextcloud"),
|
||||
),
|
||||
),
|
||||
"no_https": DictElement(
|
||||
required=False,
|
||||
parameter_form=BooleanChoice(
|
||||
title=Title("Protocol handling"),
|
||||
label=Label("Disable HTTPS"),
|
||||
help_text=Help(
|
||||
"Activate to disable encryption (not recommended), optional"
|
||||
),
|
||||
),
|
||||
),
|
||||
"no_cert_check": DictElement(
|
||||
required=False,
|
||||
parameter_form=BooleanChoice(
|
||||
title=Title("Certificate handling"),
|
||||
label=Label("Disable certificate validation"),
|
||||
help_text=Help(
|
||||
"Activate to disable certificate validation (not recommended), optional"
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
rule_spec_nextcloud = SpecialAgent(
|
||||
name="nextcloud",
|
||||
title=Title("Nextcloud connection parameters"),
|
||||
topic=Topic.APPLICATIONS,
|
||||
parameter_form=_form_spec_special_agent_nextcloud,
|
||||
)
|
||||
54
nextcloud/rulesets/nextcloud_users_rules.py
Normal file
54
nextcloud/rulesets/nextcloud_users_rules.py
Normal file
@@ -0,0 +1,54 @@
|
||||
#!/user/bin/env python3
|
||||
"""parameter form ruleset for Nextcloud users"""
|
||||
|
||||
from cmk.rulesets.v1 import Title
|
||||
from cmk.rulesets.v1.form_specs import (
|
||||
DefaultValue,
|
||||
DictElement,
|
||||
Dictionary,
|
||||
Float,
|
||||
LevelDirection,
|
||||
SimpleLevels,
|
||||
)
|
||||
from cmk.rulesets.v1.rule_specs import HostAndItemCondition, CheckParameters, Topic
|
||||
|
||||
|
||||
# function name should begin with an underscore to limit it's visibility
|
||||
def _parameter_form():
|
||||
return Dictionary(
|
||||
elements={
|
||||
"levels_users_quota_used": DictElement(
|
||||
parameter_form=SimpleLevels(
|
||||
title=Title("Levels for quota usage of users"),
|
||||
form_spec_template=Float(unit_symbol="%"),
|
||||
level_direction=LevelDirection.UPPER,
|
||||
prefill_fixed_levels=DefaultValue(value=(65.0, 85.0)),
|
||||
),
|
||||
required=True,
|
||||
),
|
||||
"levels_users_free_space": DictElement(
|
||||
parameter_form=SimpleLevels(
|
||||
title=Title("Levels for free disk space of users"),
|
||||
form_spec_template=Float(unit_symbol="MBytes"),
|
||||
level_direction=LevelDirection.LOWER,
|
||||
prefill_fixed_levels=DefaultValue(value=(256.0, 128.0)),
|
||||
),
|
||||
required=True,
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
# name must begin with "rule_spec_", should refer to the used check plugin
|
||||
# must be an instance of "CheckParameters"
|
||||
rule_spec_nextcloud_users = CheckParameters(
|
||||
# "name" should be the same as the check plugin
|
||||
name="nextcloud_users",
|
||||
# the title is shown in the GUI
|
||||
title=Title("Levels for Nextcloud users"),
|
||||
# this ruleset can be found under Setup|Service monitoring rules|Applications...
|
||||
topic=Topic.APPLICATIONS,
|
||||
# define the name of the function which creates the GUI elements
|
||||
parameter_form=_parameter_form,
|
||||
condition=HostAndItemCondition(item_title=Title("User ID")),
|
||||
)
|
||||
54
nextcloud/server_side_calls/agent_nextcloud.py
Normal file
54
nextcloud/server_side_calls/agent_nextcloud.py
Normal file
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env python3
|
||||
# pylint: disable=missing-module-docstring, missing-class-docstring, missing-function-docstring
|
||||
|
||||
from collections.abc import Iterable
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from cmk.server_side_calls.v1 import (
|
||||
HostConfig,
|
||||
Secret,
|
||||
SpecialAgentCommand,
|
||||
SpecialAgentConfig,
|
||||
)
|
||||
|
||||
|
||||
class NextcloudParams(BaseModel):
|
||||
hostname: str | None = None
|
||||
username: str | None = None
|
||||
password: Secret | None = None
|
||||
port: int | None = None
|
||||
folder: str | None = None
|
||||
no_https: bool = False
|
||||
no_cert_check: bool = False
|
||||
|
||||
|
||||
def agent_nextcloud_arguments(
|
||||
params: NextcloudParams, _host_config: HostConfig
|
||||
) -> Iterable[SpecialAgentCommand]:
|
||||
# print(f"Params: {params}")
|
||||
command_arguments: list[str | Secret] = []
|
||||
if params.hostname is not None:
|
||||
command_arguments += ["--hostname", params.hostname]
|
||||
if params.username is not None:
|
||||
command_arguments += ["--username", params.username]
|
||||
if params.password is not None:
|
||||
command_arguments += ["--password", params.password.unsafe()]
|
||||
if params.port is not None:
|
||||
command_arguments += ["--port", str(params.port)]
|
||||
if params.folder is not None:
|
||||
command_arguments += ["--folder", params.folder]
|
||||
if params.no_https:
|
||||
command_arguments.append("--no-https")
|
||||
if params.no_cert_check:
|
||||
command_arguments.append("--no-cert-check")
|
||||
# command_arguments.append(host_config.name)
|
||||
# print(f"Command Args: {command_arguments}")
|
||||
yield SpecialAgentCommand(command_arguments=command_arguments)
|
||||
|
||||
|
||||
special_agent_nextcloud = SpecialAgentConfig(
|
||||
name="nextcloud",
|
||||
parameter_parser=NextcloudParams.model_validate,
|
||||
commands_function=agent_nextcloud_arguments,
|
||||
)
|
||||
Reference in New Issue
Block a user