![]() |
This is a machine translation based on the English version of the article. It might or might not have already been subject to text preparation. If you find errors, please file a GitHub issue that states the paragraph that has to be improved. |
1. Introducción
Los check plugin son módulos de software escritos en Python que se ejecutan en un site de Checkmk y que crean y evalúan los servicios de un host.
Checkmk incluye más de 2000 plugins de check ya preparados para todo el hardware y software imaginables. El equipo de Checkmk mantiene estos plugins y cada semana se añaden nuevos. Además, en el Checkmk Exchange hay otros plugins que aportan nuestros usuarios.
Y, sin embargo, siempre puede darse el caso de que un dispositivo, una aplicación o, simplemente, una métrica determinada, que sea importante para ti, no esté cubierta todavía por ninguno de estos Plugin existentes, quizá simplemente porque es algo que se desarrolló en tu organización y, por tanto, nadie más podría tenerlo. En el artículo sobre la introducción al desarrollo de extensiones para Checkmk, puedes descubrir qué opciones tienes a tu disposición.
Este artículo te muestra cómo desarrollar verdaderos check plugins para el agente Checkmk, incluido todo lo que conlleva. Hay un artículo aparte para los check plugins basados en SNMP.
![]() |
Cuando desarrollas un plugin de comprobación basado en agente, generalmente necesitas dos plugins: el plugin de agente en el host monitorizado, que proporciona los datos, y el plugin de check en el servidor Checkmk, que evalúa estos datos. Ambos deben escribirse juntos y optimizarse el uno para el otro. Sólo así funcionarán sin problemas después. |
1.1. La documentación de la API de Check
Desde la versión de Checkmk 2.0.0, existe una API de Check de nuevo desarrollo para programar los plugins de check. En la versión de Checkmk 2.3.0, la API de Check V2 es la versión actual. En este artículo, te mostraremos cómo utilizar la API de Check versión 2 para la programación de plugins. Encontrarás información sobre la migración a la API de Check versión 2 al final de este artículo, en el capítulo Migración.
Puedes acceder a la documentación de la API de Check en cualquier momento a través de la interfaz de usuario de Checkmk: Help > Developer resources > Plugin API references. En la nueva ventana del navegador, selecciona Agent based ("Check API") > Version 2 en la barra de navegación de la izquierda:

Aquí no sólo encontrarás la documentación de la API de Check en todas las versiones compatibles, sino también la documentación de todas las demás API relevantes para la creación de plugins de check. En este artículo, no sólo utilizamos la API de Check, sino también la API de Conjuntos de reglas V1 al crear un conjunto de reglas y la API de gráficos V1 al crear definiciones métricas.
1.2. Requisitos previos
Si estás interesado en programar plugins de check, necesitarás lo siguiente:
Conocimientos del lenguaje de programación Python.
Experiencia con Checkmk, especialmente en lo que se refiere a agentes y checks.
Práctica en el uso de Linux desde la línea de comandos.
2. Escribir un plugin de agente
Si te interesa programar Plugin para Checkmk, es muy probable que ya hayas configurado un servidor Checkmk. Si lo has hecho, probablemente también hayas monitorizado el propio servidor Checkmk como host.
Aquí crearemos un escenario en el que es útil que el servidor Checkmk y el host que se está monitorizando sean idénticos. Esto nos permite utilizar las consultas Livestatus del host para obtener información sobre los grupos del host que proporciona el servidor Checkmk.
En el ejemplo descrito, supondremos una organización con varias ubicaciones:
Cada una de estas sedes está representada en Checkmk por un grupo del host.
Cada sede tiene su propio equipo de servicio.
Para garantizar que, en caso de problemas, se pueda avisar al equipo de servicio correcto, cada host debe estar asignado a una sede, es decir, también a un grupo del host. El objetivo de este ejemplo es establecer un check para garantizar que ningún host ha olvidado asignar un grupo del host.
Todo el proceso consta de dos pasos:
Leer la información para la monitorización desde el host, que es de lo que trata este capítulo.
Escribir un check plugin en el site de Checkmk que evalúe estos datos. Lo mostraremos en el próximo capítulo.
Así que, vamos ...
2.1. Recuperar y filtrar la información
El primer paso antes de escribir cualquier programa Plugin es ¡investigar! Esto significa que hay que averiguar cómo obtener la información que necesitas para la monitorización.
Para el ejemplo elegido, utilizamos el hecho de que el servidor Checkmk es también el host. Esto significa que, inicialmente, basta con recuperar los datos de estado a través de Livestatus, es decir, los datos organizados en tablas que Checkmk guarda sobre los host y servicios monitorizados en la memoria volátil.
Entra como usuario del site y consulta la información sobre los grupos del host con el siguiente comando:
OMD[mysite]:~$ lq "GET hostgroups"
action_url;alias;members;members_with_state;name;notes;notes_url;num_hosts;num_hosts_down;num_hosts_handled_problems;num_hosts_pending;num_hosts_unhandled_problems;num_hosts_unreach;num_hosts_up;num_services;num_services_crit;num_services_handled_problems;num_services_hard_crit;num_services_hard_ok;num_services_hard_unknown;num_services_hard_warn;num_services_ok;num_services_pending;num_services_unhandled_problems;num_services_unknown;num_services_warn;worst_host_state;worst_service_hard_state;worst_service_state
;Hamburg;myhost11,myhost22,myhost33;myhost11|0|1,myhost22|0|1,myhost33|0|1;Hamburg;;;3;0;0;0;0;0;3;123;10;0;10;99;0;14;99;0;24;0;14;0;2;2
;Munich;myhost1,myhost2,myhost3;myhost1|0|1,myhost2|0|1,myhost3|0|1;Munich;;;3;0;0;0;0;0;3;123;10;0;10;99;0;14;99;0;24;0;14;0;2;2
;check_mk;localhost;localhost|0|1;check_mk;;;1;0;0;0;0;0;1;66;0;0;0;4;0;1;4;61;1;0;1;0;1;1
La primera línea de la salida contiene los nombres de las columnas de la tabla hostgroups
consultada. El punto y coma actúa como separador. Las líneas siguientes contienen el contenido de todas las columnas, también separadas por punto y coma.
La salida ya es relativamente confusa en este pequeño ejemplo y contiene información que no es relevante para nuestro ejemplo. En general, deberías dejar la interpretación de los datos a Checkmk. Sin embargo, el filtrado previo en el host puede reducir el volumen de datos que hay que transferir si en realidad no se necesitan todos. Así que, en este caso, restringe la consulta a las columnas relevantes (Columns
), a los nombres de los grupos del host (name
) y a los host de esos grupos (members
):
OMD[mysite]:~$ lq "GET hostgroups\nColumns: name members"
Hamburg;myhost11,myhost22,myhost33
Munich;myhost1,myhost2,myhost3
check_mk;localhost
La interfaz Livestatus espera recibir todos los comandos y cabeceras en su propia línea separada. Los saltos de línea necesarios se indican con \n
.
En este ejemplo, actualmente hay tres grupos del host: dos grupos para las ubicaciones y uno para el grupo check_mk
. Éste contiene un host llamado localhost
.
El grupo del host check_mk
es una característica especial dentro de los grupos del host. No lo has creado tú, y no puedes añadir activamente un host a este grupo. Entonces, ¿de dónde viene este grupo del host?
Como, por definición, todo host en Checkmk debe pertenecer a un grupo, Checkmk asigna por defecto al grupo "especial" check_mk
todo host que no asignes específicamente a un grupo. En cuanto hayas asignado un host a uno de tus propios grupos de host, Checkmk lo eliminará del grupo check_mk
. Tampoco hay forma de reasignar un host al grupo del host check_mk
.
Exactamente estas propiedades del grupo check_mk
se utilizan ahora para nuestro ejemplo: Puesto que cada host debe asignarse a una ubicación, el grupo del host check_mk
debe estar vacío. Si no está vacío, hay que actuar, es decir, los hosts que contiene deben asignarse a los grupos del host y, por tanto, a sus ubicaciones correspondientes.
2.2. Incorporar el comando al agente
Hasta ahora, como usuario del site, has utilizado el comando lq
para visualizar la información, lo que resulta útil para conocer los datos.
Sin embargo, para recuperar estos datos del servidor Checkmk, el nuevo comando debe formar parte del agente Checkmk en el host monitorizado. En teoría, ahora podrías editar directamente el agente Checkmk en el archivo /usr/bin/check_mk_agent
e incluir esta parte. Sin embargo, este método tendría el inconveniente de que tu nuevo comando volvería a desaparecer cuando se actualizara el software del agente, porque este archivo se sobrescribiría durante la actualización.
Por lo tanto, es mejor crear un plugin de agente. Todo lo que necesitas para ello es un archivo ejecutable que contenga el comando y que se encuentre en el directorio /usr/lib/check_mk_agent/plugins/
.
Y una cosa más es importante: los datos no pueden salir sin más. Seguirás necesitando una cabecera de sección. Se trata de una línea con un formato especial que contiene el nombre del nuevo plugin de agente. Esta cabecera de sección permite que Checkmk reconozca posteriormente dónde empiezan los datos del nuevo plugin de agente y dónde acaban los del plugin anterior. Lo más fácil es que el plugin de agente, la cabecera de sección y el check plugin tengan el mismo nombre, aunque no sea obligatorio.
Así que, en primer lugar, necesitarás un nombre significativo para tu nuevo check plugin. Este nombre sólo puede contener letras minúsculas (sólo de la a a la z, sin diéresis ni acentos), guiones bajos y dígitos, y debe ser único. Evita conflictos de nombres con los check plugins existentes. Si tienes curiosidad por saber qué nombres ya existen, en un site de Checkmk, en la línea de comandos, puedes listarlos con cmk -L
:
OMD[mysite]:~$ cmk -L
3par_capacity agent HPE 3PAR: Capacity
3par_cpgs agent HPE 3PAR: CPGs
3par_cpgs_usage agent HPE 3PAR: CPGs Usage
3par_hosts agent HPE 3PAR: Hosts
3par_ports agent HPE 3PAR: Ports
3par_remotecopy agent HPE 3PAR: Remote Copy
3par_system agent HPE 3PAR: System
3par_volumes agent HPE 3PAR: Volumes
3ware_disks agent 3ware ATA RAID Controller: State of Disks
3ware_info agent 3ware ATA RAID Controller: General Information
3ware_units agent 3ware ATA RAID Controller: State of Units
acme_agent_sessions snmp ACME Devices: Agent Sessions
acme_certificates snmp ACME Devices: Certificates
La salida aquí sólo muestra las primeras líneas de la larguísima lista. Mediante el uso de prefijos, la asignación de muchos check plugins ya puede reconocerse fácilmente aquí. Por tanto, también se recomienda el uso de prefijos para tus propios check plugins. Por cierto, la segunda columna muestra cómo obtiene sus datos el respectivo check plugin.
Un nombre adecuado para el nuevo check plugin de nuestro ejemplo es myhostgroups
.
Ahora tienes toda la información que necesitas para crear el script del plugin de agente. Crea un nuevo archivo myhostgroups
como usuario de root
en el directorio /usr/lib/check_mk_agent/plugins/
:
#!/bin/bash
columns="name members"
site="mysite"
echo '<<<myhostgroups:sep(59)>>>'
su - ${site} lq "GET hostgroups\nColumns: ${columns}"
¿Qué significa esto en detalle?
La primera línea contiene el "shebang" (abreviatura de sharp y bang, este último es una abreviatura del signo de exclamación), mediante el cual Linux reconoce que debe ejecutar el script con el shell especificado.
Para mantener la adaptabilidad del script, a continuación se introducen dos variables
la variable
columns
, que contiene actualmente los nombres de los grupos y los miembros asociados,la variable
site
, que contiene el nombre del site Checkmk.
Utiliza el comando echo
para dar salida al título de la sección. Como las columnas de la tabla están separadas por un punto y coma, utiliza el añadido sep(59)
para especificar que se utilice el punto y coma como separador de los datos en la salida del agente. El 59 representa el código de caracteres ASCII 59, el punto y coma. Sin este añadido, se utilizaría por defecto el carácter espacio (carácter ASCII 32) como separador.
Para poder utilizar el comando lq
, que está a tu disposición como usuario del site, en un script que sea ejecutado por el usuario root
, ponle como prefijo su
.
![]() |
Es posible que acceder a |
Un punto más es importante una vez que hayas creado el archivo: haz que el archivo sea ejecutable:
root@linux# chmod +x /usr/lib/check_mk_agent/plugins/myhostgroups
Puedes probar el plugin de agente directamente a mano introduciendo la ruta completa como comando:
root@linux# /usr/lib/check_mk_agent/plugins/myhostgroups
<<<myhostgroups:sep(59)>>>
Hamburg;myhost11,myhost22,myhost33
Munich;myhost1,myhost2,myhost3
check_mk;localhost
Los grupos del host que no contienen ningún host no se listan aquí.
2.3. Probar el agente
Probar y solucionar problemas son las tareas más importantes a la hora de crear un plugin de agente que funcione. Lo mejor es proceder en tres pasos:
Prueba el plugin de agente "autónomo", como acabas de hacer en la sección anterior.
Prueba el agente en su conjunto localmente.
Recupera el agente del servidor Checkmk.
Probar el agente localmente es muy sencillo. Como root
, llama al comando check_mk_agent
:
root@linux# check_mk_agent
La nueva sección debe aparecer en algún lugar de la larguísima salida. Los Plugins de agente son emitidos por el agente al final del proceso.
Puedes desplazarte por la salida añadiendo less
(pulsa la barra espaciadora para desplazarte, /
para buscar y q
para salir):
root@linux# check_mk_agent | less
O puedes buscar en la salida las líneas interesantes. Por ejemplo, grep
con -A
tiene una opción para dar salida a unas cuantas líneas más después de cada acierto, lo que te permite buscar y dar salida a la sección cómodamente:
root@linux# check_mk_agent | grep -A3 '^<<<myhostgroups'
<<<myhostgroups:sep(59)>>>
Hamburg;myhost11,myhost22,myhost33
Munich;myhost1,myhost2,myhost3
check_mk;localhost
La tercera y última prueba se realiza directamente desde el sitio Checkmk. Incluye el host en la monitorización (por ejemplo, como localhost
), inicia sesión como usuario del site y recupera los datos del agente con cmk -d
:
OMD[mysite]:~$ cmk -d localhost | grep -A3 '^<<<myhostgroups'
Esto debería producir el mismo resultado que el comando anterior.
Si esto funciona, tu agente está listo. ¿Y qué has hecho para ello? Has creado un breve script bajo la ruta /usr/lib/check_mk_agent/plugins/myhostgroups
y lo has hecho ejecutable.
Todo lo que sigue ahora sólo tiene lugar en el servidor Checkmk: allí escribes el check plugin.
3. Escribir un simple check plugin
Preparar el agente es sólo la mitad de la diversión. Ahora tienes que enseñar a Checkmk cómo manejar la información de la nueva sección del agente, qué servicios debe generar, cuándo deben pasar a WARN o CRIT, etc. Puedes hacer todo esto programando un check plugin utilizando Python.
3.1. Preparar el archivo
Para tus propios plugins de check, encontrarás el directorio base ya preparado en la jerarquía local
del directorio del site. Se trata de ~/local/lib/python3/cmk_addons/plugins/
. El directorio pertenece al usuario del site y, por tanto, puedes escribir en él.
En este directorio, los plugins se organizan en familias de plugins, cuyos nombres de directorio pueden definirse libremente. Por ejemplo, todos los plugins relativos a los dispositivos Cisco se almacenan en la carpeta cisco
, del mismo modo que todos los plugins relativos a tus grupos del host se almacenan en la carpeta myhostgroups
. Esta convención permite que todos los plugins pertenecientes a la misma familia compartan código, y es utilizada exactamente del mismo modo por los plugins que se entregan con Checkmk.
En este subdirectorio <plug-in_family>
, se crean otros subdirectorios con nombres predefinidos según sea necesario para las distintas APIs, por ejemplo agent_based
para la API Check de los plugins de agente. A su vez, más adelante introduciremos otros subdirectorios para la API Rulesets y la API Graphing.
Crea los dos subdirectorios para el nuevo plugin de check plugin basado en agentes y, a continuación, pásate a ellos para trabajar:
OMD[mysite]:~$ mkdir -p local/lib/python3/cmk_addons/plugins/myhostgroups/agent_based
OMD[mysite]:~$ cd local/lib/python3/cmk_addons/plugins/myhostgroups/agent_based
Puedes editar tu check plugin con cualquier editor de texto instalado en el sistema Linux.
Crea aquí el archivo myhostgroups.py
para el check plugin. La convención es que el nombre del archivo refleje el nombre de la sección del agente. Es obligatorio que el archivo termine en .py
, porque a partir de la versión de Checkmk 2.0.0 los check plugins son siempre módulos reales de Python.
Un marco básico ejecutable(Descargar en GitHub), que ampliarás paso a paso a continuación, tiene el siguiente aspecto:
#!/usr/bin/env python3
from cmk.agent_based.v2 import AgentSection, CheckPlugin, Service, Result, State, Metric, check_levels
def parse_myhostgroups(string_table):
parsed = {}
return parsed
def discover_myhostgroups(section):
yield Service()
def check_myhostgroups(section):
yield Result(state=State.OK, summary="Everything is fine")
agent_section_myhostgroups = AgentSection(
name = "myhostgroups",
parse_function = parse_myhostgroups,
)
check_plugin_myhostgroups = CheckPlugin(
name = "myhostgroups",
service_name = "Host group check_mk",
discovery_function = discover_myhostgroups,
check_function = check_myhostgroups,
)
En primer lugar, tienes que importar las funciones y clases necesarias para los plugins de check de los módulos de Python. Para nuestro ejemplo, sólo importaremos lo que sea necesario o pueda ser útil en el resto de este artículo. Aquí se utiliza cmk.agent_based.v2
para especificar que se importan los módulos de la API Check V2:
from cmk.agent_based.v2 import AgentSection, CheckPlugin, Service, Result, State, Metric, check_levels
3.2. Escribir la función parse
La función parse tiene la tarea de "analizar" los datos "en bruto" del agente, es decir, analizarlos y dividirlos, y poner estos datos en una forma lógicamente estructurada que sea fácil de procesar para todos los pasos posteriores.
Como se muestra en la sección sobre la comprobación del agente, la sección suministrada por el plugin de agente tiene la siguiente estructura:
<<<myhostgroups:sep(59)>>>
Hamburg;myhost11,myhost22,myhost33
Munich;myhost1,myhost2,myhost3
check_mk;localhost
Checkmk ya divide las líneas de la sección suministrada por el plugin de agente en una lista de líneas basadas en el separador del título de la sección (en el ejemplo ;
), estas líneas son a su vez listas de palabras. Por tanto, en lugar de los datos en bruto del plugin de agente, Checkmk dispone de la siguiente estructura de datos:
[
['Hamburg', 'myhost11,myhost22,myhost33'],
['Munich', 'myhost1,myhost2,myhost3'],
['check_mk', 'localhost']
]
En la lista interior, el primer elemento contiene el nombre del grupo del host y el segundo los nombres de los host pertenecientes al grupo.
Por tanto, siempre tendrías que especificar el número de corchetes y el número de "secuencia" del contenido o contenidos deseados dentro de cada corchete. Con mayores volúmenes de datos, esto se vuelve cada vez más complejo y cada vez resulta más difícil mantener una visión de conjunto.
En este punto, una función de análisis sintáctico ofrece claras ventajas gracias a la estructura que crea. Facilita la lectura del código, los accesos son más eficaces y es mucho más fácil mantener una visión de conjunto. Transforma la estructura de datos suministrada por Checkmk de forma que puedas dirigirte a cada uno de los valores individuales por su nombre (o clave) a voluntad, y no depender de buscar repetidamente en la matriz para encontrar lo que buscas:
{
'Hamburg': {'members': 'myhost11,myhost22,myhost33'},
'Munich': {'members': 'myhost1,myhost2,myhost3'},
'check_mk': {'members': 'localhost'}
}
La convención es que la función de análisis sintáctico lleve el nombre de la sección del agente y empiece por parse_
. Recibe string_table
como único argumento. Ten en cuenta que aquí no puedes elegir libremente el argumento: debe llamarse exactamente así.
def parse_myhostgroups(string_table):
# print(string_table)
parsed = {}
for line in string_table:
parsed[line[0]] = {"members": line[1]}
# print(parsed)
return parsed
Con def
especificas en Python que se va a definir una función a continuación.parsed = {}
crea el diccionario con la estructura de datos mejorada. En nuestro ejemplo, recorreremos cada línea, elemento por elemento. El grupo del host seguido de los miembros del grupo del host se toma de cada línea y se ensambla en una entrada para el diccionario.
A continuación, se devuelve el diccionario con return parsed
.
![]() |
En el ejemplo anterior, encontrarás dos líneas comentadas. Si las comentas más tarde, cuando pruebes el check plugin, se mostrarán en la línea de comandos los datos anteriores y posteriores a la ejecución de la función parse, lo que te permitirá comprobar si la función hace realmente lo que se supone que debe hacer. |
3.3. Crear la sección agente
Para que todo este procedimiento tenga efecto, debes crear la nueva sección agente con la nueva función de análisis sintáctico. Sólo así será reconocida y tenida en cuenta por Checkmk. Para ello, crea la sección agente como una instancia de la clase AgentSection
:
agent_section_myhostgroups = AgentSection(
name = "myhostgroups",
parse_function = parse_myhostgroups,
)
Aquí es importante que el nombre de la sección coincida exactamente con el título de la sección en la salida del agente. A partir de este momento, todo plugin de check que utilice la sección myhostgroups
recibirá el valor de retorno de la función parse. Por regla general, será el plugin de check del mismo nombre. Pero también pueden suscribirse a esta sección otros plugins de check, como mostraremos en la ampliación del plugin de check.
Por cierto: Si quieres saberlo con exactitud, puedes echar un vistazo a la documentación de la API de check en este punto. Allí encontrarás una descripción detallada de esta clase -y también de las clases, funciones y objetos que se utilizarán más adelante en este artículo.

3.4. Crear un plugin de check
Para que Checkmk reconozca que hay un nuevo check plugin, hay que crearlo. Esto se hace creando una instancia de la clase CheckPlugin
.
Siempre debes especificar al menos cuatro cosas
name
: El nombre del plugin de agente. La forma más sencilla de hacerlo es utilizar el mismo nombre que la sección de tu nuevo agente. De este modo, el check definido más adelante en la función check sabrá automáticamente qué sección debe evaluar.service_name
El nombre del servicio tal y como debe aparecer en la monitorización.discovery_function
La función para descubrir servicios de este tipo (hablaremos de ello más adelante).check_function
La función que realiza el check en sí (hablaremos de ello más adelante).
El nombre de la instancia debe empezar por check_plugin_
. Entonces tiene el siguiente aspecto:
check_plugin_myhostgroups = CheckPlugin(
name = "myhostgroups",
service_name = "Host group check_mk",
discovery_function = discover_myhostgroups,
check_function = check_myhostgroups,
)
Es mejor que no lo pruebes todavía, ya que primero tienes que escribir las funciones discover_myhostgroups
y check_myhostgroups
. Éstas deben aparecer en el código fuente antes de crear la sección de agente y el plugin de check, como se ha descrito anteriormente.
![]() |
Si se utiliza el núcleo de Nagios (siempre en Checkmk Raw), no se permiten los siguientes caracteres especiales en el nombre del servicio: |
3.5. Escribir la función de descubrimiento
Una característica especial de Checkmk es el descubrimiento automático de los servicios que hay que monitorizar. Para que esto funcione, cada check plugin debe definir una función que utilice la salida del agente para reconocer si hay que crear un servicio de este tipo o qué servicios de este tipo para el host en cuestión.
La función de descubrimiento siempre se llama cuando se lleva a cabo un descubrimiento de servicios para un host. A continuación, decide si deben crearse servicios o cuáles. En el caso estándar, recibe exactamente un argumento con el nombre section
. Éste contiene los datos de la sección del agente en un formato preparado por la función de análisis sintáctico.
Por tanto, aplica la siguiente lógica simple:si existe la sección de agente myhostgroups
, crea también un servicio adecuado, que aparecerá automáticamente en todos los host en los que se distribuya el plugin de agente.
Para los plugins de check que sólo crean un servicio por host, no se necesita más información:
def discover_myhostgroups(section):
yield Service()
La función de descubrimiento debe devolver un objeto del tipo service
por cada servicio que se cree con yield
(no con return
). En Python, yield
tiene la misma función que return
: ambas devuelven un valor a la función que llama. La diferencia decisiva es que yield
recuerda hasta dónde ha llegado la función en el proceso de datos.
La siguiente llamada continúa después de la última sentencia yield
, y no vuelve a empezar desde el principio. Esto significa que no sólo se lee el primer resultado (como ocurriría con return
), sino todos los resultados en secuencia (esta ventaja será relevante más adelante en nuestro ejemplo con el descubrimiento de servicios).
3.6. Escribir la función check
Ahora puedes pasar a la función check propiamente dicha, que utiliza la salida del agente actual para decidir qué estado debe asumir el servicio y puede emitir más información.
El objetivo de la función de comprobación es establecer un check que sirva para verificar si se ha asignado un grupo del host a algún host. Para ello, comprueba si el grupo del host check_mk
contiene host. Si es así, el servicio debe recibir el estado CRIT. Si no, todo está OK y también el estado del servicio.
Aquí tienes la implementación:
def check_myhostgroups(section):
attr = section.get("check_mk")
hosts = attr["members"] if attr else ""
if hosts:
yield Result(state=State.CRIT, summary=f"Default group is not empty; Current member list: {hosts}")
else:
yield Result(state=State.OK, summary="Everything is fine")
Y ahora la explicación: La función check_myhostgroups()
primero obtiene el valor perteneciente a la clave check_mk
en la variable attr
. A continuación, la variable hosts
se vincula al valor members
si existe. Si no existe members
, hosts
permanece vacía.
A continuación, se realiza una consulta if
para la evaluación propiamente dicha:
Si la variable
hosts
tiene contenido, es decir, el grupo del hostcheck_mk
no está vacío, el estado del servicio pasa a CRIT y se emite un texto de aviso. Este texto también contiene una lista de los nombres de host de todos los host que están en el grupo del hostcheck_mk
. Para emitir el texto con expresiones se utiliza la cadena F de Python, que se llama así porque la cadena va precedida de la letraf
.Si la variable
hosts
está vacía, es decir, no hay ningún host en el grupo del hostcheck_mk
, el estado del servicio cambia a OK. En este caso, también se emite un texto de mensaje apropiado.
Una vez creada la función check, el Plugin check está listo.
El check pluginy el plugin de agenteestán disponibles en GitHub.
3.7. Prueba y activación del plugin check
La prueba y la activación se realizan en la línea de comandos con el comando cmk
.
Primero prueba el descubrimiento de servicios con la opción -I
. Añadiendo la opción v
(para verbosidad), se solicita una salida detallada. --detect-plugins
restringe la ejecución del comando a este check plugin y, a través de localhost
, a este host:
OMD[mysite]:~$ cmk -vI --detect-plugins=myhostgroups localhost
Discovering services and host labels on: localhost
localhost:
+ FETCHING DATA
No piggyback files for 'localhost'. Skip processing.
Get piggybacked data
+ ANALYSE DISCOVERED HOST LABELS
SUCCESS - Found no new host labels
+ ANALYSE DISCOVERED SERVICES
+ EXECUTING DISCOVERY PLUGINS (1)
1 myhostgroups
SUCCESS - Found 1 services
Como estaba previsto, el descubrimiento de servicios reconoce un nuevo servicio en el myhostgroups
check plugin.
Ahora puedes probar el check contenido en el check plugin:
OMD[mysite]:~$ cmk --detect-plugins=myhostgroups -v localhost
+ FETCHING DATA
No piggyback files for 'localhost'. Skip processing.
Get piggybacked data
Host group check_mk Default group is not empty; Current member list: localhost
No piggyback files for 'localhost'. Skip processing.
[agent] Success, [piggyback] Success (but no data found for this host), execution time 1.8 sec | execution_time=1.800 user_time=0.030 system_time=0.000 children_user_time=0.000 children_system_time=0.000 cmk_time_agent=1.780
Al ejecutar el check, se determinará el estado del servicio encontrado anteriormente.
Si todo ha ido como esperabas, puedes activar los cambios. Si no, encontrarás información útil en el capítulo sobre solución de problemas.
Por último, activa los cambios reiniciando el núcleo de monitorización:
OMD[mysite]:~$ cmk -R
Generating configuration for core (type nagios)...
Precompiling host checks...OK
Validating Nagios configuration...OK
Restarting monitoring core...OK
En la monitorización Checkmk, ahora encontrarás el nuevo servicio Host group check_mk en el host localhost
:

check_mk
no está vacío, el servicio es CRIT
¡Enhorabuena por haber creado con éxito tu primer plugin de check!
4. Ampliación del plugin check
4.1. Trabajo preparatorio
Ahora hay que ampliar paso a paso el primer check plugin, que se ha completado recientemente. Hasta ahora, el plugin de agente sólo ha proporcionado información sobre los nombres y los miembros de los grupos del host. Para poder evaluar el estado de los host y de los servicios que se ejecutan en ellos, por ejemplo, se necesitan más datos.
Ampliación del plugin de agente
Primero ampliarás el plugin de agente una vez para recopilar toda la información que se necesitará para ampliar el check plugin en las siguientes secciones.
Para saber qué información proporciona Checkmk sobre los grupos del host, puedes consultar todas las columnas disponibles de la tabla de grupos del host con el siguiente comando como usuario del site:
OMD[mysite]:~$ lq "GET columns\nFilter: table = hostgroups\nColumns: name"
action_url
alias
members
members_with_state
name
notes
notes_url
num_hosts
...
La salida va aún más lejos. La tabla tiene casi 30 columnas, y la mayoría de ellas tienen incluso nombres con sentido. Aquí son de interés las siguientes columnas: Número de host por grupo (columna num_hosts
), número de host en estado UP (num_hosts_up
), número de servicios de todos los host del grupo (num_services
) y número de servicios en estado OK (num_services_ok
).
Ahora estas nuevas columnas sólo tiene que suministrarlas el agente. Puedes conseguirlo ampliando el plugin de agente creado en el capítulo anterior.
Como usuario root, edita el script del plugin de agente. Como el script ya ha puesto los valores configurables en variables, basta con cambiar sólo la línea que empieza por columns
e introducir allí las cuatro columnas adicionales recuperadas:
#!/bin/bash
columns="name members num_hosts num_hosts_up num_services num_services_ok"
site="mysite"
echo '<<<myhostgroups:sep(59)>>>'
su - ${site} lq "GET hostgroups\nColumns: ${columns}"
Ejecuta el script para comprobarlo:
root@linux# /usr/lib/check_mk_agent/plugins/myhostgroups
<<<myhostgroups:sep(59)>>>
Munich;myhost3,myhost2,myhost1;3;3;180;144
Hamburg;myhost22,myhost33,myhost11;3;2;132;105
check_mk;localhost;1;1;62;45
Los cuatro nuevos valores -separados cada uno por un punto y coma- aparecen ahora al final de cada línea.
Con este cambio, el plugin de agente proporciona ahora datos distintos a los anteriores. En este punto, es importante asegurarse de que, con los datos modificados, el plugin de check sigue haciendo lo que se supone que debe hacer.
Ampliar la función de análisis sintáctico
En un plugin de check, la función de análisis sintáctico se encarga de convertir los datos suministrados por el plugin de agente. Al escribir la función de análisis sintáctico, sólo has tenido en cuenta dos columnas de la tabla del grupo del host. Ahora se suministran seis columnas en lugar de dos. Por tanto, la función de análisis sintáctico debe personalizarse para procesar las cuatro columnas adicionales.
Como usuario del site, modifica la función parse en el archivo myhostgroups.py
, que contiene el Plugin de check:
def parse_myhostgroups(string_table):
parsed = {}
column_names = [
"name",
"members",
"num_hosts",
"num_hosts_up",
"num_services",
"num_services_ok",
]
for line in string_table:
parsed[line[0]] = {}
for n in range(1, len(column_names)):
parsed[line[0]][column_names[n]] = line[n]
return parsed
Aquí se ha cambiado todo lo que hay entre parsed = {}
y return parsed
. En primer lugar, las columnas que hay que procesar se definen con sus nombres como una lista column_names
. A continuación, se crea un diccionario en el bucle for
generando los pares clave-valor de cada línea a partir del nombre de la columna y el valor leído.
Esta ampliación no es crítica para la función de comprobación existente, ya que la estructura de datos de las dos primeras columnas permanece inalterada. Sólo se están proporcionando columnas adicionales, que (todavía) no se evalúan en la función de comprobación.
Ahora que los nuevos datos se pueden procesar, también se utilizarán estos datos.
4.2. Descubrimiento de servicios
En el capítulo anterior has construido un check muy sencillo que crea un servicio en un host. Sin embargo, también es muy habitual que haya varios servicios a partir de un único check en un host.
El ejemplo más común de esto es un servicio para un sistema de archivos en un host. El check plugin con el nombre df
crea un servicio por cada sistema de archivos en el host. Para distinguir estos servicios, el punto de montaje del sistema de archivos (por ejemplo /var
) o la letra de la unidad (por ejemplo C:
) se incorpora al nombre del servicio. Esto da lugar a un nombre de servicio como Filesystem /var
o Filesystem C:
. La palabra /var
o C:
se denomina aquí item. Por lo tanto, también estamos hablando de un check con items.
Si quieres construir un check con items, debes implementar las siguientes funciones:
La función de descubrimiento debe generar un servicio para cada uno de los items que deben ser monitorizados en el host.
Debes incluir el item en el nombre del servicio utilizando el marcador de posición
%s
(por ejemplo"Filesystem %s"
).La función check se llama una vez, por separado para cada item, y recibe éste como argumento. A partir de los datos del agente, debe pescar los datos relevantes para este item.
Para comprobarlo en la práctica, crearás un servicio distinto para cada grupo del host existente.
Puesto que el plugin de comprobación myhostgroups
creado en el capítulo anterior para comprobar el grupo estándar check_mk
debe seguir funcionando, este plugin de comprobación permanece tal cual. Para la ampliación, crea el nuevo plugin de comprobación myhostgroups_advanced
en el archivo existente myhostgroups.py
.- en el primer paso, como anteriormente, creando una instancia de la clase CheckPlugin
.
Aquí puedes encontrar el código antiguo y el nuevo resaltado:
check_plugin_myhostgroups = CheckPlugin(
name = "myhostgroups",
service_name = "Host group check_mk",
discovery_function = discover_myhostgroups,
check_function = check_myhostgroups,
)
check_plugin_myhostgroups_advanced = CheckPlugin(
name = "myhostgroups_advanced",
sections = [ "myhostgroups" ],
service_name = "Host group %s",
discovery_function = discover_myhostgroups_advanced,
check_function = check_myhostgroups_advanced,
)
Para que el nuevo plugin de check pueda distinguirse del antiguo, se le da un nombre único con myhostgroups_advanced
. El parámetro sections
determina las secciones de la salida del agente a las que se suscribe el plugin de check. Aquí, myhostgroups
se utiliza para especificar que el nuevo plugin de check utiliza los mismos datos que el antiguo: la sección del plugin de agente preparada por la función parse.
El nombre del servicio contiene ahora el marcador de posición %s
. El nombre del item es insertado posteriormente en este punto por Checkmk. En las dos últimas líneas se definen los nombres de la nueva función de descubrimiento y de la nueva función de comprobación, ambas aún por escribir.
En primer lugar, la función de descubrimiento, que ahora tiene la tarea de determinar los items que se van a monitorizar, se introduce además de la ya existente:
def discover_myhostgroups_advanced(section):
for group in section:
if group != "check_mk":
yield Service(item=group)
Como en el caso anterior, la función de descubrimiento recibe el argumento section
. Los grupos de host individuales se ejecutan en un bucle. Todos los grupos de host son de interés aquí, con la excepción de check_mk
, ya que de este grupo de host especial ya se ha ocupado el check plugin existente myhostgroups
. Siempre que se encuentra un item, se devuelve con yield
, que crea un objeto del tipo Service
, que a su vez recibe el nombre del grupo del host como item.
Si el host se monitoriza posteriormente, la función check se llama por separado para cada servicio, y por tanto para cada item. Esto te lleva a la definición de la función check para el nuevo plugin de comprobación myhostgroups_advanced
. La función check recibe el argumento item
además de la sección. La primera línea de la función tiene entonces este aspecto
def check_myhostgroups_advanced(item, section):
El algoritmo de la función check es sencillo: si el grupo del host existe, el servicio se pone en OK y se enumeran el número y los nombres de los host del grupo. La función completa para esto:
def check_myhostgroups_advanced(item, section):
attr = section.get(item)
if attr:
yield Result(state=State.OK, summary=f"{attr['num_hosts']} hosts in this group: {attr['members']}")
El resultado del check se entrega devolviendo un objeto de la clase Result
a través de yield
. Para ello se necesitan los parámetros state
y summary
. Aquí, state
define el estado del servicio (en el ejemplo OK
) y summary
el texto que se muestra en la Summary del servicio. Esto es puramente informativo y Checkmk no lo evalúa más. Encontrarás más información al respecto en la siguiente sección.
Hasta aquí todo bien, pero ¿qué ocurre si no encuentras el item que buscas? Esto puede ocurrir si en el pasado ya se creó un servicio para un grupo del host, pero este grupo del host ha desaparecido, bien porque el grupo del host sigue existiendo en Checkmk pero ya no contiene ningún host, bien porque se ha eliminado por completo. En ambos casos, este grupo del host ya no estará presente en la salida del agente.
La buena noticia es que Checkmk se ocupa de esto: si no se encuentra un item buscado, Checkmk genera automáticamente el resultado UNKNOWN - Item not found in monitoring data
para el servicio. Esto es intencionado y algo bueno: si no se encuentra un item buscado, puedes simplemente ejecutar Python fuera de la función y dejar que Checkmk haga su trabajo.
Checkmk sólo sabe que el item que antes estaba allí ahora no está. Checkmk no sabe la razón de ello, pero tú sí. Por tanto, es conveniente no guardarse ese conocimiento e interceptar esta condición en la función check y mostrar un mensaje de ayuda.
def check_myhostgroups_advanced(item, section):
attr = section.get(item)
if not attr:
yield Result(state=State.CRIT, summary="Group is empty or has been deleted")
return
yield Result(state=State.OK, summary=f"{attr['num_hosts']} hosts in this group: {attr['members']}")
¿Qué ha cambiado? Ahora se trata primero la condición de error. Por tanto, comprueba en la rama if
si el item realmente no existe, establece el estado a CRIT y sal de la función con return
. En todos los demás casos, devuelve OK como antes.
Esto significa que has adoptado la situación de grupos del host que desaparecen en la función check. En lugar de UNKNOWN, el servicio asociado será ahora CRIT y contendrá información sobre la causa del estado crítico.
Esto completa el nuevo check plugin como una extensión del antiguo. En GitHub puedes encontrar de nuevo el plugin de agenteextendido y el archivo extendido para los check plugins. Este último contiene el simple check plugin myhostgroups
del capítulo anterior, la función de análisis sintáctico avanzado y los componentes del nuevo check plugin myhostgroups_advanced
con la creación del check plugin, la función de descubrimiento y la función de comprobación. Ten en cuenta que las funciones deben definirse siempre antes de crear los check plugins o las secciones de agente, para que no se produzcan errores por nombres de función no definidos.
Como el nuevo myhostgroups_advanced
check plugin proporciona nuevos servicios, debes realizar un descubrimiento de servicios para este check plugin y activar los cambios para ver estos servicios en la monitorización:

Procede como se describe en el capítulo del check plugin simple. No ejecutes los dos primeros comandos para myhostgroups
, en su lugar ejecútalos para el nuevo check plugin myhostgroups_advanced
.
4.3. Resumen y detalles
En la monitorización de Checkmk, cada servicio tiene un estado-OK, WARN, etc.-, así como una línea de texto. Este texto se encuentra en la columna Summary -como se puede ver en la captura de pantalla anterior- y, por tanto, tiene la función de proporcionar un breve resumen del estado del servicio. El concepto es que este texto no supere una longitud de 60 caracteres, lo que garantiza una visualización concisa de la tabla sin molestos saltos de línea.
También está el campo Details, en el que se muestran todos los detalles sobre el estado del servicio, que también incluye toda la información resumida. Al hacer clic en el servicio, se abre la página del servicio, en la que se pueden ver los dos campos Summary y Details junto con muchos otros.
Al llamar a yield Result(...)
, puedes determinar qué información es tan importante que deba mostrarse en el resumen, y para cuál es suficiente que aparezca en los detalles.
En nuestro ejemplo, siempre has utilizado una llamada del siguiente tipo
yield Result(state=State.OK, summary=f"{attr['num_hosts']} hosts in this group: {attr['members']}")
Esto significa que el texto definido como summary
siempre aparece en el Summary- y también en el Details. Por lo tanto, sólo debes utilizarlo para la información importante. Si un grupo del host contiene muchos host, la lista resumen puede llegar a ser muy larga - más de los 60 caracteres recomendados. Si una información es de importancia secundaria, puedes utilizar details
para especificar que su texto sólo aparezca en los detalles:
yield Result(
state = State.OK,
summary = f"{attr['num_hosts']} hosts in this group",
details = f"{attr['num_hosts']} hosts in this group: {attr['members']}",
)
Por tanto, en el ejemplo anterior, la lista de host sólo se muestra en Details. En Summary sólo se muestra el número de host del grupo:

Además de summary
y details
, existe un tercer parámetro. Con notice
puedes especificar que el texto de un servicio en OK sólo se muestre en los detalles, pero también en el resumen de todos los demás estados. De este modo, en el resumen queda inmediatamente claro por qué el servicio no está OK. El parámetro notice
no es especialmente útil si los textos están permanentemente vinculados a los estados, como en nuestro ejemplo hasta ahora.
En resumen, esto significa
El texto total del resumen no debe superar los 60 caracteres para los servicios que están OK.
Utiliza siempre
summary
onotice
; al menos uno u otro, pero no ambos.Si es necesario, añade
details
si el texto para los detalles va a ser alternativo.
4.4. Múltiples resultados parciales por servicio
Para evitar que el número de servicios de un host aumente excesivamente, a menudo se combinan varios resultados parciales en un solo servicio. Por ejemplo, el servicio Memory de Linux no sólo comprueba la carga de RAM y swap, sino también la memoria compartida, las tablas de páginas y todo tipo de cosas.
La API de comprobación proporciona una interfaz muy cómoda para ello. Una función de comprobación puede generar simplemente un resultado con yield
tantas veces como sea necesario. El estado general del servicio se basa entonces en el peor resultado parcial en el orden OK → WARN → UNKNOWN → CRIT.
En el ejemplo, utiliza esta opción para definir dos resultados adicionales para cada servicio de los grupos del host, además del resultado existente. Éstos evalúan el porcentaje de host en estado UP y los servicios en estado OK. Utilizas las columnas adicionales de la tabla de grupos del host definidas previamente en la salida del agente y la función de análisis sintáctico.
Ahora expande la función check por etapas de arriba a abajo:
def check_myhostgroups_advanced(item, section):
attr = section.get(item)
if not attr:
yield Result(state=State.CRIT, summary="Group is empty or has been deleted")
return
members = attr["members"]
num_hosts = int(attr["num_hosts"])
num_hosts_up = int(attr["num_hosts_up"])
num_services = int(attr["num_services"])
num_services_ok = int(attr["num_services_ok"])
La rama if
permanece sin cambios, es decir, los nuevos resultados parciales sólo se aplican a los grupos del host que también existen. A continuación, define cinco variables para las columnas de la tabla de grupos del host que contiene la sección. Por un lado, esto aumenta la legibilidad y, por otro, puedes convertir las cadenas leídas en números con int()
para las cuatro columnas que aún deben utilizarse para el cálculo.
El único resultado existente permanece (casi) inalterado:
yield Result(
state = State.OK,
summary = f"{num_hosts} hosts in this group",
details = f"{num_hosts} hosts in this group: {members}",
)
Sólo el acceso en la "cadena F" de Python a la expresión que devuelve el valor es ahora más fácil que antes, puesto que el attr
ya está en las definiciones de las variables.
Pasemos ahora al núcleo real de la ampliación, la definición de un resultado que implemente la siguiente expresión: "El servicio del grupo del host es WARN cuando el 90 % de los host están UP, y CRIT cuando el 80 % de los host están UP" La convención aquí es que el check pase a WARN o CRIT en cuanto se alcance el umbral, y no sólo cuando se supere. La API Check proporciona la función auxiliar check_levels
para comparar un valor determinado con los valores umbrales.
hosts_up_perc = 100.0 * num_hosts_up / num_hosts
yield from check_levels(
hosts_up_perc,
levels_lower = ("fixed", (90.0, 80.0)),
label = "UP hosts",
notice_only = True,
)
En la primera línea, el porcentaje se calcula a partir del número total y del número de host en estado UP y se almacena en la variable hosts_up_perc
. La barra simple (/
) ejecuta una división en coma flotante, lo que garantiza que el resultado es un valor flotante. Esto es útil porque algunas de las funciones que se utilizan más adelante esperan un valor flotante como entrada.
En la segunda línea, el resultado de la función check_levels
se devuelve como un objeto del tipo Result
, que puedes encontrar en la documentación de la API. Esta función se alimenta con el porcentaje que se acaba de calcular como valor (hosts_up_perc
), los dos valores umbrales inferiores (levels_lower
), una label que precede a la salida (label
) y, por último, con notice_only=True
.
El último parámetro hace uso del parámetro notice
ya introducido en la sección anterior para el objeto Result()
. Con notice_only=True
especificas que el texto del servicio sólo se muestre en Summary si el estado no es OK. Sin embargo, los resultados parciales que conduzcan a un WARN o CRIT siempre serán visibles en el resumen -independientemente del valor de notice_only
.
Por último, define el tercer resultado del mismo modo que el segundo, que evalúa el porcentaje de servicios en estado OK:
services_ok_perc = 100.0 * num_services_ok / num_services
yield from check_levels(
services_ok_perc,
levels_lower = ("fixed", (90.0, 80.0)),
label = "OK services",
notice_only = True,
)
Esto completa la función check.
El servicio para un grupo del host evalúa ahora tres resultados y muestra el peor estado de ellos en la monitorización, como en el ejemplo siguiente:

4.5. Métricas
No siempre, pero a menudo, los checks tratan con números, y estos números son muy a menudo valores medidos o calculados. En nuestro ejemplo, el número de host en el grupo del host (num_hosts
) y el número de host en estado UP (num_hosts_up
) son los valores medidos. El porcentaje de host en estado UP (hosts_up_perc
) es un valor calculado a partir de estos valores. Si este valor puede mostrarse en un intervalo de tiempo, también se denomina métrica.
Con su sistema de gráficos integrado, Checkmk dispone de un componente para almacenar, evaluar y mostrar dichos valores, que es totalmente independiente del cálculo de los estados OK, WARN y CRIT.
En este ejemplo, definirás los dos valores calculados, hosts_up_perc
y services_ok_perc
, como métricas. Las métricas serán visibles inmediatamente en la interfaz gráfica de usuario de Checkmk sin que tengas que hacer nada. Se genera automáticamente un gráfico para cada métrica.
Las métricas las determina la función check y las devuelve como resultado adicional. La forma más sencilla es añadir la información de las métricas a la función check_levels()
en la llamada.
Como recordatorio, aquí siguen las líneas con la llamada a la función check_levels()
de la sección anterior:
yield from check_levels(
hosts_up_perc,
levels_lower = ("fixed", (90.0, 80.0)),
label = "UP hosts",
notice_only = True,
)
Los dos nuevos argumentos para la métrica son metric_name
y boundaries
:
yield from check_levels(
hosts_up_perc,
levels_lower = ("fixed", (90.0, 80.0)),
metric_name = "hosts_up_perc",
label = "UP hosts",
boundaries = (0.0, 100.0),
notice_only = True,
)
Para que todo sea sencillo y tenga sentido, para el nombre de la métrica utiliza el nombre de la variable en la que se almacena el porcentaje como valor.
Puedes utilizar boundaries
para proporcionar al sistema de gráficos información sobre el rango de valores posibles. Esto se refiere al valor más pequeño y al más grande posibles. En el caso de un porcentaje, los límites de 0.0
y 100.0
no son demasiado difíciles de determinar. Se permiten tanto números de coma flotante como números enteros (que se convierten internamente en números de coma flotante), pero no cadenas. Si sólo se define un límite del rango de valores, simplemente introduce None
para el otro, por ejemplo boundaries = (0.0, None)
.
Con esta ampliación, la función check_levels
ahora también devuelve un objeto del tipo Metric
a través de yield
además del Result
.
Ahora puedes definir la métrica services_ok_perc
del mismo modo. Las últimas líneas de la función check tendrán entonces el siguiente aspecto:
hosts_up_perc = 100.0 * num_hosts_up / num_hosts
yield from check_levels(
hosts_up_perc,
levels_lower = ("fixed", (90.0, 80.0)),
metric_name = "hosts_up_perc",
label = "UP hosts",
boundaries = (0.0, 100.0),
notice_only = True,
)
services_ok_perc = 100.0 * num_services_ok / num_services
yield from check_levels(
services_ok_perc,
levels_lower = ("fixed", (90.0, 80.0)),
metric_name = "services_ok_perc",
label = "OK services",
boundaries = (0.0, 100.0),
notice_only = True,
)
Con la función de check ampliada, ambos gráficos son visibles en la monitorización. En la lista de servicios, el icono muestra ahora que hay gráficos para el servicio. Si señalas el icono con el ratón, los gráficos se mostrarán como una vista previa.

En los detalles del servicio encontrarás una Vista general de todos los gráficos, con sus leyendas y demás.
Pero, ¿qué haces si el valor de la métrica deseada no se ha definido con la función check_levels()
? Por supuesto, puedes definir una métrica independientemente de una llamada a la función. Para ello se utiliza el objeto Metric()
, que también puedes crear directamente mediante su constructor. La definición alternativa de una métrica para el valor hosts_up_perc
tiene este aspecto:
yield Metric(
name = "hosts_up_perc",
value = hosts_up_perc,
levels = (80.0, 90.0),
boundaries = (0.0, 100.0),
)
Los argumentos de Metric()
son muy similares a los de la llamada a la función mostrada anteriormente: son obligatorios los dos primeros argumentos para el nombre de la métrica y el valor. Además, hay dos argumentos opcionales: levels
para los valores umbrales WARN y CRIT y boundaries
para el intervalo de valores.
![]() |
La especificación de |
Ahora utiliza la opción de definir no sólo los dos valores calculados, sino todos los valores medidos como métricas utilizando Metric()
-en nuestro ejemplo, los cuatro valores medidos de la tabla del grupo del host. Limítate a las dos especificaciones obligatorias de nombre del host y valor de la métrica. Las cuatro nuevas líneas completan la ampliación de la función de comprobación de métricas:
hosts_up_perc = 100.0 * num_hosts_up / num_hosts
yield from check_levels(
hosts_up_perc,
levels_lower = (90.0, 80.0),
metric_name = "hosts_up_perc",
label = "UP hosts",
boundaries = (0.0, 100.0),
notice_only = True,
)
services_ok_perc = 100.0 * num_services_ok / num_services
yield from check_levels(
services_ok_perc,
levels_lower = (90.0, 80.0),
metric_name = "services_ok_perc",
label = "OK services",
boundaries = (0.0, 100.0),
notice_only = True,
)
yield Metric(name="num_hosts", value=num_hosts)
yield Metric(name="num_hosts_up", value=num_hosts_up)
yield Metric(name="num_services", value=num_services)
yield Metric(name="num_services_ok", value=num_services_ok)
Esto aumenta el número de gráficos por servicio, pero también te da la opción de combinar varias métricas en un solo gráfico, por ejemplo. Mostramos éstas y otras opciones en la sección Personalizar la visualización de las métricas, más abajo.
En el archivo de ejemplo enGitHubpuedes encontrar de nuevo toda la función check.
5. Conjuntos de reglas para los parámetros de check
En el Plugin de comprobación ampliado myhostgroups_advanced
habrás generado el estado WARN si sólo el 90 % de los host están UP, y CRIT para el 80 %. En este caso, los números 90
y 80
se definen explícitamente en la función de comprobación, o, como dirían los programadores, hard-coded. En Checkmk, sin embargo, los usuarios están acostumbrados a poder configurar dichos valores umbrales y otros parámetros de comprobación mediante reglas.
Por ejemplo, si un grupo del host sólo tiene cuatro miembros, los dos valores umbrales del 90 % y el 80 % no encajan bien, ya que el porcentaje bajará al 75 % en cuanto falle el primer host y el estado pasará directamente a CRIT, sin un estado WARN intermedio.
Por lo tanto, ahora hay que modificar el check plugin para que se pueda configurar a través de la interfaz Setup. Para ello, necesitarás un conjunto de reglas.
Para crear un conjunto de reglas para un check plugin, sal del desarrollo del check plugin y cambia el directorio, el archivo y la API. Desde Checkmk 2.3.0, la API de conjuntos de reglas te ayuda a crear esos conjuntos de reglas para los check plugins. La documentación de la API para los conjuntos de reglas se encuentra en tu site de Checkmk, en la misma página que la API de check, en Rulesets > Version 1.
5.1. Definir un nuevo conjunto de reglas
Para crear un nuevo conjunto de reglas, crea primero un nuevo subdirectorio en el directorio de la familia de plugins ~/local/lib/python3/cmk_addons/plugins/myhostgroups/
. El nombre rulesets
está predefinido para este subdirectorio:
OMD[mysite]:~$ mkdir -p local/lib/python3/cmk_addons/plugins/myhostgroups/rulesets
OMD[mysite]:~$ cd local/lib/python3/cmk_addons/plugins/myhostgroups/rulesets
A continuación, crea en este directorio un archivo para la definición del conjunto de reglas. El nombre del archivo debe basarse en el del plugin check y, como todos los archivos de los plugins, debe tener la extensión py
. Para nuestro ejemplo, es adecuado el nombre de archivo ruleset_myhostgroups.py
.
Veamos paso a paso la estructura de este archivo. Primero hay algunos comandos de importación:
#!/usr/bin/env python3
from cmk.rulesets.v1 import Label, Title
from cmk.rulesets.v1.form_specs import BooleanChoice, DefaultValue, DictElement, Dictionary, Float, LevelDirection, SimpleLevels
from cmk.rulesets.v1.rule_specs import CheckParameters, HostAndItemCondition, Topic
En primer lugar, se importan las clases de los textos.
La segunda línea hace una selección de las especificaciones de formulario, es decir, los elementos básicos para la GUI que se utilizan en el conjunto de reglas, por ejemplo, la selección binaria (BooleanChoice
), la selección múltiple (Dictionary
, DictElement
), la definición de valores umbrales (SimpleLevel
, LevelDirection
, DefaultValue
) y la introducción de números de coma flotante (Float
). Aquí sólo solicitas los elementos lógicos del formulario y dejas el diseño de la GUI a Checkmk.
La última línea importa las especificaciones de la regla, que determinan el ámbito de aplicación de la regla en Checkmk, es decir, aquí se definen los parámetros de check, la asignación a host y items y el almacenamiento bajo un tema. Como tu check plugin myhostgroups_advanced
genera varios servicios, importa aquí HostAndItemCondition
. Si tu check no genera ningún servicio, es decir, no tiene ningún item, importa en su lugar HostCondition
.
Ahora vienen las definiciones reales del formulario para introducir los parámetros de check. El usuario debe poder definir por separado los dos valores umbrales de WARN y CRIT tanto para el número de host en estado UP como para el número de servicios en estado OK:
def _parameter_form():
return Dictionary(
elements = {
"hosts_up_lower": DictElement(
parameter_form = SimpleLevels(
title = Title("Lower percentage threshold for host in UP status"),
form_spec_template = Float(),
level_direction = LevelDirection.LOWER,
prefill_fixed_levels = DefaultValue(value=(90.0, 80.0)),
),
required = True,
),
"services_ok_lower": DictElement(
parameter_form = SimpleLevels(
title = Title("Lower percentage threshold for services in OK status"),
form_spec_template = Float(),
level_direction = LevelDirection.LOWER,
prefill_fixed_levels = DefaultValue(value=(90.0, 80.0)),
),
required = True,
),
}
)
Para ello, crea una función que genere el diccionario. Puedes elegir libremente el nombre de la función; sólo es obligatorio al crear la regla siguiente. El nombre debe empezar por un guión bajo para que la función no sea visible más allá del límite del módulo.
El formulario return Dictionary()
es obligatorio. Dentro de él, utiliza elements={}
para crear los elementos del diccionario, en el ejemplo hosts_up_lower
y services_ok_lower
. El formulario de parámetros SimpleLevels
se utiliza para introducir valores umbrales fijos. En el formulario, define primero los números title
y de coma flotante para los valores a introducir (form_spec_template
). Utiliza LevelDirection.LOWER
para especificar que el estado cambiará si los valores caen por debajo de este nivel.
Por último, puedes utilizar prefill_fixed_levels
para proporcionar a los usuarios del conjunto de reglas valores en lugar de campos de entrada vacíos. Ten en cuenta que estos valores mostrados en la GUI no son los valores por defecto que se establecen más abajo en la creación del check plugin mediante check_default_parameters
. Si quieres mostrar los mismos valores por defecto en la GUI que también se aplican a la función de comprobación, debes mantener la coherencia de los valores en ambos lugares.
Por último, crea el nuevo conjunto de reglas utilizando los items importados y autodefinidos. Esto se hace creando una nueva instancia de la clase CheckParameters
. El nombre de esta instancia debe empezar por rule_spec_
:
rule_spec_myhostgroups = CheckParameters(
name = "myhostgroups_advanced",
title = Title("Host group status"),
topic = Topic.GENERAL,
parameter_form = _parameter_form,
condition = HostAndItemCondition(item_title=Title("Host group name")),
)
Se aplican las siguientes explicaciones:
El
name
del conjunto de reglas establece su conexión con los plugins de comprobación. Un plugin de comprobación que quiera utilizar este conjunto de reglas debe utilizar este nombre comocheck_ruleset_name
cuando se cree. Para llevar la cuenta de varios conjuntos de reglas nuevos, es aconsejable utilizar un prefijo en el nombre.title
define el título del conjunto de reglas tal y como aparece en la GUI de Checkmk.topic
Setup determina dónde debe aparecer el conjunto de reglas en la caja 1 . Con el valor seleccionado en el ejemplo, encontrarás el conjunto de reglas bajo en la caja , donde suele estar a buen recaudo. Setup > Services > Service monitoring rules VariousIntroduce el nombre de la función creada anteriormente como
parameter_form
.Si tu check no utiliza un item, la condición es
HostCondition
y noHostAndItemCondition
, como en el ejemplo anterior. Con el título"Host group name"
del item, defines la label en la GUI con la que puedes restringir la regla a determinados grupos del host.
5.2. Probar un conjunto de reglas
Una vez que hayas creado el archivo para el conjunto de reglas, debes probar si todo funciona hasta ahora, aún sin conexión con el check plugin. Para ello, primero debes reiniciar el Apache del site para que se pueda leer el nuevo archivo. Esto se realiza con el comando:
OMD[mysite]:~$ omd restart apache
El conjunto de reglas debería encontrarse entonces en Setup en la página mencionada anteriormente. También puedes encontrar el conjunto de reglas utilizando la función de búsqueda del menú de configuración - pero sólo después de reiniciar Redis:
OMD[mysite]:~$ omd restart redis
El conjunto de reglas que acabas de definir tendrá este aspecto en la GUI:

En la caja Conditions, encontrarás el campo de entrada Host group name definido a través de HostAndItemCondition
para restringir la regla para grupos del host.
Crea una regla y prueba con distintos valores, como se muestra en la captura de pantalla anterior. Si funciona sin errores, ya puedes utilizar los parámetros de check en la función de verificación.
5.3. Conexión del conjunto de reglas con el plugin de check
El conjunto de reglas recién creado está ahora conectado con el check plugin completado provisionalmente en el capítulo anterior. Para que la regla surta efecto, debes permitir que el check plugin acepte parámetros de check e indicarle qué regla debe utilizar. Para ello, añade dos nuevas líneas al archivo del check plugin al crear el myhostgroups_advanced
check plugin:
check_plugin_myhostgroups_advanced = CheckPlugin(
name = "myhostgroups_advanced",
sections = [ "myhostgroups" ],
service_name = "Host group %s",
discovery_function = discover_myhostgroups_advanced,
check_function = check_myhostgroups_advanced,
check_default_parameters = {},
check_ruleset_name = "myhostgroups_advanced",
)
Con la entrada check_default_parameters
, puedes definir los valores por defecto que se aplican mientras no se haya creado ninguna regla. Al convertir el Plugin de check para parámetros de check, esta línea debe estar presente. En el caso más sencillo, pasa un diccionario vacío {}
.
En segundo lugar, introduce check_ruleset_name
, es decir, el nombre del conjunto de reglas. De este modo, Checkmk sabrá a partir de qué conjunto de reglas deben determinarse los parámetros.
Ahora Checkmk intentará pasar parámetros a la función check. Para que esto funcione, debes ampliar la función check para que espere el argumento params
, que se inserta entre item
y section
:
def check_myhostgroups_advanced(item, params, section):
Si estás construyendo un check sin item, se omite item
y se pone params
al principio.
Es muy recomendable que el contenido de la variable params
salga con un print
como primera prueba:
def check_myhostgroups_advanced(item, params, section):
print(params)
Cuando se ejecute el plugin de check, las líneas impresas (una por cada servicio) con los valores definidos por una regla tendrán entonces un aspecto parecido al siguiente:
OMD[mysite]:~$ cmk --detect-plugins=myhostgroups_advanced -v localhost
Parameters({'hosts_up_lower': ('fixed', (75.0, 60.0)), 'services_ok_lower': ('fixed', (75.0, 60.0))})
Parameters({'hosts_up_lower': ('fixed', (75.0, 60.0)), 'services_ok_lower': ('fixed', (75.0, 60.0))})
Parameters({'hosts_up_lower': ('fixed', (75.0, 60.0)), 'services_ok_lower': ('fixed', (75.0, 60.0))})
![]() |
Cuando todo esté listo y funcione correctamente, elimina las llamadas a |
Ahora personaliza aún más tu función check para que los parámetros pasados puedan tener efecto. Obtén de los parámetros los dos elementos del diccionario definidos en la regla con el nombre allí seleccionado:
def check_myhostgroups_advanced(item, params, section):
hosts_up_lower = params["hosts_up_lower"]
services_ok_lower = params["services_ok_lower"]
Más abajo en la función check, los valores umbrales codificados previamente "fixed", (90.0, 80.0)
se sustituyen por las variables hosts_up_lower
y services_ok_lower
:
hosts_up_perc = 100.0 * num_hosts_up / num_hosts
yield from check_levels(
hosts_up_perc,
levels_lower = (hosts_up_lower),
metric_name = "hosts_up_perc",
label = "UP hosts",
boundaries = (0.0, 100.0),
notice_only = True,
)
services_ok_perc = 100.0 * num_services_ok / num_services
yield from check_levels(
services_ok_perc,
levels_lower = (services_ok_lower),
metric_name = "services_ok_perc",
label = "OK services",
boundaries = (0.0, 100.0),
notice_only = True,
)
Si se ha configurado una regla, ahora puedes monitorizar los grupos del host del ejemplo con los valores umbrales establecidos a través de la GUI. Sin embargo, si no se ha definido ninguna regla, esta función de check se bloqueará, ya que no se habrán rellenado los parámetros por defecto del check plugin, y el plugin generará un KeyError
en ausencia de una regla.
Sin embargo, este problema puede solucionarse si los valores por defecto se definen al crear el plugin de check:
check_plugin_myhostgroups_advanced = CheckPlugin(
name = "myhostgroups_advanced",
sections = [ "myhostgroups" ],
service_name = "Host group %s",
discovery_function = discover_myhostgroups_advanced,
check_function = check_myhostgroups_advanced,
check_default_parameters = {"hosts_up_lower": ("fixed", (90, 80)), "services_ok_lower": ("fixed", (90, 80))},
check_ruleset_name = "myhostgroups_advanced",
)
Siempre debes pasar valores por defecto de esta forma (y no interceptar el caso de que falten parámetros en el check plugin), ya que estos valores por defecto también se pueden mostrar en la interfaz Setup. Por ejemplo, en el descubrimiento de servicios de un host, en la página Services of host, en el menú Display, está el interruptor Show check parameters.
![]() |
En GitHub puedes encontrar tanto el archivo con elconjunto de reglascomo elPlugin de check extendido por el conjunto de reglas. |
6. Personalización de las visualizaciones métricas
En el ejemplo anterior, hiciste que el plugin check plugin myhostgroups_advanced
generara métricas para todos los valores medidos y calculados. Hemos presentado dos formas de hacerlo. En primer lugar, las métricas de los valores calculados se crearon como parte de la función check_levels()
con el argumento metric_name
, por ejemplo, así
yield from check_levels(
services_ok_perc,
levels_lower = ("fixed", (90.0, 80.0)),
metric_name = "services_ok_perc",
label = "OK services",
boundaries = (0.0, 100.0),
notice_only = True,
)
A continuación, has generado las métricas medidas directamente con el objeto Metric()
, por ejemplo, para el número de servicios en estado OK, así
yield Metric(name="num_services_ok", value=num_services_ok)
Las métricas se harán visibles inmediatamente en la interfaz gráfica de usuario de Checkmk sin que tengas que hacer nada. Sin embargo, existen algunas restricciones:
Las métricas coincidentes no se combinan automáticamente en un gráfico, sino que cada una aparece individualmente.
La métrica no tendrá un título propio, en su lugar se muestra el nombre de la variable interna de la métrica.
No se utiliza ninguna unidad que permita una representación significativa (por ejemplo, GB en lugar de bytes individuales).
Se selecciona un color al azar.
El "Perf-O-Meter", es decir, la vista previa gráfica de la métrica en forma de barra, no aparece automáticamente en la lista de servicios (por ejemplo, en la vista que muestra todos los servicios de un host).
Para completar la visualización de tus métricas con tales detalles, necesitarás definiciones de métricas.
Al igual que para los conjuntos de reglas, a partir de Checkmk 2.3.0 también existe una API independiente para métricas, gráficos y Perf-O-Meters con la API de gráficos. La documentación de la API de gráficos se encuentra en tu site de Checkmk, en la misma página que la API de checks, en Graphing > Version 1.
6.1. Crear una nueva definición métrica
Los procedimientos para crear conjuntos de reglas y definiciones métricas son muy similares. Primero crea un nuevo subdirectorio con el nombre por defecto graphing
en el directorio de tu familia de Plugin ~/local/lib/python3/cmk_addons/plugins/myhostgroups/
.
OMD[mysite]:~$ mkdir -p local/lib/python3/cmk_addons/plugins/myhostgroups/graphing
OMD[mysite]:~$ cd local/lib/python3/cmk_addons/plugins/myhostgroups/graphing
A continuación, crea un archivo gráfico en este directorio, por ejemplo graphing_myhostgroups.py
.
Primero hay de nuevo algunos comandos de importación:
#!/usr/bin/env python3
from cmk.graphing.v1 import Title
from cmk.graphing.v1.graphs import Graph, MinimalRange
from cmk.graphing.v1.metrics import Color, DecimalNotation, Metric, Unit
from cmk.graphing.v1.perfometers import Closed, FocusRange, Open, Perfometer
Para nuestro ejemplo, ahora defines tu propia métrica para el porcentaje de servicios en estado OK. Esto se hace creando una instancia de la clase Metric
. El nombre de esta instancia debe empezar por metric_
metric_myhostgroups_services_ok_perc = Metric(
name = "services_ok_perc",
title = Title("Percentage of services in OK state"),
unit = Unit(DecimalNotation("%")),
color = Color.ORANGE,
)
He aquí la explicación:
El nombre de la métrica (aquí
services_ok_perc
) debe corresponderse con lo que muestra la función check.El
title
es el encabezamiento del gráfico métrico y sustituye al nombre de variable interna utilizado anteriormente.Las unidades disponibles (
unit
) se pueden encontrar en la documentación de la API, todas ellas terminan enNotation
, por ejemploDecimalNotation
,EngineeringScientificNotation
oTimeNotation
.Los nombres de los colores utilizados en Checkmk para la definición
color
se pueden encontrar en GitHub.
Esta definición en el archivo gráfico garantiza ahora que el título, la unidad y el color de la métrica se muestren adecuadamente.
De forma similar a la creación de un archivo de conjunto de reglas, el archivo de gráficos debe leerse primero para que el cambio sea visible en la GUI, lo que se consigue reiniciando el Apache del site:
OMD[mysite]:~$ omd restart apache
El gráfico métrico tendrá entonces este aspecto en la GUI de Checkmk:

6.2. Gráficos con múltiples métricas
Si quieres combinar varias métricas en un gráfico (lo que suele ser muy útil), necesitarás una definición de gráfico que puedes añadir al archivo de gráficos creado en la sección anterior.
En nuestro ejemplo, las dos métricas num_services
y num_services_ok
deben mostrarse en un único gráfico. Las definiciones métricas para ello se crean del mismo modo que en la sección anterior para services_ok_perc
y tienen el siguiente aspecto:
metric_myhostgroups_services = Metric(
name = "num_services",
title = Title("Number of services in group"),
unit = Unit(DecimalNotation("")),
color = Color.PINK,
)
metric_myhostgroups_services_ok = Metric(
name = "num_services_ok",
title = Title("Number of services in OK state"),
unit = Unit(DecimalNotation("")),
color = Color.BLUE,
)
Ahora añade un gráfico que dibuje estas dos métricas como líneas. Esto se hace mediante una instancia de la clase Graph
, cuyo nombre debe empezar de nuevo con un prefijo predefinido (graph_
):
graph_myhostgroups_combined = Graph(
name = "services_ok_comparison",
title = Title("Services in OK state out of total"),
simple_lines=[ "num_services", "num_services_ok" ],
minimal_range=MinimalRange(0, 50),
)
El parámetro minimal_range
describe el rango mínimo cubierto por el eje vertical del gráfico, independientemente de los valores reales de la métrica. El eje vertical se ampliará si los valores superan el rango mínimo, pero nunca será menor.
El resultado es el gráfico combinado de la GUI de Checkmk:

6.3. Métricas en el Perf-O-Metro
¿Te gustaría mostrar un Perf-O-Metro para una métrica en la fila de la lista de servicios? Podría tener este aspecto, por ejemplo:

Para crear ese Perf-O-Meter, necesitarás otra instancia, esta vez de la clase Perfometer
, cuyo nombre empieza por el prefijo perfometer_
:
perfometer_myhostgroups_advanced = Perfometer(
name = "myhostgroups_advanced",
focus_range = FocusRange(Closed(0), Closed(100)),
segments = [ "services_ok_perc" ],
)
Los Perf-O-Metros son un poco más complicados que los gráficos, ya que no tienen leyenda. Por tanto, es difícil mostrar un rango de valores, sobre todo cuando se muestran valores absolutos. Es más fácil mostrar un porcentaje, como en el ejemplo anterior:
Los nombres de las métricas se introducen en
segments
, en este ejemplo el porcentaje de servicios en estado OK.Los límites inferior y superior se introducen en
focus_range
. Los límites deClosed
están pensados para métricas que sólo pueden aceptar valores entre los límites especificados (aquí0
y100
).
En la documentación de la API de gráficos se pueden encontrar opciones aún más sofisticadas para implementar métricas en Perf-O-Meters, por ejemplo, con las clases Bidirectional
y Stacked
, que permiten visualizar varios Perf-O-Meters en uno.
En GitHub puedes encontrar el archivo de gráficos de este capítulo, que contiene, entre otras cosas, las definiciones métricas de los cuatro valores medidos y de los dos valores calculados.
7. Formatear números
Los números se muestran a menudo en el Summary y en el Details de un servicio. Para facilitarte al máximo el formateo ordenado y correcto, y también para estandarizar la salida de todos los check plugins, existen funciones helper para mostrar los distintos tipos de tamaños. Todas ellas son subfunciones del módulo render
y, por tanto, se llaman con render.
. Por ejemplo, render.bytes(2000)
da como resultado el texto 1.95 KiB
.
Lo que todas estas funciones tienen en común es que su valor se muestra en una unidad denominada canónica o natural. Esto significa que nunca tienes que pensar y que no hay dificultades ni errores al convertir. Por ejemplo, los tiempos se dan siempre en segundos, y los tamaños de discos duros, archivos, etc., se dan siempre en bytes y no en kilobytes, kibibytes, bloques u otras confusiones por el estilo.
Utiliza estas funciones aunque no te guste mucho la visualización. En cualquier caso, estará estandarizada para el usuario. Las futuras versiones de Checkmk podrán cambiar la visualización o incluso hacerla configurable por el usuario, como ya ocurre con la visualización de la temperatura, por ejemplo. Tu plugin de check también se beneficiará de ello.
Antes de poder utilizar la función render
en tu plugin de check, también debes importarla:
from cmk.agent_based.v2 import render
Tras esta descripción detallada de todas las funciones de visualización (funciones de renderizado), encontrarás un resumen en forma de tabla fácil de leer.
7.1. Tiempos, intervalos de tiempo, frecuencias
Las especificaciones absolutas de tiempo (marcas de tiempo) se formatean con render.date()
o render.datetime()
. La información siempre se da como hora Unix, es decir, en segundos desde el 1 de enero de 1970, 00:00:00 UTC - el comienzo de la época Unix. Éste es también el formato utilizado por la función de Python time.time()
.
La ventaja de esta representación es que es muy fácil de calcular, por ejemplo el cálculo de un intervalo de tiempo, cuando se conocen las horas de inicio y fin. La fórmula es entonces simplemente duration = end - start
. Estos cálculos funcionan independientemente de la zona horaria, los cambios de horario de verano o los años bisiestos.
render.date()
sólo da salida a la fecha, render.datetime()
añade la hora. La salida es según la zona horaria actual en la que se encuentre el servidor Checkmk que está ejecutando el check. Ejemplos:
Llamada a | Salida |
---|---|
|
|
|
|
|
|
|
|
No te sorprendas de que render.datetime(0)
no muestre 00:00
como hora, sino 01:00
. Esto se debe a que estamos escribiendo este Manual de usuario en la zona horaria de Alemania, que está una hora por delante de la hora UTC estándar (al menos durante el horario estándar, porque el 1 de enero no hay horario de verano).
Para los intervalos de tiempo (o lapsos de tiempo) también existe la función render.timespan()
. A ésta se le da una duración en segundos y la emite de forma legible para el ser humano. Para lapsos de tiempo mayores, se omiten los segundos o los minutos. Si tienes un intervalo de tiempo en un objeto TimeDelta
, utiliza la función total_seconds()
para leer el número de segundos como un número en coma flotante.
Llama a | Salida |
---|---|
|
|
|
|
|
|
|
|
Una frecuencia es efectivamente el recíproco del tiempo. La unidad canónica es Hz, que significa lo mismo que 1 / seg (el recíproco de un segundo). Un ejemplo de su uso es la frecuencia de reloj de una CPU:
Llamada | Salida |
---|---|
|
|
7.2. Bytes
Siempre que se trate de memoria de trabajo, archivos, discos duros, sistemas de archivos y similares, la unidad canónica es el byte. Como los ordenadores suelen organizar estas cosas en potencias de dos, por ejemplo en unidades de 512, 1024 o 65.536 bytes, se estableció desde el principio que un kilobyte no es 1000, y por tanto mil veces la unidad, sino 1024 (2 a la potencia de 10) bytes. Esto es ciertamente ilógico, pero muy práctico porque suele dar como resultado números redondos. El legendario Commodore C64 tenía 64 kilobytes de memoria y no 65.536.
Por desgracia, en algún momento a los fabricantes de discos duros se les ocurrió especificar los tamaños de sus discos en unidades de 1000. Como la diferencia entre 1000 y 1024 es del 2,4 % para cada tamaño, y éstas se multiplican, un disco de tamaño 1 GB (1024 por 1024 por 1024) se convierte de repente en 1,07 GB. Eso se vende mejor.
Esta molesta confusión sigue existiendo hoy en día y sigue produciendo errores. Para paliarla, la Comisión Electrotécnica Internacional (CEI) ha definido nuevos prefijos basados en el sistema binario. Así, hoy un kilobyte es oficialmente 1000 bytes y un kibibyte 1024 bytes (2 a la potencia de 10). Además, hay que decir mebibyte, gibibyte y tebibyte. Las abreviaturas son entonces KiB, MiB, GiB y TiB.
Checkmk se ajusta a esta norma y te ayuda con una serie de funciones de representación personalizadas para garantizar que siempre produzcas la salida correcta. Por ejemplo, está la función render.disksize()
, especial para discos duros y sistemas de archivos, que produce su salida en potencias de 1000.
Llama a | Salida |
---|---|
|
|
|
|
|
|
Cuando se trata del tamaño de los archivos, a menudo es habitual especificar el tamaño exacto en bytes sin redondear. Esto tiene la ventaja de que puedes ver muy rápidamente si un archivo ha cambiado aunque sea mínimamente o que dos archivos son (probablemente) iguales. Para ello se utiliza la función render.filesize()
:
Llama a | Salida |
---|---|
|
|
|
|
|
|
Si quieres dar salida a un valor que no sea el tamaño de un disco duro o de un archivo, utiliza simplemente la función genérica render.bytes()
. Con ella obtendrás la salida en las "clásicas" 1024 potencias de la notación oficial:
Llama a | Salida |
---|---|
|
|
|
|
|
|
7.3. Anchos de banda, velocidades de datos
Los que trabajan en red tienen sus propios términos y formas de expresar las cosas. Y como siempre, Checkmk hace todo lo posible por adoptar la forma convencional de comunicarse en cada ámbito. Por eso hay tres funciones de representación diferentes para las tasas de datos y las velocidades. Lo que todas tienen en común es que las tasas se pasan en bytes por segundo, ¡aunque la salida real sea en bits!
render.nicspeed()
representa la velocidad máxima de una tarjeta de red o de un puerto del switch. Como no son valores medidos, no es necesario redondear. Aunque ningún puerto puede enviar bits individuales, los datos están en bits por razones históricas.
Importante: No obstante, ¡también debes pasar aquí los bytes por segundo!
Llama a | Salida |
---|---|
|
|
|
|
render.networkbandwidth()
está pensada para una velocidad de transmisión medida realmente en la red. El valor de entrada es de nuevo bytes por segundo:
Llamada | Salida |
---|---|
|
|
|
|
|
|
Cuando no se trata de una red y las velocidades de transmisión de datos siguen siendo de salida, los bytes vuelven a ser habituales. El caso más destacado es el de las velocidades de entrada/salida de los discos duros. Para ello se utiliza la función render.iobandwidth()
, que en Checkmk funciona con potencias de 1000:
Llama a | Salida |
---|---|
|
|
|
|
|
|
7.4. Porcentajes
La función render.percent()
representa un porcentaje -redondeado a dos decimales-. Es una excepción a las demás funciones, ya que no pasa el valor natural real -es decir, el cociente- sino el porcentaje real. Por ejemplo, si algo está medio lleno, no tienes que pasar 0.5
sino 50
.
Como a veces puede ser interesante saber si un valor es casi cero o exactamente cero, los valores se marcan añadiendo un carácter "<", para los valores que son mayores que cero pero menores que 0,01 por ciento.
Llama a | Salida |
---|---|
|
|
|
|
|
|
7.5. Resumen
En conclusión, aquí tienes un resumen de todas las funciones de renderizado:
Función | Entrada | Descripción | Ejemplo de salida |
---|---|---|---|
|
Hora Unix |
Fecha |
|
|
Hora Unix |
Fecha y hora |
|
|
Segundos |
Duración / edad |
|
|
Hz |
Frecuencia (por ejemplo, frecuencia de reloj) |
|
|
Bytes |
Tamaño de un disco duro, base 1000 |
|
|
Bytes |
Tamaño de un archivo, precisión total |
|
|
Bytes |
Tamaño, base 1024 |
|
|
Bytes por segundo |
Velocidad de la tarjeta de red |
|
|
Bytes por segundo |
Velocidad de transmisión |
|
|
Bytes por segundo |
Anchos de banda IO |
|
|
Número porcentual |
Porcentaje, redondeado significativamente |
|
8. Solución de errores
El tratamiento correcto de los errores (por desgracia) ocupa una gran parte de cualquier trabajo de programación. La buena noticia es que la API de check ya elimina gran parte del trabajo de tratamiento de errores. Por tanto, para algunos tipos de errores, es mejor que no los manejes tú mismo.
Si Python se encuentra con una situación inesperada, reacciona con una excepción. He aquí algunos ejemplos:
Conviertes una cadena en un número con
int()
, pero la cadena no contiene un número, por ejemploint("foo")
.Utilizas
bar[4]
para acceder al quinto elemento debar
, pero sólo tiene cuatro elementos.Estás llamando a una función que no existe.
Para decidir cómo abordar los errores, primero es importante conocer el punto exacto del código en el que se produce un error. Para ello, puedes utilizar la GUI o la línea de comandos, dependiendo de dónde estés trabajando en ese momento.
8.1. Excepciones e informes de fallos en la GUI
Si se produce una excepción en la monitorización o durante el descubrimiento de servicios en la Setup, la Summary contiene referencias al informe de fallos que se acaba de crear. Por ejemplo, tendrá el siguiente aspecto:

Si haces clic en el icono, aparecerá una página con detalles en la que podrás
puedes ver el archivo en el que se ha producido el crash,
recibir toda la información sobre el crash, como una lista de los errores que se produjeron en el programa(rastrear), los valores actuales de las variables locales, la salida del agente y mucho más, y
puedes enviarnos el informe a nosotros (Checkmk GmbH) como respuesta.
El rastrear te ayuda como desarrollador a decidir si hay un error en el programa (por ejemplo, la llamada a una función inexistente) o datos del agente que no se han podido procesar como se esperaba. En el primer caso querrás corregir el error, en el segundo a menudo tiene sentido no hacer nada.
Por supuesto, enviar el informe sólo es útil para los plugins de check que forman parte oficialmente de Checkmk. Si pones tus propios plugins a disposición de terceros, puedes pedir a tus usuarios que te envíen los datos.
8.2. Vista de excepciones en la línea de comandos
Si ejecutas tu check plugin utilizando la línea de comandos, no recibirás ninguna indicación del ID de ningún informe de fallos generado. Sólo verás el mensaje de error resumido:
OMD[mysite]:~$ cmk --detect-plugins=myhostgroups_advanced localhost
Error in agent based plugin myhostgroups: invalid syntax (myhostgroups.py, line 11)
Si añades la opción --debug
como parámetro de llamada adicional, recibirás el rastreo del intérprete de Python:
OMD[mysite]:~$ cmk --debug --detect-plugins=myhostgroups_advanced localhost
Traceback (most recent call last):
File "/omd/sites/mysite/lib/python3/cmk/discover_plugins/_python_plugins.py", line 195, in add_from_module
module = importer(mod_name, raise_errors=True)
^^^^^^^^^^^^^
File "/omd/sites/mysite/lib/python3/cmk/discover_plugins/_python_plugins.py", line 156, in _import_optionally
return importlib.import_module(module_name)
^^^^^^^^^^^^
File "/omd/sites/mysite/lib/python3.12/importlib/init.py", line 90, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
^^^^^^^^^^^^^^^^^^
File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 995, in exec_module
File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
File "/omd/sites/mysite/local/lib/python3/cmk_addons/plugins/myhostgroups/agent_based/myhostgroups.py", line 110, in <module>
agent_section_myhostgroups == AgentSection(
^^^^^^^^^^
NameError: name 'agent_section_myhostgroups' is not defined
Si el error no vuelve a producirse la próxima vez que llames a --debug
, por ejemplo porque hay disponible una nueva salida del agente, también puedes ver los últimos informes de fallos en el sistema de archivos:
OMD[mysite]:~$ ls -lhtr ~/var/check_mk/crashes/check/ | tail -n 5
drwxrwxr-x 2 mysite mysite 4.0K Aug 6 17:56 7b59a49e-540c-11ef-9595-7574c603ce8d/
drwxrwxr-x 2 mysite mysite 4.0K Aug 6 17:56 7c8870c0-540c-11ef-9595-7574c603ce8d/
drwxrwxr-x 2 mysite mysite 4.0K Aug 6 17:56 7cf9626c-540c-11ef-9595-7574c603ce8d/
drwxrwxr-x 2 mysite mysite 4.0K Aug 6 17:56 7d192d68-540c-11ef-9595-7574c603ce8d/
drwxrwxr-x 2 mysite mysite 4.0K Aug 6 17:56 7e2d5ec2-540c-11ef-9595-7574c603ce8d/
Hay dos archivos en cada una de estas carpetas:
crash.info
contiene un diccionario Python con rastreos y mucha más información. A menudo basta con echar un vistazo al archivo con el Buscapersonas.agent_output
contiene la salida completa del agente que estaba activo en el momento del crash.
8.3. Salida de depuración personalizada
En los ejemplos mostrados anteriormente, utilizamos la función print()
para dar salida al contenido de las variables o a la estructura de los objetos para ti como desarrollador. Estas funciones para la salida de depuración deben eliminarse del Plugin de check plugin terminado.
Como alternativa a la eliminación, también puedes hacer que la salida de depuración sólo se muestre cuando se llame al check plugin desde la consola en modo de depuración. Para ello, importa el objeto de depuración de la caja de herramientas Checkmk y, si es necesario, la ayuda de formato pprint()
. Ahora puedes producir la salida de depuración en función del valor del objeto de depuración:
![]() |
El objeto de depuración cambiará su ruta entre Checkmk 2.3.0 y 2.4.0. En lugar de la ubicación actual |
try:
from cmk.ccc import debug
except ImportError:
from cmk.utils import debug
from pprint import pprint
def check_mystuff(section):
if debug.enabled():
pprint(section)
Ten en cuenta que cualquier salida de depuración restante debe utilizarse con moderación y limitarse a pistas que ayuden a los usuarios posteriores con la depuración. Los errores de usuario obvios y previsibles (por ejemplo, que el contenido de la sección de agente indique que el plugin de agente se ha configurado incorrectamente) deben responderse con el estado UNKNOWN e incluir notas explicativas en el resumen.
8.4. Salida inválida del agente
La cuestión es cómo debes reaccionar si la salida del agente no tiene la forma que realmente esperas, ya sea del agente Checkmk o recibida a través de SNMP. Supongamos que siempre esperas tres palabras por línea, ¿qué debes hacer si sólo obtienes dos?
Bueno, si se trata de un comportamiento permitido y conocido por el agente, entonces, por supuesto, tienes que interceptarlo y trabajar con una distinción de casos. Sin embargo, si en realidad no está permitido, entonces lo mejor es actuar como si la línea constara siempre de tres palabras, por ejemplo, con la siguiente función de análisis sintáctico:
def parse_foobar(string_table):
for foo, bar, baz in string_table:
# ...
Si hay una línea que no consta exactamente de tres palabras, se genera una excepción y recibes el informe de fallos tan útil que acabamos de mencionar.
Si accedes a claves de un diccionario que se espera que falten ocasionalmente, puede tener sentido, por supuesto, reaccionar en consecuencia. Esto se puede hacer estableciendo el servicio en CRIT o DESCONOCIDO y poniendo una nota en el resumen sobre la salida del agente que no se puede evaluar. En cualquier caso, es mejor utilizar la función get()
del diccionario para esto que capturar la excepción KeyError
. Esto se debe a que get()
devuelve un objeto de tipo None
o un sustituto opcional que se debe pasar como segundo parámetro si la clave no está disponible:
def check_foobar(section):
foo = section.get("bar")
if not foo:
yield Result(state=State.CRIT, summary="Missing key in section: bar")
return
# ...
8.5. Items que faltan
¿Qué ocurre si el agente emite datos correctos, pero falta el item que hay que comprobar? Así, por ejemplo:
def check_foobar(item, section):
# Try to access the item as key in the section:
foo = section.get(item)
if foo:
yield Result(state=State.OK, summary="Item found in monitoring data")
# If foo is None, nothing is yielded here
Si el item que buscas no está incluido, se ejecuta el bucle y Python simplemente lo abandona al final de la función sin devolver un resultado a través de yield
. ¡Y ése es exactamente el enfoque correcto! Porque Checkmk reconoce que falta el item a monitorizar y genera el estado correcto y un texto estándar adecuado con UNKNOWN.
8.6. Pruebas con archivos spool
Si quieres simular determinadas salidas del agente, los archivos del directorio spool son muy útiles. Puedes utilizarlos para probar casos límite que, de otro modo, serían difíciles de recrear. O puedes utilizar directamente la salida del agente que provocó un informe de fallos para probar cambios en un plugin de checkin.
En primer lugar, desactiva tu plugin de agente habitual, por ejemplo, revocando su autorización de ejecución. A continuación, crea un archivo en el directorio /var/lib/check_mk_agent/spool/
que contenga la sección del agente (o las secciones esperadas del agente) que espera tu plugin de check plugin, incluida la cabecera de la sección, y que termine con Newline. La próxima vez que se llame al agente, se transferirá el contenido del archivo spool en lugar de la salida del plugin de agente.
8.7. Los antiguos check plugins se vuelven lentos para muchos servicios
Con algunos check plugins que utilizan items, es muy posible que en servidores grandes se generen varios cientos de servicios. Si no se utiliza una función de análisis separada, esto significa que hay que recorrer toda la lista de cientos de líneas para cada uno de los cientos de items. Por tanto, el tiempo necesario para buscar aumenta por el cuadrado del número de items listados, lo que significa decenas de miles de comparaciones para cientos de servicios. Si, por el contrario, la lista anidada se transfiere a un diccionario, el tiempo necesario para buscar un elemento sólo aumenta linealmente con el tamaño del diccionario.
En la wiki de Python encontrarás una vista general de los costes de búsqueda en distintos tipos de datos, incluyendo una explicación y la notación O. El uso de la función parse reduce la complejidad de la búsqueda de O(n) a O(1).
Como las versiones anteriores de este artículo no hacían uso de la función parse, debes identificar tales plugins de check y reescribirlos para que utilicen una función parse.
9. Migración
La API de check V1 introducida en la versión 2.0.0 de Checkmk seguirá siendo compatible con la versión 2.3.0, pero dejará de serlo en la próxima versión 2.4.0. Por tanto, recomendamos aprovechar el tiempo hasta el lanzamiento de la versión 2.4.0 y migrar todos los plugins de check a las nuevas API.
La siguiente información te ayudará con la migración de los plugins de comprobación de la API de comprobación V1 a la V2:
La nueva estructura de directorios y la convención de nomenclatura para almacenar los archivos de todas las API de los plugins se encuentran en la documentación de la API en la página principal Checkmk’s Plug-in APIs.
El resumen de los cambios en la API de Check V2 también se puede encontrar en la documentación de la API en Checkmk’s Plug-in APIs > Agent based ('Check API') > Version 2 > New in this version. Allí también encontrarás el enlace a un commit de GitHub que migra el plugin de check existente
apt
a la API de Check V2.En GitHub encontrarás scripts en el directorio
treasures
de Checkmk que te ayudarán a migrar a las nuevas API.
Una nota final para los usuarios que todavía tienen plugins de comprobación que se desarrollaron con la antigua API de comprobación que era válida hasta la versión 1.6.0 de Checkmk. Estos plugins de comprobación ya no se admiten oficialmente en la versión 2.3.0 de Checkmk. Puedes encontrar más detalles en Werk #16689. Puedes averiguar cómo migrar estos plugins de comprobación obsoletos a la API de comprobación V1 en la entrada del blog sobre migración.
10. Archivos y directorios
Ruta del archivo | Descripción |
---|---|
|
Directorio base para almacenar archivos de Plugin. |
|
Ubicación de almacenamiento para los Plugin de check escritos según la API de Check V2. |
|
Ubicación de almacenamiento para archivos de conjuntos de reglas creados según la API de Conjuntos de Reglas. |
|
Ubicación de almacenamiento para los archivos de gráficos creados según la API de gráficos. |
|
Este directorio se encuentra en un host Linux monitorizado. El agente Checkmk para Linux espera extensiones del agente (plugins de agente) aquí. |