Skip to content

Commit

Permalink
Cisco ESA: Implement Queue service
Browse files Browse the repository at this point in the history
CMK-20863

Change-Id: I43d92de055586994024516500539a39ef1e9afb9
  • Loading branch information
szymonlip committed Feb 6, 2025
1 parent 8e3afeb commit f2900fd
Show file tree
Hide file tree
Showing 7 changed files with 401 additions and 1 deletion.
141 changes: 141 additions & 0 deletions cmk/plugins/cisco_sma/agent_based/mail_queue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#!/usr/bin/env python3
# Copyright (C) 2025 Checkmk GmbH - License: GNU General Public License v2
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
# conditions defined in the file COPYING, which is part of this source code package.


from dataclasses import dataclass
from enum import Enum
from typing import assert_never, TypedDict

from pydantic import BaseModel

from cmk.agent_based.v2 import (
check_levels,
CheckPlugin,
CheckResult,
DiscoveryResult,
render,
Result,
Service,
SimpleSNMPSection,
SNMPTree,
State,
StringTable,
)
from cmk.plugins.cisco_sma.agent_based.detect import DETECT_CISCO_SMA_SNMP
from cmk.rulesets.v1.form_specs import SimpleLevelsConfigModel


class QueueAvailabilityStatus(Enum):
queue_space_available = 1
queue_space_shortage = 2
queue_full = 3


@dataclass(frozen=True)
class Queue(BaseModel):
utilization: float
availability_status: QueueAvailabilityStatus
length: int
oldest_message_age: float


def _parse_mail_queue(string_table: StringTable) -> Queue | None:
if not string_table or not string_table[0]:
return None

data = string_table[0]

return Queue(
utilization=float(data[0]),
availability_status=QueueAvailabilityStatus(int(data[1])),
length=int(data[2]),
oldest_message_age=float(data[3]),
)


snmp_section_queue = SimpleSNMPSection(
parsed_section_name="cisco_sma_queue",
name="cisco_sma_queue",
detect=DETECT_CISCO_SMA_SNMP,
fetch=SNMPTree(
base=".1.3.6.1.4.1.15497.1.1.1",
oids=["4", "5", "11", "14"],
),
parse_function=_parse_mail_queue,
)


class Params(TypedDict):
monitoring_status_memory_available: int
monitoring_status_memory_shortage: int
monitoring_status_queue_full: int

monitoring_status_percent_queue_utilization: SimpleLevelsConfigModel[float]
monitoring_status_work_queue_messages: SimpleLevelsConfigModel[int]
monitoring_status_oldest_message_age: SimpleLevelsConfigModel[float]


def _check_mail_queue(params: Params, section: Queue) -> CheckResult:
match section.availability_status:
case QueueAvailabilityStatus.queue_space_available:
yield Result(
state=State(params["monitoring_status_memory_available"]),
notice="Memory available",
)
case QueueAvailabilityStatus.queue_space_shortage:
yield Result(
state=State(params["monitoring_status_memory_shortage"]),
notice="Memory shortage",
)
case QueueAvailabilityStatus.queue_full:
yield Result(state=State(params["monitoring_status_queue_full"]), notice="Memory full")
case _:
assert_never(section.availability_status)

yield from check_levels(
section.utilization,
label="Utilization",
render_func=render.percent,
metric_name="cisco_sma_queue_utilization",
levels_upper=params["monitoring_status_percent_queue_utilization"],
)

yield from check_levels(
section.length,
label="Total messages",
metric_name="cisco_sma_queue_length",
levels_upper=params["monitoring_status_work_queue_messages"],
notice_only=True,
)

yield from check_levels(
section.oldest_message_age,
label="Oldest message age",
metric_name="cisco_sma_queue_oldest_message_age",
levels_upper=params["monitoring_status_oldest_message_age"],
render_func=render.timespan,
notice_only=True,
)


def _discover_queue(section: Queue) -> DiscoveryResult:
yield Service()


check_plugin_queue = CheckPlugin(
name="cisco_sma_queue",
service_name="Queue",
discovery_function=_discover_queue,
check_function=_check_mail_queue,
check_ruleset_name="cisco_sma_queue",
check_default_parameters=Params(
monitoring_status_memory_available=State.OK.value,
monitoring_status_memory_shortage=State.WARN.value,
monitoring_status_queue_full=State.CRIT.value,
monitoring_status_percent_queue_utilization=("fixed", (80.0, 90.0)),
monitoring_status_work_queue_messages=("fixed", (500, 1000)),
monitoring_status_oldest_message_age=("no_levels", None),
),
)
22 changes: 22 additions & 0 deletions cmk/plugins/cisco_sma/checkman/cisco_sma_queue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
title: Cisco SMA Mail Queue
agents: snmp
catalog: os/storage
license: GPLv2
distribution: check_mk
description:
This check monitors the mail queue for the Cisco Security Management Appliance (SMA).

The mapping of memory availability statuses reported by the system can be configured via the dedicated ruleset.

The check utilizes the following OID from ASYNCOS-MAIL-MIB

- queueAvailabilityStatus .1.3.6.1.4.1.15497.1.1.1.5

- workQueueMessages .1.3.6.1.4.1.15497.1.1.1.11

- oldestMessageAge .1.3.6.1.4.1.15497.1.1.1.14

- perCentQueueUtilization .1.3.6.1.4.1.15497.1.1.1.4

discovery:
One service is created.
23 changes: 23 additions & 0 deletions cmk/plugins/cisco_sma/graphing/mail_queue_standalone.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env python3
# Copyright (C) 2025 Checkmk GmbH - License: GNU General Public License v2
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
# conditions defined in the file COPYING, which is part of this source code package.
from cmk.graphing.v1 import metrics, Title

UNIT_SECOND = metrics.Unit(metrics.TimeNotation())
UNIT_INTEGER = metrics.Unit(metrics.DecimalNotation(""))


metric_queue_length = metrics.Metric(
name="cisco_sma_queue_length",
title=Title("Queue Length"),
unit=UNIT_INTEGER,
color=metrics.Color.DARK_GREEN,
)

metric_queue_oldest_message_age = metrics.Metric(
name="cisco_sma_queue_oldest_message_age",
title=Title("Oldest Message Age"),
unit=UNIT_SECOND,
color=metrics.Color.DARK_ORANGE,
)
23 changes: 23 additions & 0 deletions cmk/plugins/cisco_sma/graphing/mail_queue_utilization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env python3
# Copyright (C) 2025 Checkmk GmbH - License: GNU General Public License v2
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
# conditions defined in the file COPYING, which is part of this source code package.
from cmk.graphing.v1 import metrics, perfometers, Title

UNIT_PERCENT = metrics.Unit(metrics.DecimalNotation("%"))

metric_queue_utilization = metrics.Metric(
name="cisco_sma_queue_utilization",
title=Title("Utilization"),
unit=UNIT_PERCENT,
color=metrics.Color.DARK_BLUE,
)

perfometer_queue_utilization = perfometers.Perfometer(
name="cisco_sma_queue_utilization_perfometer",
focus_range=perfometers.FocusRange(
lower=perfometers.Closed(0),
upper=perfometers.Open(100),
),
segments=["cisco_sma_queue_utilization"],
)
95 changes: 95 additions & 0 deletions cmk/plugins/cisco_sma/rulesets/mail_queue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#!/usr/bin/env python3
# Copyright (C) 2025 Checkmk GmbH - License: GNU General Public License v2
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
# conditions defined in the file COPYING, which is part of this source code package.


from cmk.rulesets.v1 import Label, Title
from cmk.rulesets.v1.form_specs import (
DefaultValue,
DictElement,
Dictionary,
InputHint,
Integer,
LevelDirection,
LevelsType,
Percentage,
ServiceState,
SimpleLevels,
SimpleLevelsConfigModel,
TimeMagnitude,
TimeSpan,
)
from cmk.rulesets.v1.rule_specs import CheckParameters, HostCondition, Topic


def _queue_form() -> Dictionary:
return Dictionary(
elements={
"monitoring_status_memory_available": DictElement(
parameter_form=ServiceState(
title=Title("Monitoring status if memory is available"),
prefill=DefaultValue(ServiceState.OK),
)
),
"monitoring_status_memory_shortage": DictElement(
parameter_form=ServiceState(
title=Title("Monitoring status in case of memory shortage"),
prefill=DefaultValue(ServiceState.WARN),
)
),
"monitoring_status_queue_full": DictElement(
parameter_form=ServiceState(
title=Title("Monitoring status if memory is full"),
prefill=DefaultValue(ServiceState.CRIT),
)
),
"monitoring_status_percent_queue_utilization": DictElement[
SimpleLevelsConfigModel[float]
](
required=True,
parameter_form=SimpleLevels(
title=Title("Levels on queue utilization"),
level_direction=LevelDirection.UPPER,
form_spec_template=Percentage(),
prefill_fixed_levels=InputHint((80.0, 90.0)),
),
),
"monitoring_status_work_queue_messages": DictElement[SimpleLevelsConfigModel[int]](
required=True,
parameter_form=SimpleLevels(
title=Title("Levels on total number of messages in queue"),
level_direction=LevelDirection.UPPER,
form_spec_template=Integer(),
prefill_fixed_levels=InputHint((500, 1000)),
),
),
"monitoring_status_oldest_message_age": DictElement[SimpleLevelsConfigModel[float]](
required=True,
parameter_form=SimpleLevels(
title=Title("Levels on age of oldest message"),
level_direction=LevelDirection.UPPER,
form_spec_template=TimeSpan(
label=Label(""),
displayed_magnitudes=[
TimeMagnitude.HOUR,
TimeMagnitude.MINUTE,
TimeMagnitude.SECOND,
],
prefill=DefaultValue(58.0),
),
prefill_levels_type=DefaultValue(LevelsType.NONE),
prefill_fixed_levels=InputHint((0.0, 0.0)),
),
),
}
)


rule_spec_queue = CheckParameters(
name="cisco_sma_queue",
title=Title("Cisco SMA queue"),
topic=Topic.APPLICATIONS,
parameter_form=_queue_form,
condition=HostCondition(),
)
2 changes: 1 addition & 1 deletion tests/unit/cmk/plugins/cisco_sma/agent_based/test_dns.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# Copyright (C) 2024 Checkmk GmbH - License: GNU General Public License v2
# Copyright (C) 2025 Checkmk GmbH - License: GNU General Public License v2
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
# conditions defined in the file COPYING, which is part of this source code package.

Expand Down
Loading

0 comments on commit f2900fd

Please sign in to comment.