Attualmente sono disponibili quasi 2.000 plug-in di controllo ufficiali come estensioni per il monitoraggio IT con Checkmk. Tuttavia, l'API di Check è la stessa di quando c'erano solo pochi plug-in e non è stata aggiornata da allora. Con Checkmk 2.0, stiamo introducendo una nuova API Check che ristruttura i plug-in di controllo per standardizzare e semplificare la loro gestione a vari livelli.
Inoltre, con la nuova versione del prodotto passiamo da Python 2 a Python 3. Questo passaggio non ha nulla a che vedere con la versione del prodotto. Questo passaggio non ha nulla a che fare con la nuova API Check, ma significa che dovrai comunque rivedere il codice dei tuoi plug-in.
Sebbene ci siano buone probabilità che il porting dei plug-in Check esistenti alla nuova API Check funzioni senza problemi, il passaggio a Python 3 richiede comunque di esaminare i propri plug-in e di verificare se sia necessario apportare qualche modifica. Per sapere come verificare se l'auto-migrazione di un plugin fallisce, si può consultare il documento werk #10601.
In questo post spiegheremo in dettaglio come migrare senza problemi i plugin di controllo scritti in proprio alla nuova API Check. L'articolo è rivolto agli utenti avanzati ed è diviso in tre parti: "Preparazione del codice", "Migrazione del plug-in" e "La pallottola d'argento".
Preparazione del codice
Per prima cosa affrontiamo il porting da Python 2 a Python 3. In rete si trovano già molti tutorial al riguardo. Nel nostro caso, i punti più importanti sono la 'Divisione' e la gestione di 'Testo contro Dati binari'. Poiché str è Unicode in Python 3, la regola generale per il codice del plug-in di controllo è di usare str il più spesso possibile. Poiché, come descritto nella documentazione, la divisione cambia a partire da Python 3, è necessario controllare il codice per assicurarsi che il valore calcolato sia ancora del tipo desiderato (float/int). Questo commit mostra come correggere la divisione degli interi in un controllo.
Pulire il codice
Prima di iniziare la migrazione vera e propria, è necessario ripulire il codice e renderlo il più 'vicino' possibile alla nuova API, senza però effettuare il porting. Per fare ciò, è necessario aggiungere i comandi "import". Questo non era necessario con le 'vecchie' librerie, poiché Checkmk si assicurava che fossero 'magicamente' disponibili. Con la nuova API di Check, invece, ogni utente è responsabile dell'importazione regolare delle librerie Python necessarie.
A questo punto è bene rendere esplicite queste importazioni 'magiche'. Importando le librerie Python nella prima fase, si riducono i falsi positivi quando si usa un IDE con linter
o typechecker
. Questo permette anche di concentrarsi meglio sui potenziali problemi durante la migrazione vera e propria.
Tutto questo diventa obsoleto con le nuove API
A partire da Python 3, il metodo iteritems
è stato abbandonato. Ciò richiede anche alcune modifiche al codice del plug-in, in particolare la sostituzione di iteritems
con items
.
Anche le due funzioni saveint()
e savefloat()
sono state eliminate. In passato, saveint(x)
restituiva il valore 0 se x non poteva essere convertito in un numero, ad esempio perché era una stringa vuota o non era composto solo da cifre. Sebbene questa azione possa essere utile in alcune situazioni, nella maggior parte dei casi veniva utilizzata in modo scorretto, mascherando molti errori. Ora, se si vuole ottenere uno 0 su una stringa vuota - che secondo la nostra esperienza è l'uso più comune di saveint(x)
- è sufficiente codificare come segue:
foo = int(x) if x else 0
Lo stesso vale per savefloat()
.
Un ruolo più importante per la funzione parse
Con la nuova API Check, la funzione parse ha un ruolo più importante. Pertanto, si dovrebbe sempre implementarne una, se non lo si è già fatto. L'implementazione è semplice, come dimostra il commit che introduce una funzione di parsing.
La funzione parse ha il compito di convertire tutti i dati grezzi in insiemi di dati utilizzabili e di scartare i dati inutili.
Inoltre, è utile inserire la funzione di controllo in una struttura di dati adeguata. Ad esempio, se il record dell'elemento non viene trovato nelle sezioni analizzate, il motore di controllo può generare un messaggio generico e coerente.
Se si trasforma la chiave in un elemento, si può semplicemente scoprire un servizio per chiave e cercare i dati corrispondenti nella funzione di controllo, come mostra il commit "parse data into a dictionary".
Le funzioni check e discovery devono essere funzioni generatore
Si noti, tuttavia, che le funzioni di verifica e di scoperta devono essere funzioni generatore. Questo è obbligatorio nella nuova API. Questo era già possibile nella vecchia API, quindi possiamo cambiarlo qui senza problemi.
Inoltre, bisogna considerare che i parametri della funzione check, se ne ha, devono sempre essere un dizionario. Se attualmente non è così, si dovrà adattare di conseguenza l'implementazione nella catena di regole WATO.
Come ultimo passo della preparazione alla migrazione, controlliamo se check_levels
può essere utilizzato.
Sebbene non abbia nulla a che fare con la migrazione stessa, si tratta di un'utile funzione di aiuto che può facilitare il lavoro. Molti plug-in di controllo confrontano un valore (numerico) con i valori di soglia definiti per impostare uno stato. A tale scopo, si dovrebbe sempre utilizzare la funzione check_levels
, disponibile sia nella vecchia che nella nuova API. In linea di principio, si consiglia di utilizzare il maggior numero possibile di funzioni API per ottenere un aspetto generale più uniforme e standardizzato.
Con questo abbiamo completato i preparativi e possiamo passare alla migrazione vera e propria.
Migrazione dei plug-in
Quando si migra un plug-in alla nuova API di Check, è necessario tenere conto di alcuni punti che sono cambiati rispetto alla vecchia API. Come già detto, i plug-in sono moduli Python. I plug-in personalizzati saranno ora registrati nella directory local/lib/check_mk/base/plugins/agent_based
e richiederanno l'estensione .py
. È necessario tenerne conto in ogni caso quando si memorizza il file. Molte delle spiegazioni seguenti possono essere comprese meglio guardando questo commit di migrazione.
La nuova API Check registra ora separatamente le sezioni e i controlli effettivi. Nella vecchia API questi erano ancora raggruppati sotto check_info
.
Nella nuova API, l'agent_section è la funzione di registrazione. L'utente può trovare tutte le informazioni su questo processo e sulle funzionalità di agent_section
nella documentazione dell'API. Le sezioni si definiscono con register.agent_section
o register.snmp_sections
, a seconda del tipo di origine dei dati. Per un elenco di tutti gli argomenti consentiti, consultare la documentazione di Sphinx accessibile dal menu Aiuto della propria istanza di Checkmk.
Nel caso più semplice, sono necessari solo due argomenti: il nome della sezione (che di solito corrisponde al nome del plug-in) e la funzione di analisi. Si noti che il nome della variabile dell'argomento deve essere string_table
.
Il rilevamento SNMP sostituisce la funzione di scansione SNMP
La vecchia funzione di scansione SNMP è ora chiamata rilevamento SNMP nell'ambito della nuova API Check ed è una specifica descrittiva. Per il rilevamento SNMP l'utente deve specificare i singoli OID e i testi di ricerca di cui ha bisogno e l'intervallo SNMP che il controllo SNMP deve rilevare per il monitoraggio. Maggiori informazioni su questo aspetto e sulla logica con cui si deve ricostruire la funzione di scansione sono disponibili nel manuale di Checkmk.
Inoltre, con i supersedes è ora disponibile un'opzione separata con la quale è possibile escludere altre sezioni. Questo non avviene più tramite la funzione di scansione SNMP.
Abbiamo anche rinominato le funzioni OIDBytes. Questo comporta le seguenti modifiche: OID_END
diventa OIDEnd()
, BINARY('2')
diventa OIDBytes('2')
e CACHED_OID('3'
) diventa OIDCached('3'). Tuttavia, sono cambiati solo i nomi, la funzionalità rimane la stessa. Tutte le altre funzioni OID, come OID_STRING
, non esistono più con la nuova API Check. Le tre funzioni citate sono necessarie per specificare l'albero SNMP. Mentre in passato era sufficiente specificare un elenco, ora è necessario utilizzare la classe SNMPTree. Informazioni più dettagliate al riguardo sono disponibili nel manuale di Checkmk.
Abbiamo anche rinominato diversi nomi di funzioni e argomenti con la nuova API Check. Ad esempio, ora si parla sempre coerentemente di Discovery quando si tratta di scoprire servizi o etichette di host. Inventario verrà utilizzato solo per le funzioni relative all'inventario di hardware e software.
La funzione di scoperta e la funzione di controllo, come già detto, devono ora funzionare sempre come generatori, quindi è necessario utilizzare yield
. È inoltre possibile aggiungere parametri alla funzione di scoperta, in modo che host_extra_conf
non sia più necessario.
Con la nuova API, il precedente meccanismo di "inclusione" non esiste più. Ora funziona tramite le importazioni Python. Il codice che diversi plug-in devono utilizzare può essere memorizzato in directory local/lib/check_mk/base/plugins/agent_based/utils
e può quindi essere importato nel modo normale dai plug-in.
Passare ValueStore in modo esplicito
È cambiata anche la funzione item_state
, che assicurava la persistenza di un valore durante i vari controlli. La vecchia API utilizzava le funzioni set_item_rate
, get_rate
e get_average
per questo scopo. Al loro posto, la nuova API ha ora la funzione get_value_store
. Essa restituisce una cosiddetta "mappatura mutabile", un oggetto che può essere usato come un dizionario. In questo caso, ogni servizio riceve il proprio oggetto individuale, quindi non è necessario inserire il nome del plug-in di controllo o dell'elemento nella chiave. Questo oggetto (il ValueStore) ha il compito di occuparsi dei valori persistenti.
get_average
e get_rate
esistono ancora, ma bisogna passargli esplicitamente il ValueStore. In passato, questi oggetti condividevano la funzione in background. Con la nuova API, invece, si tratta di funzioni puramente ausiliarie senza interazione con il backend, alle quali deve essere passato esplicitamente il ValueStore.
Anche la precedente classe di errore MKCounterWrapped
ha ricevuto un nuovo nome. Questa classe consentiva di sollevare eccezioni che causavano lo "stallo" di un servizio. Ora è possibile ottenere questo comportamento sollevando un'eccezione del tipo IgnoreResultsError
o restituendo un oggetto IgnoreResults
.
Come si può leggere nella documentazione dell'API, la nuova API Check ora restituisce le costanti State.OK, State.WARN, State.CRIT, State.UNKOWN invece di valori di ritorno magici, come 2 per CRIT. Ciò significa che in precedenza un risultato appariva come questo:
return 0, "All OK",[("foo",42)].
Con la nuova API, il risultato cambia ora come segue:
yield Result(state=State.OK, summary="Everything OK")
yield Metric("foo",42)
Se ci sono più risultati, Checkmk prende automaticamente lo stato peggiore.
Nella documentazione e nel manuale di Checkmk è presente anche una spiegazione dettagliata delle precedenti funzioni di get_<type>_human_readable
, che ora sono riunite in un modulo di rendering comune.
Il "proiettile d'argento"
Se hai seguito tutti i passaggi finora descritti, i tuoi plug-in scritti in proprio funzioneranno già con la nuova API di Check. A questo punto, tuttavia, vorremmo approfondire alcuni argomenti più avanzati, ovvero la "pallottola d'argento" della migrazione dei plug-in. Questo è rivolto principalmente agli utenti esperti che desiderano ottenere ancora di più dal proprio codice.
Tra le altre cose, è possibile utilizzare le definizioni di tipo per chiarire l'intenzione di quale tipo di valore ci si aspetta nel codice. Questi suggerimenti sul tipo assicurano una struttura pulita del codice e permettono di eseguire un typechecker sul codice e quindi di rilevare eventuali errori. Nella documentazione di Sphinx, sotto la voce "Type annotations" si possono vedere le varie definizioni di tipo utilizzate nell'API. Qui si trovano anche tutte le informazioni sul modulo 'Type Definitions', particolarmente utile per lo sviluppo dei propri plug-in.
L'uso delle definizioni dei tipi non è obbligatorio, ma lo consigliamo. Questo passo è necessario al più tardi quando si vuole mainlinizzare il codice. In linea di principio, se si decide di fare il mainline del codice, questo deve essere coperto da test unitari. Poiché i plug-in sono ora moduli regolari, è molto più facile scrivere test unitari. Questo aiuta anche a codificare plug-in puliti.
Un altro punto che vorremmo affrontare a questo punto sono i plug-in dei cluster. Con la vecchia API, ogni plug-in poteva essere utilizzato automaticamente su un cluster. Questa situazione è cambiata con la nuova API. Se un plug-in deve funzionare in un cluster, è necessario implementare esplicitamente una funzione cluster. La procedura per farlo può essere facilmente compresa in questo commit su come rendere un plug-in adatto al clustering.
Speriamo di essere riusciti a rispondere a tutte le domande aperte sulla migrazione dei plug-in scritti in proprio alla nuova API di Check. Ulteriori informazioni sulla nuova API di Check sono disponibili nel manuale di Checkmk. Se hai ancora domande aperte, puoi anche inviarle al nostro forum Checkmk.