diff --git a/agent_based/traefik_http_components.py b/agent_based/traefik_http_components.py new file mode 100644 index 0000000..3848ed7 --- /dev/null +++ b/agent_based/traefik_http_components.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python3 +# pylint: disable=missing-module-docstring, unused-argument, +# pylint: disable=missing-function-docstring, line-too-long + +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, + State, + Metric, + Result, + DiscoveryResult, + CheckResult, + check_levels, +) + + +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_traefik_http_components(string_table): + """the parse function""" + parsed_data = {} + for line in string_table: + if line[0] == "routers": + parsed_data["num_routers"] = int(line[1]) + parsed_data["num_routers_warn"] = int(line[2]) + parsed_data["num_routers_crit"] = int(line[3]) + elif line[0] == "services": + parsed_data["num_services"] = int(line[1]) + parsed_data["num_services_warn"] = int(line[2]) + parsed_data["num_services_crit"] = int(line[3]) + elif line[0] == "middlewares": + parsed_data["num_middlewares"] = int(line[1]) + parsed_data["num_middlewares_warn"] = int(line[2]) + parsed_data["num_middlewares_crit"] = int(line[3]) + return parsed_data + + +def discover_traefik_http_components(section) -> DiscoveryResult: + """the discover function""" + yield Service() + + +def check_traefik_http_components(params, section) -> CheckResult: + """the check function""" + if debug.enabled(): + pprint(section) + _level_type, levels_percent_not_ok = params["levels_traefik_http_components_not_ok"] + levels_min_routers = params["levels_traefik_min_http_routers"] + levels_max_routers = params["levels_traefik_max_http_routers"] + levels_min_services = params["levels_traefik_min_http_services"] + levels_max_services = params["levels_traefik_max_http_services"] + levels_min_middlewares = params["levels_traefik_min_http_middlewares"] + levels_max_middlewares = params["levels_traefik_max_http_middlewares"] + num_routers: int = section["num_routers"] + num_routers_warn: int = section["num_routers_warn"] + num_routers_crit: int = section["num_routers_crit"] + num_services: int = section["num_services"] + num_services_warn: int = section["num_services_warn"] + num_services_crit: int = section["num_services_crit"] + num_middlewares: int = section["num_middlewares"] + num_middlewares_warn: int = section["num_middlewares_warn"] + num_middlewares_crit: int = section["num_middlewares_crit"] + num_components: int = num_routers + num_services + num_middlewares + components_warn: int = num_routers_warn + num_services_warn + num_middlewares_warn + components_crit: int = num_routers_crit + num_services_crit + num_middlewares_crit + components_percent_not_ok: float = 0.0 + if num_components > 0: + components_percent_not_ok = ( + (components_warn + components_crit) * 100 / num_components + ) + yield Metric( + name="traefik_percent_http_components_not_ok", + value=components_percent_not_ok, + levels=levels_percent_not_ok, + ) + summary: str = f"Number of HTTP routers/services/middlewares: {num_routers}/{num_services}/{num_middlewares}" + details_routers: str = ( + f"Routers WARN: {num_routers_warn}\nRouters CRIT: {num_routers_crit}" + ) + details_services: str = ( + f"Services WARN: {num_services_warn}\nServices CRIT: {num_services_crit}" + ) + details_middlewares: str = f"Middlewares WARN: {num_middlewares_warn}\nMiddlewares CRIT: {num_middlewares_crit}\n\n" + details = f"{details_routers}\n\n{details_services}\n\n{details_middlewares}" + state: State = State.OK + if components_warn > 0: + state = State.WARN + if components_crit > 0: + state = State.CRIT + yield Result( + state=state, + summary=summary, + details=details, + ) + yield from check_levels( + metric_name="traefik_num_http_routers", + value=num_routers, + levels_lower=levels_min_routers, + levels_upper=levels_max_routers, + label="Number of HTTP routers", + notice_only=True, + ) + yield from check_levels( + metric_name="traefik_num_http_services", + value=num_services, + levels_lower=levels_min_services, + levels_upper=levels_max_services, + label="Number of HTTP services", + notice_only=True, + ) + yield from check_levels( + metric_name="traefik_num_http_middlewares", + value=num_middlewares, + levels_lower=levels_min_middlewares, + levels_upper=levels_max_middlewares, + label="Number of HTTP middlewares", + notice_only=True, + ) + + +# create the new agent section, must begin with "agent_section_" +# and must be an instance of "AgentSection" +agent_section_traefik_http_components = AgentSection( + # "name" must exactly match the section name within the agent output + name="traefik_http_components", + # define the parse function, name is arbitrary, a good choice is to choose + # "parse_" as prefix and append the section name + parse_function=parse_traefik_http_components, +) + +# create the new check plugin, must begin with "check_plugin_" +# and must be an instance of "CheckPlugin" +check_plugin_traefik_http_components = CheckPlugin( + # "name" should be the same as the corresponding section within the agent output + name="traefik_http_components", + service_name="Traefik HTTP components", + # define the discovery function, name is arbitrary, a good choice is to choose + # "discover_" as prefix and append the section name + discovery_function=discover_traefik_http_components, + # define the check function, name is arbitrary, a good choice is to choose + # "check_" as prefix and append the section name + check_function=check_traefik_http_components, + # define the default parameters + check_default_parameters={ + "levels_traefik_min_http_routers": ("fixed", (10, 5)), + "levels_traefik_max_http_routers": ("fixed", (75, 100)), + "levels_traefik_min_http_services": ("fixed", (10, 5)), + "levels_traefik_max_http_services": ("fixed", (75, 100)), + "levels_traefik_min_http_middlewares": ("fixed", (5, 2)), + "levels_traefik_max_http_middlewares": ("fixed", (20, 50)), + "levels_traefik_http_components_not_ok": ("fixed", (0.5, 1.0)), + }, + # connect to the ruleset where parameters can be defined + # must match the name of the ruleset exactly + check_ruleset_name="traefik_http_components", +) diff --git a/graphing/graph_traefik.py b/graphing/graph_traefik.py index 024c5a8..9077a59 100644 --- a/graphing/graph_traefik.py +++ b/graphing/graph_traefik.py @@ -15,6 +15,36 @@ metric_traefik_agent_execution_time = Metric( color=Color.DARK_ORANGE, ) +metric_traefik_num_http_routers = Metric( + name="traefik_num_http_routers", + title=Title("Number of HTTP routers"), + unit=Unit(DecimalNotation("")), + color=Color.LIGHT_GREEN, +) + +metric_traefik_num_http_services = Metric( + name="traefik_num_http_services", + title=Title("Number of HTTP services"), + unit=Unit(DecimalNotation("")), + color=Color.LIGHT_BLUE, +) + +metric_traefik_num_http_middlewares = Metric( + name="traefik_num_http_middlewares", + title=Title("Number of HTTP middlewares"), + unit=Unit(DecimalNotation("")), + color=Color.LIGHT_RED, +) + + +metric_traefik_percent_http_components_not_ok = Metric( + name="traefik_percent_http_components_not_ok", + title=Title("Percent of HTTP components in not OK state"), + unit=Unit(DecimalNotation("%")), + color=Color.DARK_RED, +) + + perfometer_traefik_agent_execution_time = Perfometer( name="traefik_agent_execution_time", focus_range=FocusRange(Closed(0), Closed(100)), diff --git a/rulesets/rs_traefik_http.py b/rulesets/rs_traefik_http.py new file mode 100644 index 0000000..468d4bc --- /dev/null +++ b/rulesets/rs_traefik_http.py @@ -0,0 +1,122 @@ +#!/user/bin/env python3 +"""HTTP components parameter form for Traefik""" + +from cmk.rulesets.v1 import Title, Help +from cmk.rulesets.v1.form_specs import ( + DictElement, + Dictionary, + SimpleLevels, + DefaultValue, + Integer, + LevelDirection, + Float, +) + +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_traefik_min_http_routers": DictElement( + parameter_form=SimpleLevels( + title=Title("Levels for minimum number of HTTP routers"), + help_text=Help( + "Define the levels for the minimum number of HTTP routers" + ), + form_spec_template=Integer(unit_symbol=""), + level_direction=LevelDirection.LOWER, + prefill_fixed_levels=DefaultValue(value=(10, 5)), + ), + required=True, + ), + "levels_traefik_max_http_routers": DictElement( + parameter_form=SimpleLevels( + title=Title("Levels for maximum number of HTTP routers"), + help_text=Help( + "Define the levels for the maximum number of HTTP routers" + ), + form_spec_template=Integer(unit_symbol=""), + level_direction=LevelDirection.UPPER, + prefill_fixed_levels=DefaultValue(value=(75, 100)), + ), + required=True, + ), + "levels_traefik_min_http_services": DictElement( + parameter_form=SimpleLevels( + title=Title("Levels for minimum number of HTTP services"), + help_text=Help( + "Define the levels for the minimum number of HTTP services" + ), + form_spec_template=Integer(unit_symbol=""), + level_direction=LevelDirection.LOWER, + prefill_fixed_levels=DefaultValue(value=(10, 5)), + ), + required=True, + ), + "levels_traefik_max_http_services": DictElement( + parameter_form=SimpleLevels( + title=Title("Levels for maximum number of HTTP services"), + help_text=Help( + "Define the levels for the maximum number of HTTP services" + ), + form_spec_template=Integer(unit_symbol=""), + level_direction=LevelDirection.UPPER, + prefill_fixed_levels=DefaultValue(value=(75, 100)), + ), + required=True, + ), + "levels_traefik_min_http_middlewares": DictElement( + parameter_form=SimpleLevels( + title=Title("Levels for minimum number of HTTP middlewares"), + help_text=Help( + "Define the levels for the minimum number of HTTP middlewares" + ), + form_spec_template=Integer(unit_symbol=""), + level_direction=LevelDirection.LOWER, + prefill_fixed_levels=DefaultValue(value=(5, 2)), + ), + required=True, + ), + "levels_traefik_max_http_middlewares": DictElement( + parameter_form=SimpleLevels( + title=Title("Levels for maximum number of HTTP middlewares"), + help_text=Help( + "Define the levels for the maximum number of HTTP middlewares" + ), + form_spec_template=Integer(unit_symbol=""), + level_direction=LevelDirection.UPPER, + prefill_fixed_levels=DefaultValue(value=(20, 50)), + ), + required=True, + ), + "levels_traefik_http_components_not_ok": DictElement( + parameter_form=SimpleLevels( + title=Title("Levels for percentage of not OK HTTP components"), + help_text=Help( + "Define the levels for the maximum number of HTTP routers in not OK state" + ), + form_spec_template=Float(unit_symbol="%"), + level_direction=LevelDirection.UPPER, + prefill_fixed_levels=DefaultValue(value=(0.5, 1.0)), + ), + required=True, + ), + } + ) + + +# name must begin with "rule_spec_", should refer to the used check plugin +# must be an instance of "CheckParameters" +rule_spec_traefik_http_components = CheckParameters( + # "name" should be the same as the check plugin + name="traefik_http_components", + # the title is shown in the GUI + title=Title("Traefik HTTP componments parameters"), + # 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(), +)