![]() |
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. Introduzione
I plug-in di controllo sono moduli software scritti in Python che vengono eseguiti su un'istanza Checkmk e che creano e valutano i servizi di un host.
Checkmk include più di 2000 plug-in di controllo già pronti per tutti gli hardware e i software possibili. Questi plug-in vengono mantenuti dal team di Checkmk e ne vengono aggiunti di nuovi ogni settimana. Inoltre, su Checkmk Exchange sono presenti altri plug-in che vengono forniti dai nostri utenti.
Tuttavia, può sempre capitare che un dispositivo, un'applicazione o semplicemente una metrica importante per te non sia ancora coperta da nessuno di questi plug-in esistenti, magari semplicemente perché è stata sviluppata all'interno della tua organizzazione e quindi nessun altro potrebbe averla. Nell'articolo sull'introduzione allo sviluppo di estensioni per Checkmk, puoi scoprire quali sono le opzioni a tua disposizione.
Questo articolo ti mostra come sviluppare dei veri e propri plug-in di controllo per l'agente Checkmk, compreso tutto ciò che ne consegue. Esiste un articolo separato per i plug-in di controllo basati su SNMP.
![]() |
Quando sviluppi un plug-in di controllo basato su agent, in genere hai bisogno di due plug-in: il plug-in dell'agente sull'host monitorato, che fornisce i dati, e il plug-in di controllo sul server Checkmk, che valuta questi dati. Entrambi devono essere scritti insieme e ottimizzati l'uno per l'altro. Solo così funzioneranno senza problemi. |
1.1. La documentazione dell'API di Check
Dalla versione di Checkmk 2.0.0, è stata sviluppata una nuova Check API per la programmazione dei plug-in di controllo. Nella versione di Checkmk 2.3.0, la Check API V2 è la versione attuale. In questo articolo ti mostreremo come utilizzare la Check API versione 2 per la programmazione dei plug-in. Puoi trovare informazioni sulla migrazione alla Check API versione 2 alla fine di questo articolo nel capitolo Migrazione.
Puoi accedere alla documentazione di Check API in qualsiasi momento tramite l'interfaccia utente di Checkmk: Help > Developer resources > Plugin API references. Nella nuova finestra del browser, seleziona Agent based ("Check API") > Version 2 nella barra di navigazione a sinistra:

Qui non troverai solo la documentazione della Check API in tutte le versioni supportate, ma anche quella di tutte le altre API rilevanti per la creazione di plug-in di controllo. In questo articolo non utilizzeremo solo la Check API, ma anche la Rulesets API V1 per la creazione di un set di regole e la Graphing API V1 per la creazione di definizioni di metriche.
1.2. Prerequisiti
Se sei interessato a programmare i plug-in di controllo, devi avere i seguenti requisiti:
Conoscenza del linguaggio di programmazione Python.
Esperienza con Checkmk, soprattutto per quanto riguarda agenti e controlli.
Pratica nell'utilizzo di Linux dalla linea di comando.
2. Scrivere un plug-in dell'agente
Se sei interessato a programmare dei plug-in per Checkmk, è molto probabile che tu abbia già configurato un server Checkmk. Se lo hai fatto, probabilmente hai anche monitorato il server Checkmk stesso come host.
In questa sede creeremo uno scenario in cui è utile che il server Checkmk e l'host monitorato siano identici. Questo ci permette di utilizzare le query Livestatus dall'host per ottenere informazioni sui gruppi di host forniti dal server Checkmk.
Nell'esempio descritto, ipotizziamo un'organizzazione con diverse sedi:
Ognuna di queste sedi è rappresentata in Checkmk da un gruppo di host.
Ogni sede ha un proprio team di servizio.
Per garantire che il team di assistenza corretto possa essere avvisato in caso di problemi, ogni host deve essere assegnato a una sede, cioè anche a un gruppo di host. Lo scopo di questo esempio è quello di impostare un controllo per garantire che nessun host abbia dimenticato di assegnare un gruppo di host.
L'intero processo comprende due fasi:
Leggere le informazioni per il monitoraggio dall'host. Questo è l'argomento di questo capitolo.
Scrivere un plug-in di controllo nell'istanza Checkmk che valuti questi dati. Lo mostreremo nel prossimo capitolo.
Quindi, andiamo...
2.1. Recuperare e filtrare le informazioni
Il primo passo da fare prima di scrivere un programma plug-in è la ricerca: ciò significa che bisogna scoprire come ottenere le informazioni necessarie per il monitoraggio.
Per l'esempio scelto, utilizziamo il fatto che il server Checkmk è anche l'host. Questo significa che inizialmente è sufficiente recuperare i dati di stato tramite Livestatus, cioè i dati organizzati in tabelle che Checkmk conserva nella memoria volatile sugli host e i servizi monitorati.
Accedi come utente dell'istanza e interroga le informazioni sui gruppi di host con il seguente 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 prima riga dell'output contiene i nomi delle colonne della tabella hostgroups
interrogata. Il punto e virgola funge da separatore. Le righe successive contengono il contenuto di tutte le colonne, anch'esse separate dal punto e virgola.
L'output è già relativamente confuso in questo piccolo esempio e contiene informazioni che non sono rilevanti per il nostro esempio. In generale, dovresti lasciare l'interpretazione dei dati a Checkmk. Tuttavia, il pre-filtraggio sull'host può ridurre il volume di dati da trasferire se non tutti sono effettivamente necessari. Quindi, in questo caso, limita la query alle colonne rilevanti (Columns
), ai nomi dei gruppi di host (name
) e agli host in questi gruppi (members
):
OMD[mysite]:~$ lq "GET hostgroups\nColumns: name members"
Hamburg;myhost11,myhost22,myhost33
Munich;myhost1,myhost2,myhost3
check_mk;localhost
L'interfaccia Livestatus si aspetta di ricevere tutti i comandi e le intestazioni in una riga separata. Le interruzioni di riga necessarie sono indicate da \n
.
In questo esempio, ci sono attualmente tre gruppi di host: due gruppi per le sedi e uno per il gruppo check_mk
, che contiene un host chiamato localhost
.
Il gruppo di host check_mk
è una caratteristica speciale dei gruppi di host, non è stato creato da te e non puoi aggiungere attivamente un host a questo gruppo. Da dove viene questo gruppo di host?
Poiché per definizione ogni host in Checkmk deve appartenere a un gruppo, Checkmk assegna ogni host che non è stato specificamente assegnato a un gruppo al gruppo "speciale" check_mk
come impostazione predefinita. Non appena avrai assegnato un host a uno dei tuoi gruppi di host, verrà rimosso da Checkmk dal gruppo check_mk
. Non c'è nemmeno modo di riassegnare un host al gruppo di host check_mk
.
Per il nostro esempio utilizziamo esattamente queste proprietà del gruppo check_mk
: poiché ogni host deve essere assegnato a una posizione, il gruppo di host check_mk
deve essere vuoto. Se non è vuoto, è necessario agire, cioè gli host in esso contenuti devono essere assegnati ai gruppi di host e quindi alle posizioni appropriate.
2.2. Incorporare il comando nell'agente
Finora, come utente dell'istanza, hai utilizzato il comando lq
per visualizzare le informazioni, il che è utile per comprendere i dati.
Tuttavia, per recuperare questi dati dal server Checkmk, il nuovo comando deve entrare a far parte dell'agente Checkmk sull'host monitorato. In teoria, potresti modificare direttamente l'agente Checkmk nel file /usr/bin/check_mk_agent
e includere questa parte. Questo metodo, tuttavia, avrebbe lo svantaggio di far scomparire il nuovo comando quando il software dell'agente viene aggiornato perché questo verrà sovrascritto durante l'aggiornamento.
Per questo motivo è meglio creare un plug-in dell'agente: tutto ciò di cui hai bisogno è un file eseguibile che contenga il comando e che si trovi nella directory /usr/lib/check_mk_agent/plugins/
.
Un'altra cosa è importante: i dati non possono essere semplicemente inviati. Avrai bisogno di un'intestazione di sezione. Si tratta di una riga formattata in modo speciale che contiene il nome del nuovo plug-in dell'agente. Questa intestazione di sezione permette a Checkmk di riconoscere in seguito dove iniziano i dati del nuovo plug-in dell'agente e dove finiscono quelli del plug-in precedente. È più facile se il plug-in dell'agente, l'intestazione di sezione e il plug-in di controllo hanno lo stesso nome, anche se questo non è obbligatorio.
Quindi, prima di tutto, dovrai trovare un nome significativo per il tuo nuovo plug-in di controllo. Questo nome può contenere solo lettere minuscole (solo a-z, niente dieresi, niente accenti), trattini bassi e cifre e deve essere unico. Evita i conflitti di nome con i plug-in di controllo esistenti. Se sei curioso di sapere quali nomi esistono già, in un'istanza Checkmk alla riga di comando puoi elencarli 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
L'output qui mostra solo le prime righe del lunghissimo elenco. Grazie all'uso dei prefissi, l'assegnazione di molti plug-in di controllo è già facilmente riconoscibile. L'uso dei prefissi è quindi consigliato anche per i tuoi plug-in di controllo. Tra l'altro, la seconda colonna mostra come il rispettivo plug-in di controllo ottiene i suoi dati.
Un nome adatto per il nuovo plug-in di controllo del nostro esempio è myhostgroups
.
root
Ora hai tutte le informazioni necessarie per creare lo script per il plug-in dell'agente. Crea un nuovo file myhostgroups
come user agent nella directory /usr/lib/check_mk_agent/plugins/
:
#!/bin/bash
columns="name members"
site="mysite"
echo '<<<myhostgroups:sep(59)>>>'
su - ${site} lq "GET hostgroups\nColumns: ${columns}"
Cosa significa in dettaglio?
La prima riga contiene lo "shebang" (abbreviazione di sharp e bang, quest'ultimo abbreviazione del punto esclamativo), grazie al quale Linux riconosce che deve eseguire lo script con la shell specificata.
Per rendere lo script adattabile, vengono introdotte due variabili:
la variabile
columns
, che attualmente contiene i nomi dei gruppi e i membri associati,la variabile
site
, che contiene il nome dell'istanza Checkmk.
Utilizza il comando echo
per produrre l'header sezioni. Poiché le colonne della tabella sono separate da un punto e virgola, utilizza l'aggiunta sep(59)
per specificare che il punto e virgola viene utilizzato come separatore per i dati nell'output dell'agente. 59 sta per il codice ASCII 59, il punto e virgola. Senza questa aggiunta, il carattere spazio (carattere ASCII 32) verrebbe utilizzato come separatore per impostazione predefinita.
Per poter utilizzare il comando lq
, che è a tua disposizione come utente dell'istanza, in uno script eseguito dall'utente root
, devi anteporre il prefisso su
.
![]() |
È possibile che l'accesso a |
Una volta creato il file, è importante renderlo eseguibile:
root@linux# chmod +x /usr/lib/check_mk_agent/plugins/myhostgroups
Puoi provare il plug-in dell'agente direttamente a mano inserendo il percorso completo come comando:
root@linux# /usr/lib/check_mk_agent/plugins/myhostgroups
<<<myhostgroups:sep(59)>>>
Hamburg;myhost11,myhost22,myhost33
Munich;myhost1,myhost2,myhost3
check_mk;localhost
I gruppi di host che non contengono host non vengono elencati.
2.3. Testare l'agente
I test e la risoluzione dei problemi sono le attività più importanti quando si crea un plug-in dell'agente funzionante. È meglio procedere in tre fasi:
Prova il plug-in dell'agente "standalone", come hai appena fatto nella sezione precedente.
Prova l'agente nel suo complesso a livello locale.
Recuperare l'agente Checkmk dal server Checkmk.
Testare l'agente localmente è molto semplice. Come root
, chiama il comando check_mk_agent
:
root@linux# check_mk_agent
La nuova sezione deve apparire da qualche parte nel lunghissimo output. I plug-in dell'agente vengono emessi dall'agente alla fine del processo.
Puoi scorrere l'output aggiungendo less
(premi la barra spaziatrice per scorrere, /
per cercare e q
per uscire):
root@linux# check_mk_agent | less
In alternativa, puoi cercare le righe interessanti nell'output. Ad esempio, grep
con -A
ha un'opzione che consente di emettere alcune righe in più dopo ogni risposta. Questo ti permette di cercare ed emettere comodamente la sezione:
root@linux# check_mk_agent | grep -A3 '^<<<myhostgroups'
<<<myhostgroups:sep(59)>>>
Hamburg;myhost11,myhost22,myhost33
Munich;myhost1,myhost2,myhost3
check_mk;localhost
Il terzo e ultimo test viene effettuato direttamente dal sito Checkmk. Includi l'host nel monitoraggio (ad esempio come localhost
), effettua il login come user dell'istanza e recupera i dati dell'agente con cmk -d
:
OMD[mysite]:~$ cmk -d localhost | grep -A3 '^<<<myhostgroups'
Questo dovrebbe produrre lo stesso risultato del comando precedente.
Se funziona, il tuo agente è pronto. Hai creato un breve script nel percorso /usr/lib/check_mk_agent/plugins/myhostgroups
e lo hai reso eseguibile.
Ora tutto ciò che segue avviene solo sul server Checkmk: lì scrivi il plug-in di controllo.
3. Scrivere un semplice plug-in di controllo
La preparazione dell'agente Checkmk è solo metà del divertimento. Ora devi insegnare a Checkmk come gestire le informazioni provenienti dalla nuova sezione dell'agente, quali servizi deve generare, quando devono andare in WARN o CRIT, ecc.
3.1. Preparazione del file
Per i tuoi plug-in di controllo troverai la directory di base già pronta nella gerarchia local
della directory del sito. Si tratta di ~/local/lib/python3/cmk_addons/plugins/
. La directory appartiene all'utente dell'istanza e quindi è scrivibile per te.
In questa directory, i plug-in sono organizzati in famiglie di plug-in, i cui nomi del plug-in possono essere definiti liberamente. Ad esempio, tutti i plug-in relativi ai dispositivi Cisco sono archiviati nella cartella cisco
, mentre tutti i plug-in relativi ai gruppi di host sono archiviati nella cartella myhostgroups
. Questa convenzione permette a tutti i plug-in appartenenti alla stessa famiglia di condividere il codice ed è utilizzata esattamente nello stesso modo dai plug-in forniti con Checkmk.
In questa sottocartella <plug-in_family>
, vengono poi create altre sottocartelle con nomi predefiniti a seconda delle necessità per le varie API, ad esempio agent_based
per l'API Check dei plug-in dell'agente di controllo. A sua volta, in seguito introdurremo altre sottocartelle per l'API Rulesets e l'API Graphing.
Crea le due sottodirectory per il nuovo plug-in dell'agente di controllo e poi passa ad esse per lavorare:
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
Puoi modificare il plug-in di controllo con qualsiasi editor di testo installato sul sistema Linux.
Crea qui il file myhostgroups.py
per il plug-in di controllo. La convenzione vuole che il nome del file rifletta il nome della sezione dell'agente Checkmk. È obbligatorio che il file finisca con .py
, perché dalla versione Checkmk 2.0.0 i plug-in di controllo sono sempre dei veri e propri moduli Python.
Un framework di base eseguibile(scaricabile da GitHub), che verrà ampliato passo dopo passo nel seguito, ha il seguente aspetto:
#!/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,
)
Per prima cosa, è necessario importare le funzioni e le classi necessarie per i plug-in di controllo dai moduli Python. Per il nostro esempio, importeremo solo ciò che sarà necessario o potrà essere utile nel resto dell'articolo. In questo caso, cmk.agent_based.v2
è utilizzato per specificare che i moduli della Check API V2 sono importati:
from cmk.agent_based.v2 import AgentSection, CheckPlugin, Service, Result, State, Metric, check_levels
3.2. Scrivere la funzione parse
La funzione parse ha il compito di "analizzare" i dati "grezzi" dell'agente, cioè di analizzarli e suddividerli, e di inserirli in un modulo logicamente strutturato che sia facile da processare per tutti i passaggi successivi.
Come mostrato nella sezione dedicata al test dell'agente, la sezione fornita dal plug-in dell'agente ha la seguente struttura:
<<<myhostgroups:sep(59)>>>
Hamburg;myhost11,myhost22,myhost33
Munich;myhost1,myhost2,myhost3
check_mk;localhost
Checkmk divide già le righe della sezione fornita dal plug-in dell'agente in un elenco di righe in base al separatore presente nell'intestazione della sezione (nell'esempio ;
); queste righe sono a loro volta elenchi di parole. La seguente struttura di dati è quindi disponibile in Checkmk al posto dei dati grezzi del plug-in dell'agente:
[
['Hamburg', 'myhost11,myhost22,myhost33'],
['Munich', 'myhost1,myhost2,myhost3'],
['check_mk', 'localhost']
]
Nell'elenco interno, il primo elemento contiene il nome del gruppo di host e il secondo i nomi degli host appartenenti al gruppo.
È possibile indirizzare tutte queste informazioni, ma solo attraverso la loro posizione nel set di dati. Pertanto, dovrai sempre specificare il numero di parentesi quadre e il numero di "sequenza" del contenuto desiderato all'interno di ogni parentesi. Con volumi di dati più grandi, questo diventa sempre più complesso e diventa sempre più difficile mantenere una panoramica.
A questo punto, una funzione di parsing offre chiari vantaggi grazie alla struttura che crea: rende il codice più facile da leggere, gli accessi sono più performanti ed è molto più semplice mantenere una panoramica. Trasforma la struttura dei dati fornita da Checkmk in modo tale da poter indirizzare a piacimento ogni singolo valore per nome (o chiave), senza dover cercare ripetutamente nell'array per trovare quello che stai cercando:
{
'Hamburg': {'members': 'myhost11,myhost22,myhost33'},
'Munich': {'members': 'myhost1,myhost2,myhost3'},
'check_mk': {'members': 'localhost'}
}
La convenzione prevede che la funzione di parsing prenda il nome della sezione agente e inizi con parse_
. Riceve string_table
come unico argomento. Nota che non sei libero di scegliere l'argomento: deve essere chiamata esattamente così.
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
specifichi in Python che una funzione deve essere definita di seguito.parsed = {}
crea il dizionario con la struttura dei dati migliorata. Nel nostro esempio, esamineremo ogni riga, elemento per elemento. Il gruppo di host seguito dai membri del gruppo di host viene preso da ogni riga e assemblato in una voce per il dizionario.
Il dizionario viene poi restituito con return parsed
.
![]() |
Nell'esempio mostrato sopra, troverai due righe commentate. Se le commenti in un secondo momento durante il test del plug-in di controllo, i dati prima e dopo l'esecuzione della funzione di parsing verranno visualizzati sulla linea di comando. In questo modo potrai verificare se la funzione fa davvero quello che dovrebbe fare. |
3.3. Creare la sezione agente
Affinché l'intera procedura abbia effetto, devi creare la nuova sezione agente con la nuova funzione di parsing. Solo così sarà riconosciuta e presa in considerazione da Checkmk. Per farlo, crea la sezione agente come istanza della classe AgentSection
:
agent_section_myhostgroups = AgentSection(
name = "myhostgroups",
parse_function = parse_myhostgroups,
)
È importante che il nome della sezione corrisponda esattamente all'header sezioni nell'output dell'agente. Da questo momento in poi, ogni plug-in di controllo che utilizza la sezione myhostgroups
riceve il valore restituito dalla funzione di parse. Di regola, si tratta dell'omonimo plug-in di controllo, ma anche altri plug-in di controllo possono sottoscrivere questa sezione, come mostreremo nell'estensione del plug-in di controllo.
A proposito: Se vuoi saperlo con precisione, puoi dare un'occhiata alla documentazione dell'API Check, dove troverai una descrizione dettagliata di questa classe e anche delle classi, delle funzioni e degli oggetti che verranno utilizzati più avanti in questo articolo.

3.4. Creare un plug-in di controllo
Affinché Checkmk riconosca l'esistenza di un nuovo plug-in di controllo, è necessario crearlo. Ciò avviene creando un'istanza della classe CheckPlugin
.
Devi sempre specificare almeno quattro cose:
name
: Il nome del plug-in di controllo. Il modo più semplice per farlo è utilizzare lo stesso nome della sezione del nuovo agente. In questo modo, il controllo definito successivamente nella funzione di controllo sa automaticamente quale sezione deve valutare.service_name
Il nome del servizio così come dovrebbe apparire nel monitoraggio.discovery_function
La funzione per scoprire i servizi di questo tipo (ne parleremo tra poco).check_function
La funzione che esegue il controllo vero e proprio (per saperne di più, leggi qui di seguito).
Il nome dell'istanza deve iniziare con check_plugin_
. L'aspetto è quindi il seguente:
check_plugin_myhostgroups = CheckPlugin(
name = "myhostgroups",
service_name = "Host group check_mk",
discovery_function = discover_myhostgroups,
check_function = check_myhostgroups,
)
È meglio non provarlo ancora, perché è necessario scrivere le funzioni discover_myhostgroups
e check_myhostgroups
. Queste devono comparire nel codice sorgente prima della creazione della sezione agente e del plug-in dell'agente come descritto sopra.
![]() |
Se viene utilizzato il core di Nagios (sempre nel Checkmk Raw), i seguenti caratteri speciali non sono ammessi nel nome del servizio: |
3.5. Scrittura della funzione di ricerca
Una caratteristica speciale di Checkmk è la scoperta automatica dei servizi da monitorare. Affinché questo funzioni, ogni plug-in di controllo deve definire una funzione che utilizza l'output dell'agente per riconoscere se un servizio di questo tipo o quali servizi di questo tipo devono essere creati per l'host in questione.
La funzione di scoperta viene sempre chiamata quando viene effettuata una scoperta del servizio per un host e decide se e quali servizi devono essere creati. Nel caso standard, riceve esattamente un argomento con il nome section
che contiene i dati della sezione dell'agente in un formato preparato dalla funzione parse.
Pertanto, implementa la seguente semplice logica:se la sezione dell'agente myhostgroups
esiste, crea anche un servizio adatto, che apparirà automaticamente su tutti gli host su cui è stato distribuito il plug-in dell'agente.
Per i plug-in di controllo che creano solo un servizio per host, non sono necessarie altre informazioni:
def discover_myhostgroups(section):
yield Service()
La funzione di scoperta deve restituire un oggetto del tipo service
per ogni servizio da creare utilizzando yield
(non con return
). In Python, yield
ha la stessa funzione di return
- entrambi restituiscono un valore alla funzione chiamante. La differenza decisiva è che yield
ricorda a che punto è la funzione nell'elaborazione dei dati.
La chiamata successiva continua dopo l' ultima istruzione di yield
e non ricomincia dall'inizio. Ciò significa che non viene letto solo il primo risultato (come nel caso di return
), ma tutti i risultati in sequenza (questo vantaggio diventerà rilevante più avanti nel nostro esempio di scoperta del servizio).
3.6. Scrivere la funzione di controllo
Ora puoi passare alla funzione di controllo vera e propria, che utilizza l'output dell'agente corrente per decidere quale stato deve assumere il servizio e può fornire ulteriori informazioni.
L'obiettivo della funzione di controllo è quello di impostare un controllo che possa essere utilizzato per verificare se è stato assegnato un gruppo di host a un qualsiasi host. A tal fine, controlla se il gruppo di host di check_mk
contiene host. In questo caso, il servizio dovrebbe ricevere lo stato CRIT. In caso contrario, tutto è OK e anche lo stato del servizio.
Ecco l'implementazione:
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")
E ora la spiegazione: la funzione check_myhostgroups()
recupera innanzitutto il valore appartenente alla chiave check_mk
nella variabile attr
. Poi la variabile hosts
viene collegata al valore members
se esiste. Se non ci sono members
, hosts
rimane vuota.
Segue un'interrogazione di if
per la valutazione vera e propria:
Se la variabile
hosts
ha un contenuto, cioè il gruppo di hostcheck_mk
non è vuoto, lo stato del servizio passa a CRIT e viene emesso un testo di avviso. Questo testo contiene anche un elenco dei nomi host di tutti gli host che fanno parte del gruppo di hostcheck_mk
. Per emettere il testo con le espressioni viene utilizzata la F-String di Python, così chiamata perché la stringa è preceduta dalla letteraf
.Se la variabile
hosts
è vuota, cioè non ci sono host nel gruppo di hostcheck_mk
, lo stato del servizio cambia in OK. In questo caso, viene emesso anche un messaggio appropriato.
Una volta creata la funzione di controllo, il plug-in di controllo è pronto.
Il plug-in di controlloe il plug-in dell'agentesono stati resi disponibili su GitHub.
3.7. Test e attivazione del plug-in di controllo
Il test e l'attivazione vengono eseguiti alla riga di comando con il comando cmk
.
Per prima cosa prova la scoperta del servizio con l'opzione -I
. Aggiungendo l'opzione v
(per verbose), viene richiesto un output dettagliato. --detect-plugins
limita l'esecuzione del comando a questo plug-in di controllo e, tramite localhost
, a questo 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
Come previsto, la scoperta del servizio riconosce un nuovo servizio nel plug-in di controllo myhostgroups
.
Ora puoi provare il controllo contenuto nel plug-in di controllo:
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
Eseguendo il controllo, verrà determinato lo stato del servizio trovato in precedenza.
Se tutto è andato come previsto, puoi attivare le modifiche; in caso contrario, troverai informazioni utili nel capitolo sulla risoluzione dei problemi.
Infine, attiva le modifiche riavviando il nucleo di monitoraggio:
OMD[mysite]:~$ cmk -R
Generating configuration for core (type nagios)...
Precompiling host checks...OK
Validating Nagios configuration...OK
Restarting monitoring core...OK
Nel monitoraggio di Checkmk, ora troverai il nuovo servizio Host group check_mk presso l'host localhost
:

check_mk
non è vuoto, il servizio è CRIT.Congratulazioni per aver creato con successo il tuo primo plug-in di controllo!
4. Estendere il plug-in di controllo
4.1. Lavoro preparatorio
Il primo plug-in di controllo, completato di recente, deve essere esteso passo dopo passo. Finora, il plug-in dell'agente ha fornito solo informazioni sui nomi e sui membri dei gruppi di host. Per poter valutare lo stato dell'host e dei servizi in esecuzione su di esso, ad esempio, sono necessari altri dati.
Estendere il plug-in dell'agente
Per prima cosa estenderai il plug-in dell'agente una volta per raccogliere tutte le informazioni che ti serviranno per estendere il plug-in di controllo nelle sezioni successive.
Per scoprire quali informazioni Checkmk fornisce per i gruppi di host, puoi interrogare tutte le colonne disponibili della tabella dei gruppi di host con il seguente comando come utente dell'istanza:
OMD[mysite]:~$ lq "GET columns\nFilter: table = hostgroups\nColumns: name"
action_url
alias
members
members_with_state
name
notes
notes_url
num_hosts
...
L'output è ancora più completo: la tabella ha quasi 30 colonne e la maggior parte di esse ha anche nomi significativi. Le colonne di interesse sono le seguenti: numero di host per gruppo (colonna num_hosts
), numero di host in stato UP (num_hosts_up
), numero di servizi di tutti i gruppi di host (num_services
) e numero di servizi in stato OK (num_services_ok
).
Ora queste nuove colonne devono essere fornite solo dall'agente. Puoi ottenere questo risultato estendendo il plug-in dell'agente creato nel capitolo precedente.
Come utente root, modifica lo script del plug-in dell'agente. Poiché lo script ha già inserito i valori configurabili nelle variabili, è sufficiente modificare la riga che inizia con columns
e inserire le quattro colonne aggiuntive recuperate:
#!/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}"
Esegui lo script per verificarlo:
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
I quattro nuovi valori, separati da un punto e virgola, appaiono ora alla fine di ogni riga.
Con questa modifica, il plug-in dell'agente fornisce ora dati diversi da quelli precedenti. A questo punto, è importante assicurarsi che con i dati modificati, il plug-in di controllo continui a fare ciò che deve fare.
Estendere la funzione di parsing
In un plug-in di controllo, la funzione di parse è responsabile della conversione dei dati forniti dal plug-in dell'agente. Scrivendo la funzione di parse, hai preso in considerazione solo due colonne della tabella del gruppo di host. Ora vengono fornite sei colonne invece di due. La funzione di parse deve quindi essere personalizzata per processare le quattro colonne aggiuntive.
Come utente dell'istanza, modifica la funzione di analisi nel file myhostgroups.py
, che contiene il plug-in di controllo:
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
Tutto ciò che si trova tra parsed = {}
e return parsed
è stato modificato. Innanzitutto, le colonne da processare sono definite con i loro nomi come elenco column_names
. Un dizionario viene poi creato nel ciclo for
generando le coppie chiave-valore in ogni riga a partire dal nome della colonna e dal valore letto.
Questa estensione non è critica per la funzione di controllo esistente, in quanto la struttura dei dati delle prime due colonne rimane invariata. Vengono solo fornite colonne aggiuntive, che non vengono (ancora) valutate nella funzione di controllo.
Ora che i nuovi dati possono essere processati, li utilizzerai anche tu.
4.2. La scoperta del servizio
Nel capitolo precedente hai realizzato un controllo molto semplice che crea un servizio su un host. Tuttavia, è molto comune che ci siano più servizi da un singolo controllo su un host.
L'esempio più comune è quello di un servizio per un file system su un host. Il plug-in di controllo con il nome df
crea un servizio per ogni file system sull'host. Per distinguere questi servizi, il mount point del file system (ad esempio /var
) o la lettera dell'unità (ad esempio C:
) viene incorporato nel nome del servizio. Questo si traduce in un nome del servizio come Filesystem /var
o Filesystem C:
. La parola /var
o C:
viene qui indicata come un elemento. Stiamo quindi parlando anche di un controllo con elementi.
Se vuoi creare un controllo con elementi, devi implementare le seguenti funzioni:
La funzione di scoperta deve generare un servizio per ogni elemento che deve essere monitorato sull'host.
Devi includere l'elemento nel nome del servizio utilizzando il segnaposto
%s
(es."Filesystem %s"
).La funzione di controllo viene richiamata una volta, separatamente per ogni elemento, e riceve questo come argomento. Dai dati dell'agente deve poi estrarre i dati rilevanti per questo elemento.
Per testare questa funzione nella pratica, dovrai creare un servizio separato per ogni gruppo di servizi esistente.
Dato che il plug-in di controllo myhostgroups
creato nel capitolo precedente per controllare il gruppo standard check_mk
dovrebbe continuare a funzionare, questo plug-in di controllo rimane così com'è. Per l'estensione, crea il nuovo plug-in di controllo myhostgroups_advanced
nel file esistente myhostgroups.py
.- Nel primo passo, come in precedenza, crea un'istanza della classe CheckPlugin
.
Qui puoi trovare il vecchio codice e il nuovo codice evidenziato:
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,
)
Affinché il nuovo plug-in di controllo possa essere distinto dal vecchio, gli viene assegnato un nome univoco con myhostgroups_advanced
. Il parametro sections
determina le sezioni dell'output dell'agente a cui il plug-in di controllo si iscrive. In questo caso, myhostgroups
viene utilizzato per specificare che il nuovo plug-in di controllo utilizza gli stessi dati del vecchio: la sezione del plug-in dell'agente preparata dalla funzione di parsing.
Il nome del servizio contiene ora il segnaposto %s
. Il nome dell'elemento viene inserito in seguito da Checkmk. Nelle ultime due righe, vengono definiti i nomi della nuova funzione di discovery e della nuova funzione di check, entrambe ancora da scrivere.
Prima la funzione di ricerca, che ora ha il compito di determinare gli elementi da monitorare: anche questa viene inserita in aggiunta a quella esistente:
def discover_myhostgroups_advanced(section):
for group in section:
if group != "check_mk":
yield Service(item=group)
Come in precedenza, la funzione di ricerca riceve l'argomento section
. I singoli gruppi di host vengono esaminati in un ciclo. Tutti i gruppi di host sono di interesse in questo caso, ad eccezione di check_mk
, poiché questo gruppo di host speciale è già stato preso in considerazione dal plug-in di controllo esistente myhostgroups
. Ogni volta che viene trovato un elemento, questo viene restituito con yield
, che crea un oggetto del tipo Service
, che a sua volta riceve il nome del gruppo di host come elemento.
Se l'host viene monitorato in seguito, la funzione di controllo viene richiamata separatamente per ogni servizio e quindi per ogni elemento. Questo ti porta alla definizione della funzione di controllo per il nuovo plug-in di controllo myhostgroups_advanced
. La funzione di controllo riceve l'argomento item
oltre alla sezione. La prima riga della funzione si presenta quindi come segue:
def check_myhostgroups_advanced(item, section):
L'algoritmo della funzione di controllo è semplice: se il gruppo di host esiste, il servizio viene impostato su OK e vengono elencati il numero e i nomi degli host del gruppo. La funzione completa è questa:
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']}")
Il risultato del controllo viene fornito restituendo un oggetto della classe Result
tramite yield
. Questo richiede i parametri state
e summary
. Qui, state
definisce lo stato del servizio (nell'esempio OK
) e summary
il testo che viene visualizzato nella sezione Summary del servizio. Questi dati sono puramente informativi e non vengono valutati ulteriormente da Checkmk. Puoi trovare maggiori informazioni a riguardo nella prossima sezione.
Fin qui tutto bene, ma cosa succede se l'elemento che stai cercando non viene trovato? Questo può accadere se in passato è già stato creato un servizio per un gruppo di host, ma ora questo gruppo di host è scomparso: o perché il gruppo di host esiste ancora in Checkmk ma non contiene più un host, o perché è stato completamente cancellato. In entrambi i casi, questo gruppo di host non sarà più presente nell'output dell'agente.
Se un elemento ricercato non viene trovato, Checkmk genera automaticamente il risultato UNKNOWN - Item not found in monitoring data
per il servizio. Questo è intenzionale e positivo: se un elemento ricercato non viene trovato, puoi semplicemente eseguire Python dalla funzione e lasciare che Checkmk faccia il suo lavoro.
Checkmk sa solo che l'elemento che c'era prima ora non c'è più. Checkmk non ne conosce il motivo, ma tu sì. È quindi opportuno non tenere per sé questa conoscenza e intercettare questa condizione nella funzione check e visualizzare un messaggio utile.
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']}")
Cosa è cambiato? La condizione di errore viene ora gestita per prima. Pertanto, controlla nel ramo if
se l'elemento non esiste davvero, imposta lo stato a CRIT ed esci dalla funzione con return
. In tutti gli altri casi, restituisci OK come prima.
Questo significa che hai adottato la situazione della scomparsa dei gruppi di servizi nella funzione di controllo: invece di SCONOSCIUTO, il servizio associato sarà ora CRIT e conterrà informazioni sulla causa dello stato critico.
Il plug-in dell'agenteesteso e il file esteso per i plug-in di controllosi trovano ancora una volta su GitHub. Quest'ultimo contiene il semplice plug-in di controllo myhostgroups
del capitolo precedente, la funzione di parse avanzata e i componenti del nuovo plug-in di controllo myhostgroups_advanced
con la creazione del plug-in di controllo, la funzione di ricerca e la funzione di controllo. Nota che le funzioni devono essere sempre definite prima della creazione dei plug-in di controllo o delle sezioni dell'agente, in modo che non si verifichino errori dovuti a nomi di funzioni non definiti.
Poiché il nuovo plug-in di controllo myhostgroups_advanced
fornisce nuovi servizi, devi eseguire una scoperta del servizio per questo plug-in di controllo e attivare le modifiche per poter vedere questi servizi nel monitoraggio:

Procedi come descritto nel capitolo sui plug-in di controllo semplici. Non eseguire i primi due comandi per myhostgroups
, ma eseguili per il nuovo plug-in di controllo myhostgroups_advanced
.
4.3. Riepilogo e dettagli
Nello stato di monitoraggio di Checkmk, ogni servizio ha uno stato -OK, WARN, ecc. - e una riga di testo. Questo testo si trova nella colonna Summary - come si può vedere nella schermata precedente - e ha il compito di fornire un breve riassunto dello stato dell'attività del servizio. Il concetto è che questo testo non deve superare la lunghezza di 60 caratteri. Questo garantisce una visualizzazione concisa della tabella senza fastidiose interruzioni di riga.
Esiste anche il campo Details, in cui vengono visualizzati tutti i dettagli sullo stato del servizio, che include anche tutte le informazioni di riepilogo. Cliccando sul servizio si apre la pagina del servizio, in cui si possono vedere i due campi Summary e Details e molti altri.
Quando chiami yield Result(...)
, puoi stabilire quali informazioni sono così importanti da dover essere visualizzate nel riepilogo e per quali è sufficiente che appaiano nei dettagli.
Nel nostro esempio, hai sempre utilizzato una chiamata del tipo seguente:
yield Result(state=State.OK, summary=f"{attr['num_hosts']} hosts in this group: {attr['members']}")
Ciò significa che il testo definito con summary
appare sempre nel riquadro Summary- e anche nel riquadro Details. Dovresti quindi utilizzarlo solo per le informazioni importanti. Se un gruppo di host contiene molti host, l'elenco riassuntivo può diventare molto lungo, più dei 60 caratteri raccomandati. Se un'informazione è di secondaria importanza, puoi utilizzare details
per specificare che il suo testo appare solo nei dettagli:
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']}",
)
Nell'esempio precedente, l'elenco degli host viene quindi visualizzato solo nel sito Details. Il sito Summary mostra quindi solo il numero di host del gruppo:

Oltre a summary
e details
, c'è un terzo parametro. Con notice
puoi specificare che un testo per un servizio in OK viene visualizzato solo nei dettagli, ma anche nel riepilogo per tutti gli altri stati. In questo modo è immediatamente chiaro dal riepilogo perché il servizio non è OK. Il parametro notice
non è particolarmente utile se i testi sono permanentemente legati agli stati, come nel nostro esempio.
In sintesi, questo significa che:
Il testo totale del riepilogo non deve essere più lungo di 60 caratteri per i servizi OK.
Utilizza sempre
summary
onotice
- almeno uno o l'altro, ma non entrambi.Se necessario, aggiungi
details
se il testo dei dettagli deve essere alternativo.
4.4. Risultati parziali multipli per servizio
Per evitare che il numero di servizi su un host aumenti eccessivamente, spesso si combinano diversi risultati parziali in un unico servizio. Ad esempio, il servizio Memory di Linux controlla non solo l'utilizzo della RAM e dello swap, ma anche della memoria condivisa, delle tabelle di pagine e di ogni altra cosa.
L'API Check fornisce un'interfaccia molto comoda per questo. Una funzione di controllo può semplicemente generare un risultato con yield
tutte le volte che è necessario. Lo stato generale del servizio si basa quindi sul peggior risultato parziale nell'ordine OK → WARN → SCONOSCIUTO → CRIT.
Nell'esempio, utilizza questa opzione per definire due risultati aggiuntivi per ogni servizio dei gruppi di servizi host oltre al risultato esistente. Questi valutano la percentuale di host nello stato UP e i servizi nello stato OK. Utilizzi le colonne aggiuntive della tabella dei gruppi di host precedentemente definite nell'output dell'agente e nella funzione di parsing.
Ora espandi la funzione di controllo per gradi dall'alto verso il basso:
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"])
Il ramo if
rimane invariato, cioè i nuovi risultati parziali si applicano solo ai gruppi di host che esistono. Definisci quindi cinque variabili per le colonne della tabella dei gruppi di host contenuta nella sezione. Da un lato, questo aumenta la leggibilità e, dall'altro, puoi convertire le stringhe lette in numeri con int()
per le quattro colonne che devono ancora essere utilizzate per il calcolo.
L'unico risultato esistente rimane (quasi) invariato:
yield Result(
state = State.OK,
summary = f"{num_hosts} hosts in this group",
details = f"{num_hosts} hosts in this group: {members}",
)
Solo l'accesso nella "F-String" di Python all'espressione che restituisce il valore è ora più semplice che in precedenza, poiché attr
è già presente nelle definizioni delle variabili.
Passiamo ora al vero e proprio core dell'estensione, la definizione di un risultato che implementa la seguente affermazione: "Il servizio del gruppo di host è WARN quando il 90% degli host è UP e CRIT quando l'80% degli host è UP". La convenzione è che il controllo passa a WARN o CRIT non appena viene raggiunta la soglia e non solo quando viene superata. L'API Check fornisce la funzione ausiliaria check_levels
per confrontare un valore determinato con i valori soglia.
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,
)
Nella prima riga, la percentuale viene calcolata dal numero totale e dal numero di host nello stato UP e viene memorizzata nella variabile hosts_up_perc
. La barra singola (/
) esegue una divisione in virgola mobile, assicurando che il risultato sia un valore float. Questo è utile perché alcune delle funzioni utilizzate in seguito prevedono un valore float come input.
Nella seconda riga, il risultato della funzione check_levels
viene restituito come oggetto del tipo Result
, che può essere trovato nella documentazione dell'API. Questa funzione viene alimentata con la percentuale appena calcolata come valore (hosts_up_perc
), i due valori di soglia inferiori (levels_lower
), un'etichetta che precede l'uscita (label
) e infine con notice_only=True
.
Quest'ultimo parametro utilizza il parametro notice
già introdotto nella sezione precedente per l'oggetto Result()
. Con notice_only=True
specifichi che il testo del servizio viene visualizzato in Summary solo se lo stato non è OK. Tuttavia, i risultati parziali che portano a un WARN o a un CRIT saranno sempre visibili nel riepilogo, indipendentemente dal valore di notice_only
.
Infine, definisci il terzo risultato nello stesso modo del secondo, che valuta la percentuale di servizi in stato 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,
)
Questo completa la funzione di controllo.
Il servizio per un gruppo di host ora valuta tre risultati e visualizza lo stato peggiore di questi nel monitoraggio, come nell'esempio seguente:

4.5. Metriche
Non sempre, ma spesso i controlli hanno a che fare con i numeri e questi numeri sono spesso valori misurati o calcolati. Nel nostro esempio, il numero di host nel gruppo di host (num_hosts
) e il numero di host nello stato UP (num_hosts_up
) sono i valori misurati. La percentuale di host nello stato UP (hosts_up_perc
) è un valore calcolato a partire da questi valori. Se questo valore può essere visualizzato in un periodo di tempo, si parla anche di metrica.
Con il suo sistema integrato di grafici, Checkmk dispone di un componente per la memorizzazione, la valutazione e la visualizzazione di tali valori, completamente indipendente dal calcolo degli stati OK, WARN e CRIT.
In questo esempio, definirai i due valori calcolati, hosts_up_perc
e services_ok_perc
, come metriche. Le metriche saranno immediatamente visibili nell'interfaccia utente di Checkmk senza che tu debba fare nulla. Per ogni metrica viene generato automaticamente un grafico.
Le metriche vengono determinate dalla funzione di controllo e restituite come risultato aggiuntivo. Il modo più semplice è quello di aggiungere le informazioni sulle metriche alla funzione check_levels()
durante la chiamata.
Come promemoria, le righe con la chiamata alla funzione check_levels()
della sezione precedente sono riportate qui:
yield from check_levels(
hosts_up_perc,
levels_lower = ("fixed", (90.0, 80.0)),
label = "UP hosts",
notice_only = True,
)
I due nuovi argomenti per la metrica sono metric_name
e 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,
)
Per mantenere le cose semplici e significative, per il nome della metrica usa il nome della variabile in cui la percentuale è memorizzata come valore.
Puoi utilizzare boundaries
per fornire al sistema grafico informazioni sull'intervallo di valori possibili. Questo si riferisce al valore più piccolo e più grande possibile. Nel caso di una percentuale, i limiti di 0.0
e 100.0
non sono troppo difficili da determinare. Sono consentiti sia i numeri in virgola mobile che i numeri interi (che vengono convertiti internamente in numeri in virgola mobile), ma non le stringhe. Se viene definito solo un limite dell'intervallo di valori, è sufficiente inserire None
per l'altro, ad esempio boundaries = (0.0, None)
.
Con questa estensione, la funzione check_levels
ora restituisce anche un oggetto del tipo Metric
tramite yield
oltre a Result
.
Ora puoi definire la metrica services_ok_perc
nello stesso modo. Le ultime righe della funzione di controllo saranno quindi come queste:
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 funzione di controllo estesa, entrambi i grafici sono visibili nel monitoraggio. Nell'elenco dei servizi, l'icona mostra ora che ci sono dei grafici per il servizio. Se punti l'icona con il mouse, i grafici verranno visualizzati in anteprima.

Una panoramica di tutti i grafici, con le relative legende e altro ancora, si trova nei dettagli del servizio.
Ma cosa fare se il valore della metrica desiderata non è stato definito con la funzione check_levels()
? Naturalmente puoi definire una metrica indipendentemente da una chiamata di funzione. A questo scopo viene utilizzato l'oggetto Metric()
, che puoi anche creare direttamente tramite il suo costruttore. La definizione alternativa di una metrica per il valore hosts_up_perc
si presenta così:
yield Metric(
name = "hosts_up_perc",
value = hosts_up_perc,
levels = (80.0, 90.0),
boundaries = (0.0, 100.0),
)
Gli argomenti di Metric()
sono molto simili a quelli della chiamata di funzione mostrata sopra: sono obbligatori i primi due argomenti per il nome della metrica e il valore. Inoltre, ci sono due argomenti opzionali: levels
per i valori di threshold WARN e CRIT e boundaries
per l'intervallo di valori.
![]() |
Le specifiche di |
Ora usa l'opzione di definire non solo i due valori calcolati, ma anche tutti i valori misurati come metriche usando Metric()
- nel nostro esempio, i quattro valori misurati dalla tabella del gruppo di host. Limitati alle due specifiche obbligatorie di nome e valore della metrica. Le quattro nuove righe completano l'estensione della funzione di controllo delle metriche:
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)
Questo aumenta il numero di grafici per servizio, ma ti dà anche la possibilità di combinare più metriche in un unico grafico, ad esempio. Mostriamo queste e altre opzioni nella sezione Personalizzare la visualizzazione delle metriche.
Nel file di esempio suGitHubpuoi ritrovare l'intera funzione di controllo.
5. Set di regole per i parametri del check
Nel plug-in di controllo esteso di myhostgroups_advanced
avrai generato lo stato WARN se solo il 90% degli host è UP e CRIT per l'80%. In questo caso, i numeri 90
e 80
sono definiti esplicitamente nella funzione di controllo o, come direbbero i programmatori, hard-coded. In Checkmk, tuttavia, gli utenti sono abituati a poter configurare tali valori soglia e altri parametri del check tramite regole.
Ad esempio, se un gruppo di host ha solo quattro membri, i due valori di threshold del 90% e dell'80% non si adattano bene, poiché la percentuale scenderà al 75% non appena il primo host si guasterà e lo stato passerà direttamente a CRIT, senza uno stato WARN intermedio.
Per questo motivo, il plug-in di controllo deve essere modificato in modo da poter essere configurato tramite l'interfaccia Setup. Per farlo, ti servirà un set di regole.
Per creare un set di regole per un plug-in di controllo devi uscire dallo sviluppo del plug-in di controllo e switchare la directory, il file e l'API. Da Checkmk 2.3.0 l'API Rulesets ti supporta nella creazione di questi set di regole per i plug-in di controllo. La documentazione dell'API per i set di regole si trova nella tua istanza Checkmk nella stessa pagina dell'API Check, sotto Rulesets > Version 1.
5.1. Definire un nuovo set di regole
Per creare un nuovo set di regole, crea innanzitutto una nuova sottodirectory nella directory della famiglia del plug-in ~/local/lib/python3/cmk_addons/plugins/myhostgroups/
. Il nome rulesets
è predefinito per questa sottodirectory:
OMD[mysite]:~$ mkdir -p local/lib/python3/cmk_addons/plugins/myhostgroups/rulesets
OMD[mysite]:~$ cd local/lib/python3/cmk_addons/plugins/myhostgroups/rulesets
In questa directory, crea un file per la definizione del set di regole. Il nome del file deve essere basato su quello del plug-in di controllo e, come tutti i file del plug-in, deve avere l'estensione py
. Per il nostro esempio, il nome del file ruleset_myhostgroups.py
è adatto.
Vediamo passo dopo passo la struttura di questo file. Innanzitutto ci sono alcuni comandi di importazione:
#!/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
Per prima cosa, vengono importate le classi dei testi.
La seconda riga effettua una selezione dalle specifiche del modulo, cioè gli elementi di base per l'interfaccia grafica che vengono utilizzati nel set di regole, ad esempio la selezione binary (BooleanChoice
), la selezione multipla (Dictionary
, DictElement
), la definizione dei threshold (SimpleLevel
, LevelDirection
, DefaultValue
) e l'inserimento di numeri in virgola mobile (Float
). Qui richiedi solo gli elementi logici del modulo e lascia a Checkmk la progettazione dell'interfaccia grafica.
L'ultima riga importa le specifiche della regola, che determinano l'area di applicazione della regola in Checkmk, cioè la definizione dei parametri del check, l'assegnazione agli host e agli elementi e la memorizzazione in un tema. Se il tuo plug-in di controllo myhostgroups_advanced
genera diversi servizi, importa qui HostAndItemCondition
. Se il tuo controllo non genera un servizio, in altre parole non ha un elemento, importa invece HostCondition
.
L'utente deve poter definire separatamente i due valori soglia per WARN e CRIT sia per il numero di host nello stato UP che per il numero di servizi nello stato 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,
),
}
)
Per fare ciò, crea una funzione che generi il dizionario. Puoi scegliere liberamente il nome della funzione; è necessario solo per la creazione della regola sottostante. Il nome dovrebbe iniziare con un trattino basso in modo che la funzione non sia visibile oltre il confine del modulo.
Il modulo return Dictionary()
è obbligatorio. All'interno di esso, utilizza elements={}
per creare gli elementi del dizionario, nell'esempio hosts_up_lower
e services_ok_lower
. Il modulo dei parametri SimpleLevels
viene utilizzato per inserire dei valori di soglia fissi. Nel modulo, devi prima definire i title
e i numeri in virgola mobile per i valori da inserire (form_spec_template
). Utilizza LevelDirection.LOWER
per specificare che il livello cambierà se i valori scendono al di sotto di questo livello.
Infine, puoi utilizzare prefill_fixed_levels
per fornire agli utenti del set di regole dei valori invece che dei semplici campi di input vuoti. Nota che questi valori visualizzati nella GUI non sono i valori predefiniti impostati più avanti nella creazione del plug-in di controllo tramite check_default_parameters
. Se vuoi visualizzare gli stessi valori predefiniti nella GUI che si applicano anche alla funzione di controllo, devi mantenere i valori coerenti in entrambi i luoghi.
Infine, crea il nuovo set di regole utilizzando gli elementi importati e quelli autodefiniti, creando una nuova istanza della classe CheckParameters
. Il nome di questa istanza deve iniziare con 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")),
)
Le spiegazioni sono le seguenti:
name
stabilisce la connessione del set di regole con i plug-in di controllo. Un plug-in di controllo che vuole utilizzare questo set di regole deve utilizzare questo nome comecheck_ruleset_name
quando viene creato. Per tenere traccia di un certo numero di nuovi set di regole, è consigliabile utilizzare un prefisso nel nome.title
definisce il titolo del set di regole così come appare nella GUI di Checkmk.topic
determina dove il set di regole deve apparire nel box Setup. Con il valore selezionato nell'esempio, troverai il set di regole sotto Setup > Services > Service monitoring rules nel box Various, dove di solito si trova in buone mani.Inserisci il nome della funzione precedentemente creata come
parameter_form
.Se il tuo controllo non utilizza un elemento, la condizione è
HostCondition
e nonHostAndItemCondition
, come nell'esempio precedente. Con il titolo"Host group name"
dell'elemento, definisci l'etichetta dell'host con cui puoi limitare la regola a determinati gruppi di host.
5.2. Testare un set di regole
Una volta creato il file per il set di regole, devi verificare che tutto funzioni fino a questo momento, sempre senza una connessione al plug-in di controllo. Per farlo, devi prima riavviare Apache del sito in modo che il nuovo file possa essere letto. Questa operazione viene eseguita con il comando:
OMD[mysite]:~$ omd restart apache
Il set di regole si trova in Setup nella pagina citata sopra. Puoi trovare il set di regole anche utilizzando la funzione di ricerca nel menu di configurazione, ma solo dopo aver riavviato Redis:
OMD[mysite]:~$ omd restart redis
Il set di regole appena definito avrà il seguente aspetto nella GUI:

Nel box Conditions troverai il campo di input Host group name definito tramite HostAndItemCondition
per limitare la regola ai gruppi di host.
Crea una regola e prova diversi valori, come mostrato nella schermata qui sopra. Se funziona senza errori, ora puoi utilizzare i parametri del check nella funzione di controllo.
5.3. Connessione del set di regole al plug-in di controllo
Il set di regole appena creato è ora connesso al plug-in di controllo completato in via provvisoria nel capitolo precedente. Affinché la regola abbia effetto, devi permettere al plug-in di controllo di accettare i parametri del check e dirgli quale regola deve essere utilizzata. Per farlo, aggiungi due nuove righe al file del plug-in di controllo quando crei il plug-in di controllo myhostgroups_advanced
:
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 voce check_default_parameters
puoi definire i valori predefiniti che si applicano finché non viene creata alcuna regola. Quando converti il plug-in di controllo per i parametri del check, questa riga deve essere presente. Nel caso più semplice, passa un dizionario vuoto {}
.
In secondo luogo, inserisci check_ruleset_name
, cioè il nome del set di regole. In questo modo Checkmk sa da quale set di regole devono essere determinati i parametri.
Ora Checkmk cercherà di passare i parametri del check. Affinché questo funzioni, devi estendere la funzione check in modo che si aspetti l'argomento params
, inserito tra item
e section
:
def check_myhostgroups_advanced(item, params, section):
Se stai creando un controllo senza elementi, item
viene omesso e params
si trova all'inizio.
Si consiglia vivamente di far uscire il contenuto della variabile params
con un print
come primo test:
def check_myhostgroups_advanced(item, params, section):
print(params)
Quando il plug-in di controllo viene eseguito, le righe stampate (una per ogni servizio) con i valori definiti da una regola avranno un aspetto simile a questo:
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))})
![]() |
Quando tutto è pronto e funziona correttamente, rimuovi le chiamate a |
Ora personalizza ulteriormente la funzione del check in modo che i parametri passati possano avere effetto. Ottieni i due elementi del dizionario definiti nella regola con il nome selezionato dai parametri:
def check_myhostgroups_advanced(item, params, section):
hosts_up_lower = params["hosts_up_lower"]
services_ok_lower = params["services_ok_lower"]
Più avanti nella funzione di controllo, i valori di soglia precedentemente codificati "fixed", (90.0, 80.0)
vengono sostituiti dalle variabili hosts_up_lower
e 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,
)
Se è stata configurata una regola, ora puoi monitorare i gruppi di host dell'esempio con i valori di soglia impostati tramite la GUI. Tuttavia, se non è stata definita alcuna regola, questa funzione di controllo andrà in crash, poiché i parametri predefiniti del plug-in di controllo non saranno stati compilati e il plug-in genererà un KeyError
in assenza di una regola.
Tuttavia, questo problema può essere risolto se i valori predefiniti vengono definiti al momento della creazione del plug-in di controllo:
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",
)
Dovresti sempre passare i valori predefiniti in questo modo (e non intercettare il caso di parametri mancanti nel plug-in di controllo), poiché questi valori predefiniti possono essere visualizzati anche nell'interfaccia Setup. Ad esempio, nella scoperta del servizio di un host, nella pagina Services of host, nel menu Display, c'è lo switch Show check parameters.
![]() |
Su GitHub puoi trovare sia il file con ilset di regoleche ilplug-in di controllo esteso dal set di regole. |
6. Personalizzare le visualizzazioni delle metriche
Nell'esempio precedente, il plug-in di controllo myhostgroups_advanced
ha generato le metriche per tutti i valori misurati e calcolati. Abbiamo presentato due modi per farlo. In primo luogo, le metriche dei valori calcolati sono state create come parte della funzione check_levels()
con l'argomento metric_name
, ad esempio in questo modo:
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,
)
Poi hai generato le metriche misurate direttamente con l'oggetto Metric()
- per il numero di servizi in stato OK, ad esempio, in questo modo:
yield Metric(name="num_services_ok", value=num_services_ok)
Le metriche saranno immediatamente visibili nell'interfaccia grafica dell'utente di Checkmk senza che tu debba fare nulla. Ci sono tuttavia alcune restrizioni:
Le metriche corrispondenti non vengono automaticamente combinate in un grafico, ma vengono visualizzate singolarmente.
La metrica non avrà un titolo vero e proprio, ma verrà mostrato il nome della variabile interna della metrica.
Non viene utilizzata alcuna unità di misura che consenta una rappresentazione significativa (es. GB invece di singoli byte).
Viene selezionato un colore a caso.
Il "Perf-O-Meter", cioè l'anteprima grafica della metrica sotto forma di barra, non appare automaticamente nell'elenco dei servizi (ad esempio nella visualizzazione che mostra tutti i servizi di un host).
Per completare la visualizzazione delle metriche con tali dettagli, è necessario definire le metriche.
Come per i set di regole, a partire da Checkmk 2.3.0 esiste anche un'API separata per le metriche, i grafici e i Perf-O-Meter, la Graphing API. La documentazione della Graphing API si trova nell'istanza Checkmk alla stessa pagina della Check API, sotto Graphing > Version 1.
6.1. Creazione di nuove definizioni metriche
Le procedure per la creazione dei set di regole e delle definizioni metriche sono molto simili. Innanzitutto crea una nuova sottodirectory con il nome predefinito graphing
nella directory della famiglia del plug-in ~/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
Quindi crea un file grafico in questa directory, ad es. graphing_myhostgroups.py
.
Per prima cosa, devi eseguire alcuni comandi di importazione:
#!/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
Per il nostro esempio, ora devi definire la tua metrica per la percentuale di servizi in stato OK, creando un'istanza della classe Metric
. Il nome di questa istanza deve iniziare con 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,
)
Ecco la spiegazione:
Il nome della metrica (qui
services_ok_perc
) deve corrispondere a ciò che la funzione di controllo produce.title
è l'intestazione del grafico della metrica e sostituisce il nome della variabile interna precedentemente utilizzata.Le unità di misura disponibili (
unit
) possono essere trovate nella documentazione dell'API; tutte terminano conNotation
, ad es.DecimalNotation
,EngineeringScientificNotation
oTimeNotation
.I nomi dei colori utilizzati in Checkmk per la definizione
color
sono disponibili su GitHub.
Questa definizione nel file grafico assicura che il titolo, l'unità e il colore della metrica siano visualizzati in modo appropriato.
Come per la creazione di un file di set di regole, il file grafico deve essere letto prima che la modifica sia visibile nella GUI, riavviando Apache del sito:
OMD[mysite]:~$ omd restart apache
Il grafico della metrica avrà l'aspetto seguente nella GUI di Checkmk:

6.2. Grafici con più metriche
Se vuoi combinare più metriche in un unico grafico (cosa spesso molto utile), avrai bisogno di una definizione di grafico che potrai aggiungere al file di grafici creato nella sezione precedente.
Nel nostro esempio, le due metriche num_services
e num_services_ok
devono essere visualizzate in un unico grafico. Le definizioni di metrica sono create nello stesso modo della sezione precedente per services_ok_perc
e hanno il seguente aspetto:
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,
)
Ora aggiungiamo un grafico che disegni queste due metriche sotto forma di linee. Questo avviene tramite un'istanza della classe Graph
, il cui nome deve iniziare con un prefisso predefinito (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),
)
Il parametro minimal_range
descrive l'intervallo minimo coperto dall'asse verticale del grafico, indipendentemente dai valori effettivi della metrica. L'asse verticale sarà esteso se i valori superano l'intervallo minimo, ma non sarà mai più piccolo.
Il risultato è il grafico combinato nella GUI di Checkmk:

6.3. Le metriche nel Perf-O-Meter
Vuoi visualizzare un Perf-O-Meter per una metrica nella riga dell'elenco dei servizi? Ad esempio, potrebbe apparire così:

Per creare un Perf-O-Meter di questo tipo, avrai bisogno di un'altra istanza, questa volta della classe Perfometer
, il cui nome inizia con il prefisso perfometer_
:
perfometer_myhostgroups_advanced = Perfometer(
name = "myhostgroups_advanced",
focus_range = FocusRange(Closed(0), Closed(100)),
segments = [ "services_ok_perc" ],
)
I Perf-O-Meter sono un po' più complicati dei grafici, poiché non hanno una legenda. È quindi difficile visualizzare un intervallo di valori, soprattutto se si tratta di valori assoluti. È più facile visualizzare una percentuale. Questo è anche il caso dell'esempio precedente:
I nomi del servizio sono inseriti in
segments
, in questo esempio la percentuale di servizi in stato OK.I limiti inferiori e superiori sono inseriti in
focus_range
. I limiti diClosed
sono destinati alle metriche che possono accettare solo valori compresi tra i limiti specificati (qui0
e100
).
Altre opzioni ancora più sofisticate per implementare le metriche nei Perf-O-Meter si trovano nella documentazione dell'API di Grafica, ad esempio con le classi Bidirectional
e Stacked
, che permettono di visualizzare più Perf-O-Meter in uno.
Il file grafico di questo capitolo è disponibile su GitHub e contiene, tra le altre cose, le definizioni delle metriche per i quattro valori misurati e i due valori calcolati.
7. Formattazione dei numeri
I numeri vengono spesso visualizzati nelle pagine Summary e Details di un servizio. Per semplificare al massimo una formattazione ordinata e corretta e per standardizzare l'output di tutti i plug-in di controllo, esistono delle funzioni helper per visualizzare i vari tipi di grandezze. Tutte queste funzioni sono sottofunzioni del modulo render
e vengono quindi richiamate con render.
. Ad esempio, render.bytes(2000)
produce il testo 1.95 KiB
.
Tutte queste funzioni hanno in comune il fatto che il loro valore viene mostrato in una cosiddetta unità canonica o naturale. Questo significa che non devi mai pensarci e che non ci sono difficoltà o errori nella conversione. Ad esempio, i tempi sono sempre indicati in secondi e le dimensioni di dischi rigidi, file, ecc. sono sempre indicate in byte e non in kilobyte, kibibyte, blocchi o altre confusioni del genere.
Utilizza queste funzioni anche se la visualizzazione non ti piace molto: in ogni caso, sarà standardizzata per l'utente. Le versioni future di Checkmk potrebbero essere in grado di modificare la visualizzazione o addirittura renderla configurabile dall'utente, come già avviene ad esempio per la visualizzazione della temperatura. Anche il tuo plug-in di controllo ne beneficerà.
Per poter utilizzare la funzione render
nel tuo plug-in di controllo, devi anche importarla:
from cmk.agent_based.v2 import render
Dopo questa descrizione dettagliata di tutte le funzioni di visualizzazione (funzioni di rendering), troverai un riassunto sotto forma di tabella di facile lettura.
7.1. Tempi, periodi di tempo e frequenze
Le specifiche temporali assolute (timestamp) sono formattate con render.date()
o render.datetime()
. Le informazioni sono sempre fornite come tempo Unix, cioè in secondi dal 1 gennaio 1970, 00:00:00 UTC - l'inizio dell'epoca Unix. Questo è anche il formato utilizzato dalla funzione Python time.time()
.
Il vantaggio di questa rappresentazione è che è molto facile da calcolare, ad esempio il calcolo di un periodo di tempo, quando si conoscono gli orari di inizio e fine. La formula è quindi semplicemente duration = end - start
. Questi calcoli funzionano indipendentemente dal fuso orario, dal cambio dell'ora legale o dagli anni bisestili.
render.date()
render.datetime()
aggiunge l'ora. L'output è conforme al fuso orario in cui si trova il server Checkmk che sta eseguendo il controllo. Esempi:
Chiamata | Uscita |
---|---|
|
|
|
|
|
|
|
|
Non sorprenderti se render.datetime(0)
non riporta l'ora di 00:00
, ma quella di 01:00
. Questo perché stiamo scrivendo questo manuale d'uso con il fuso orario della Germania, che è un'ora avanti rispetto all'ora standard UTC (almeno durante l'ora solare, perché il 1° gennaio non c'è l'ora legale).
Per i periodi di tempo (o intervalli di tempo) c'è anche la funzione render.timespan()
. Questa funzione riceve una durata in secondi e la restituisce in un modulo leggibile. Per intervalli di tempo più ampi, i secondi o i minuti vengono omessi. Se hai un intervallo di tempo in un oggetto TimeDelta
, usa la funzione total_seconds()
per leggere il numero di secondi come numero in virgola mobile.
Chiamata | Uscita |
---|---|
|
|
|
|
|
|
|
|
Una frequenza è di fatto il reciproco del tempo. L'unità canonica è l'Hz, che equivale a 1 / sec (il reciproco di un secondo). Un esempio di utilizzo è la frequenza di clock di una CPU:
Chiamata | Uscita |
---|---|
|
|
7.2. Byte
Per quanto riguarda la memoria di lavoro, i file, i dischi rigidi, i file system e simili, l'unità di misura canonica è il byte. Poiché i computer di solito organizzano queste cose in potenze di due, ad esempio in unità di 512, 1024 o 65.536 byte, si è stabilito fin dall'inizio che un kilobyte non è 1000, e quindi mille volte l'unità, ma 1024 (2 alla potenza di 10) byte. Si tratta di una scelta illogica, ma molto pratica perché di solito si ottengono numeri tondi: il leggendario Commodore C64 aveva 64 kilobyte di memoria e non 65,536.
Sfortunatamente, a un certo punto i produttori di dischi rigidi hanno avuto l'idea di specificare le dimensioni dei loro dischi in unità di 1000. Poiché la differenza tra 1000 e 1024 è del 2,4% per ogni dimensione, moltiplicando questi valori, un disco di dimensioni pari a 1 GB (1024 per 1024 per 1024) diventa improvvisamente 1,07 GB.
Per ovviare a questa fastidiosa confusione, la Commissione Elettrotecnica Internazionale (IEC) ha definito nuovi prefissi basati sul sistema binario. Di conseguenza, oggi un kilobyte è ufficialmente 1000 byte e un kibibyte è 1024 byte (2 alla potenza di 10). Inoltre, si dovrebbe dire mebibyte, gibibyte e tebibyte. Le abbreviazioni sono quindi KiB, MiB, GiB e TiB.
Checkmk rispetta questo standard e ti aiuta con una serie di funzioni di rendering personalizzate per assicurarti di produrre sempre l'output corretto. Ad esempio, c'è la funzione render.disksize()
specifica per i dischi rigidi e i file system, che produce il suo output in potenze di 1000.
Chiamata | Output |
---|---|
|
|
|
|
|
|
Quando si parla di dimensioni dei file, spesso è consuetudine specificare le dimensioni esatte in byte senza arrotondare. Questo ha il vantaggio di poter vedere molto rapidamente se un file è cambiato anche solo in minima parte o se due file sono (probabilmente) uguali. A questo scopo si usa la funzione render.filesize()
:
Chiamata | Output |
---|---|
|
|
|
|
|
|
Se vuoi ottenere un valore che non sia la dimensione di un disco rigido o di un file, usa semplicemente la funzione generica render.bytes()
. In questo modo otterrai l'output nella notazione ufficiale "classica" di 1024 potenze:
Chiama | Uscita |
---|---|
|
|
|
|
|
|
7.3. Larghezze di banda, velocità di trasmissione dati
Gli utenti della rete hanno i loro termini e modi di esprimersi e, come sempre, Checkmk si sforza di adottare il modo convenzionale di comunicare in ogni ambito. Per questo motivo esistono tre diverse funzioni di rendering per le velocità di trasmissione dei dati. Ciò che hanno in comune è che le velocità sono espresse in byte al secondo, anche se l'output effettivo è in bit!
render.nicspeed()
rappresenta la velocità massima di una scheda di rete o di una porta dello switch. Poiché non si tratta di valori misurati, non è necessario arrotondare. Anche se nessuna porta può inviare singoli bit, i dati sono espressi in bit per motivi storici.
Importante: tuttavia, è necessario passare anche i byte al secondo!
Chiamata | Uscita |
---|---|
|
|
|
|
render.networkbandwidth()
è destinata a una velocità di trasmissione effettivamente misurata nella rete. Il valore inserito è di nuovo byte al secondo:
Chiamata | Uscita |
---|---|
|
|
|
|
|
|
Quando non è coinvolta una rete e la velocità di trasmissione dei dati è ancora una volta il byte. Il caso più evidente è quello della velocità di trasmissione dei dischi rigidi. A questo scopo si utilizza la funzione render.iobandwidth()
, che in Checkmk lavora con potenze di 1000:
Chiamata | Uscita |
---|---|
|
|
|
|
|
|
7.4. Percentuali
La funzione render.percent()
rappresenta una percentuale, arrotondata a due cifre decimali. Fa eccezione alle altre funzioni in quanto non passa il valore naturale effettivo, cioè il rapporto, ma la percentuale reale. Ad esempio, se qualcosa è pieno a metà, non devi passare 0.5
ma 50
.
Poiché a volte può essere interessante sapere se un valore è quasi zero o esattamente zero, i valori sono contrassegnati dall'aggiunta del carattere "<" per i valori superiori a zero ma inferiori allo 0,01%.
Chiamata | Uscita |
---|---|
|
|
|
|
|
|
7.5. Riepilogo
Per concludere, ecco una panoramica di tutte le funzioni di rendering:
Funzione | Ingresso | Descrizione | Esempio di uscita |
---|---|---|---|
|
Tempo Unix |
Data |
|
|
Tempo Unix |
Data e ora |
|
|
Secondi |
Durata / età |
|
|
Hz |
Frequenza (es. frequenza dell'orologio) |
|
|
Byte |
Dimensione di un disco rigido, base 1000 |
|
|
Byte |
Dimensione di un file, precisione completa |
|
|
Byte |
Dimensione, base 1024 |
|
|
Byte al secondo |
Velocità della scheda di rete |
|
|
Byte al secondo |
Velocità di trasmissione |
|
|
Byte al secondo |
Larghezze di banda IO |
|
|
Numero percentuale |
Percentuale, arrotondata in modo significativo |
|
8. Risoluzione dei problemi
La corretta gestione degli errori (purtroppo) occupa gran parte del lavoro di programmazione. La buona notizia è che l'API Check si occupa già di gran parte della gestione degli errori. Per alcuni tipi di errori, quindi, è meglio non gestirli affatto da soli.
Se Python incontra una situazione in qualche modo inaspettata, reagisce con una cosiddetta eccezione. Ecco alcuni esempi:
Converti una stringa in un numero con
int()
, ma la stringa non contiene un numero, ad esempioint("foo")
.Utilizzi
bar[4]
per accedere al quinto elemento dibar
, che però ha solo quattro elementi.Stai chiamando una funzione che non esiste.
Per decidere come affrontare gli errori, è importante prima di tutto conoscere il punto esatto del codice in cui si verifica l'errore. A questo scopo puoi utilizzare la GUI o la linea di comando, a seconda del punto in cui stai lavorando.
8.1. Eccezioni e report sui crash nella GUI
Se si verifica un'eccezione durante il monitoraggio o la scoperta del servizio nel sito Setup, il sito Summary contiene i riferimenti al report sui crash appena creato. L'aspetto è il seguente, ad esempio:

Facendo clic sul simbolo viene visualizzata una pagina con i dettagli in cui è possibile:
puoi vedere il file in cui si è verificato il crash,
ricevere tutte le informazioni sul crash, come l'elenco degli errori che si sono verificati nel programma(traceback), i valori correnti delle variabili locali, l'output dell'agente e molto altro ancora, e
può inviare il report a noi (Checkmk GmbH) come feedback.
Il traceback ti aiuta come sviluppatore a decidere se c'è un errore nel programma (ad es. la chiamata di una funzione inesistente) o se i dati dell'agente non hanno potuto essere processati come previsto. Nel primo caso vorrai correggere l'errore, nel secondo caso spesso ha senso non fare nulla.
L'invio del report è ovviamente utile solo per i plug-in di controllo che fanno ufficialmente parte di Checkmk. Se metti a disposizione di terzi i tuoi plug-in, puoi chiedere ai tuoi utenti di inviarti i dati.
8.2. Visualizzazione delle eccezioni da linea di comando
Se esegui il plug-in di controllo tramite la linea di comando, non riceverai alcuna indicazione sull'ID dei report sui crash generati, ma vedrai solo il messaggio di errore riassunto:
OMD[mysite]:~$ cmk --detect-plugins=myhostgroups_advanced localhost
Error in agent based plugin myhostgroups: invalid syntax (myhostgroups.py, line 11)
Se aggiungi l'opzione --debug
come parametro di chiamata aggiuntivo, riceverai il traceback dall'interprete 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
Se l'errore non si ripresenta alla prossima chiamata a --debug
, ad esempio perché è disponibile un nuovo output dell'agente, puoi anche visualizzare gli ultimi report sui crash nel file system:
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/
In ognuna di queste cartelle sono presenti due file:
crash.info
contiene un dizionario Python con traceback e molte altre informazioni. Spesso è sufficiente dare un'occhiata al file con il Pager.agent_output
contiene l'output completo dell'agente al momento del crash.
8.3. Output di debug personalizzato
Negli esempi mostrati in precedenza, abbiamo utilizzato la funzione print()
per visualizzare il contenuto delle variabili o la struttura degli oggetti per gli sviluppatori. Queste funzioni di debug devono essere rimosse dal plug-in di controllo finito.
In alternativa alla rimozione, puoi anche fare in modo che l'output di debug venga visualizzato solo quando il plug-in di controllo viene richiamato dalla console in modalità di debug. Per fare ciò, importa l'oggetto di debug dalla casella degli strumenti Checkmk e, se necessario, l'aiuto alla formattazione pprint()
. Ora puoi produrre l'output di debug in base al valore dell'oggetto di debug:
![]() |
L'oggetto di debug cambierà il suo percorso tra Checkmk 2.3.0 e 2.4.0. Invece della posizione attuale |
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)
Gli errori evidenti e prevedibili dell'utente (ad esempio, il contenuto della sezione agent indica che il plug-in dell'agente è stato configurato in modo errato) dovrebbero essere risolti con lo stato SCONOSCIUTO e includere note esplicative nel riepilogo.
8.4. Output dell'agente non valido
La questione è come reagire se l'output dell'agente non ha il modulo che ti aspetti, sia che provenga dall'agente Checkmk sia che venga ricevuto tramite SNMP. Supponiamo che ti aspetti sempre tre parole per riga, cosa dovresti fare se ne ricevi solo due?
Se si tratta di un comportamento consentito e conosciuto dall'agente, allora è ovvio che devi intercettarlo e lavorare con una distinzione di casi. Tuttavia, se questo non è consentito, allora è meglio agire come se la riga fosse sempre composta da tre parole, ad esempio con la seguente funzione di parsing:
def parse_foobar(string_table):
for foo, bar, baz in string_table:
# ...
Se c'è una riga che non è composta esattamente da tre parole, viene generata un'eccezione e si riceve l'utilissimo report sui crash appena citato.
Se si accede a chiavi di un dizionario che si prevede possano essere occasionalmente mancanti, può avere senso reagire di conseguenza, impostando il servizio su CRIT o SCONOSCIUTO e inserendo una nota nel riepilogo sull'output dell'agente che non può essere valutato. In ogni caso, è meglio utilizzare la funzione get()
del dizionario piuttosto che cogliere l'eccezione KeyError
. Questo perché get()
restituisce un oggetto di tipo None
o un sostituto opzionale da passare come secondo parametro se la chiave non è disponibile:
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. Elementi mancanti
Cosa succede se l'agente fornisce i dati corretti, ma l'elemento da controllare è mancante? Ad esempio, questo è il caso:
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
Se l'elemento che stai cercando non è incluso, il ciclo viene eseguito e Python si ferma semplicemente alla fine della funzione senza restituire un risultato tramite yield
. E questo è esattamente l'approccio giusto! Perché Checkmk riconosce che l'elemento da monitorare è mancante e genera lo stato corretto e un testo standard adeguato con SCONOSCIUTO.
8.6. Test con i file di spool
Se vuoi simulare particolari output dell'agente, i file di spool nella directory sono molto utili. Puoi usarli per testare casi limite che sono altrimenti difficili da ricreare. Oppure puoi usare direttamente l'output dell'agente che ha portato a un report sui crash per testare le modifiche a un plug-in di controllo.
Per prima cosa disattiva il tuo normale plug-in dell'agente, ad esempio revocando la sua autorizzazione all'esecuzione. Poi crea un file nella directory /var/lib/check_mk_agent/spool/
che contenga la sezione dell'agente (o le sezioni previste dell'agente) che il tuo plug-in di controllo si aspetta, compresa l'header sezioni, e che termini con Newline. La prossima volta che l'agente viene chiamato, il contenuto del file di spool verrà trasferito al posto dell'output del plug-in dell'agente.
8.7. I vecchi plug-in di controllo diventano lenti per molti servizi
Con alcuni plug-in di controllo che utilizzano gli elementi, è possibile che sui server più grandi vengano generate diverse centinaia di servizi. Se non viene utilizzata una funzione di parse separata, ciò significa che l'intero elenco di centinaia di righe deve essere esaminato per ognuna delle centinaia di elementi. Il tempo necessario per la ricerca aumenta quindi del quadrato del numero di elementi elencati, il che significa decine di migliaia di confronti per centinaia di servizi. Se invece l'elenco annidato viene trasferito in un dizionario, il tempo necessario per la ricerca di un elemento aumenta solo linearmente con la dimensione del dizionario.
Nel wiki di Python troverai una panoramica dei costi per la ricerca in diversi tipi di dati, compresa una spiegazione e la notazione O. L'utilizzo della funzione parse riduce la complessità della ricerca da O(n) a O(1).
Poiché le versioni precedenti di questo articolo non facevano uso della funzione di parsing, dovresti identificare tali plug-in di controllo e riscriverli in modo da utilizzare una funzione di parsing.
9. Migrazione
La Check API V1 introdotta nella versione 2.0.0 di Checkmk continuerà ad essere supportata nella versione 2.3.0, ma non più nella prossima versione 2.4.0. Ti consigliamo quindi di sfruttare il tempo che manca al rilascio della versione 2.4.0 e di migrare tutti i plug-in di controllo alle nuove API.
Le seguenti informazioni ti aiuteranno nella migrazione dei plug-in di controllo dalla Check API V1 alla V2:
La nuova struttura delle directory e la convenzione di denominazione per l'archiviazione dei file di tutte le API del plug-in sono disponibili nella documentazione delle API alla pagina principale Checkmk’s Plug-in APIs.
Il riepilogo delle modifiche apportate alla Check API V2 si trova anche nella documentazione delle API alla pagina Checkmk’s Plug-in APIs > Agent based ('Check API') > Version 2 > New in this version. Qui troverai anche il link a un commit GitHub che migra i plug-in di controllo esistenti
apt
alla Check API V2.Su GitHub troverai degli script nella directory
treasures
di Checkmk che ti aiuteranno a migrare alle nuove API.
Un'ultima nota per gli utenti che hanno ancora dei plug-in di controllo sviluppati con le vecchie Check API, valide fino alla versione Checkmk 1.6.0. Questi plug-in di controllo non sono più ufficialmente supportati dalla versione Checkmk 2.3.0. I dettagli si trovano in Werk #16689. Puoi scoprire come migrare questi plug-in di controllo obsoleti alla Check API V1 nel post del blog sulla migrazione.
10. File e directory
Percorso del file | Descrizione |
---|---|
|
Directory di base per la memorizzazione dei file dei plug-in. |
|
Luogo di archiviazione dei plug-in di controllo scritti secondo la Check API V2. |
|
Luogo di archiviazione dei file dei set di regole creati secondo la Rulesets API. |
|
Percorso di archiviazione per i file dei grafici creati secondo la Graphing API. |
|
Questa directory si trova su un host Linux monitorato. L'agente Checkmk per Linux attende qui le estensioni dell'agente (plug-in dell'agente). |