#!/usr/bin/env python3 # pylint: disable=too-many-locals, line-too-long, too-many-statements, too-many-branches """Mailcow check for mailboxes""" import time from pprint import pprint from cmk.utils import debug # import necessary elements from API version 2 from cmk.agent_based.v2 import ( AgentSection, CheckPlugin, Service, Result, State, Metric, render, ) def get_state_upper( levels: tuple[int | float, int | float], value: int | float ) -> State: """returns OK/WARN/CRIT depending on the given parameters""" warn, crit = levels if value >= crit: return State.CRIT if value >= warn: return State.WARN return State.OK def parse_mailcow_mailboxes(string_table): """the parse function""" parsed_data = {} # convert the raw output of the agent section to an meaningful structure # do type conversions and so on for line in string_table: mailboxname = line[0] value_active = int(line[1]) if value_active == 1: active = "yes" else: active = "no" # calculate creation and last modification date in human readable format create_time_value = line[2] if create_time_value == "None": create_time_data = "Not available" else: create_time_data = create_time_value modify_time_value = line[3] if modify_time_value == "None": modify_time_data = "Never" else: modify_time_data = modify_time_value # get display name display_name = line[4] # number of messages within mailbox number_of_messages = int(line[5]) # calculate storage used for all messages in mailbox quota = int(line[7]) total_number_of_bytes_used = int(line[8]) if quota == 0: # quota is not set, if this is the case, line[6] contains no numeric value, but the char "-" # so set all usage counters to zero percent_in_use = 0 percent_storage_used_for_messages = 0 else: # percent in use, rounded to full percent (calculated by Mailcow) percent_in_use = int(line[6]) # let's calculate our own value percent_storage_used_for_messages = total_number_of_bytes_used * 100 / quota # get time of last login for IMAP/POP3/SMTP (seconds since epoch) last_imap_login = int(line[9]) last_pop3_login = int(line[10]) last_smtp_login = int(line[11]) # transfer these times into a human friendly format if last_imap_login == 0: last_imap_login_data = "Never" else: curr_time = int(time.time()) diff_time = curr_time - last_imap_login last_imap_login_data = render.timespan(diff_time) if last_pop3_login == 0: last_pop3_login_data = "Never" else: curr_time = int(time.time()) diff_time = curr_time - last_pop3_login last_pop3_login_data = render.timespan(diff_time) if last_smtp_login == 0: last_smtp_login_data = "Never" else: curr_time = int(time.time()) diff_time = curr_time - last_smtp_login last_smtp_login_data = render.timespan(diff_time) # store all (calculated) data parsed_data[f"{mailboxname}"] = [ active, create_time_data, modify_time_data, display_name, number_of_messages, percent_in_use, quota, total_number_of_bytes_used, percent_storage_used_for_messages, last_imap_login_data, last_pop3_login_data, last_smtp_login_data, ] return parsed_data def discover_mailcow_mailboxes(section): """the discover function""" # since we have a service with item here we must create one service per item if debug.enabled(): pprint(section) for key in section: yield Service(item=key) def check_mailcow_mailboxes(item, params, section): """the check function""" mailbox = section.get(item) if not mailbox: # if a previously found domain does not exist anymore, create a meaningful result yield Result( state=State.UNKNOWN, summary="Mailbox not found anymore, maybe it was deleted?", ) return # get all infos regarding the mailbox active = mailbox[0] create_time = mailbox[1] modify_time = mailbox[2] display_name = mailbox[3] number_of_messages = mailbox[4] _percent_in_use = mailbox[5] quota = mailbox[6] total_number_of_bytes_used = mailbox[7] percent_storage_used_for_messages = mailbox[8] last_imap_login_data = mailbox[9] last_pop3_login_data = mailbox[10] last_smtp_login_data = mailbox[11] # create (main) service for used storage (mailbox quota) _type, levels = params["levels_mailcow_mailboxes_quota_used"] state_quota = get_state_upper(levels, percent_storage_used_for_messages) # create graph for used quota yield Metric( "mailcow_mailboxes_used_quota", percent_storage_used_for_messages, levels=levels ) summary_quota = f"Storage quota for mailbox of '{display_name}' is {render.percent(percent_storage_used_for_messages)}" details_quota = f"Quota: {render.bytes(total_number_of_bytes_used)} of {render.bytes(quota)} used" # create service yield Result(state=state_quota, summary=summary_quota, details=details_quota) # create some additional services and information only details notice = f"Active: {active}" yield Result(state=State.OK, notice=notice) notice = f"Creation date: {create_time}" yield Result(state=State.OK, notice=notice) notice = f"Last modified: {modify_time}" yield Result(state=State.OK, notice=notice) notice = f"Last IMAP login: {last_imap_login_data} ago" yield Result(state=State.OK, notice=notice) notice = f"Last POP3 login: {last_pop3_login_data} ago" yield Result(state=State.OK, notice=notice) notice = f"Last SMTP login: {last_smtp_login_data} ago" yield Result(state=State.OK, notice=notice) # create service for number of messages _type, levels = params["levels_mailcow_mailboxes_num_messages"] state_messages = get_state_upper(levels, number_of_messages) yield Metric("mailcow_mailboxes_messages", number_of_messages, levels=levels) notice = f"Number of messages: {number_of_messages}" yield Result(state=state_messages, notice=notice) # create the new agent section, must begin with "agent_section_" # and must be an instance of "AgentSection" agent_section_mailcow_mailboxes = AgentSection( # "name" must exactly match the section name within the agent output name="mailcow_mailboxes", # define the parse function, name is arbitrary, a good choice is to choose # "parse_" as prefix and append the section name parse_function=parse_mailcow_mailboxes, ) # create the new check plugin, must begin with "check_plugin_" # and must be an instance of "CheckPlugin" check_plugin_mailcow_mailboxes = CheckPlugin( # "name" should be the same as the corresponding section within the agent output name="mailcow_mailboxes", # this is a service with item, so you have to include a place holder for the item id service_name="Mailcow mailbox %s", # define the discovery function, name is arbitrary, a good choice is to choose # "discover_" as prefix and append the section name discovery_function=discover_mailcow_mailboxes, # define the check function, name is arbitrary, a good choice is to choose # "check_" as prefix and append the section name check_function=check_mailcow_mailboxes, # define the default parameters check_default_parameters={ "levels_mailcow_mailboxes_quota_used": ("fixed", (65.0, 85.0)), "levels_mailcow_mailboxes_num_messages": ("fixed", (1000, 2500)), }, # connect to the ruleset where parameters can be defined # must match the name of the ruleset exactly check_ruleset_name="mailcow_mailboxes", )