Un plug-in dinamico è un plug-in personalizzato che può essere creato per qualsiasi nodo di trasporto supportato, ad esempio un host ESXi, per verificarne l'integrità.
Un plug-in dinamico può essere installato in runtime e svolge le seguenti funzioni:
- Scrive un registro di sistema per il nodo di trasporto interessato.
- Esegue un comando o una CLI tramite la funzione
run_command()
. - Legge le metriche esistenti.
- Esporta i dati in Wavefront (solo in un ambiente VMware Cloud).
È possibile creare un plug-in dinamico se si è esperti nella gestione di NSX. Un plug-in dinamico può essere creato anche dall'assistenza VMware. Una volta creato, un plug-in deve essere inviato a un repository GIT per la convalida. Prima dell'invio, deve essere esaminato e testato.
Gestione della sicurezza
Una volta esaminato e testato il plug-in, il risultato del test e le modifiche del codice vengono inviati per la convalida in un repository GIT. Utilizzare i seguenti dettagli del repository GIT
- GitLab: https://gitlab.eng.vmware.com/core-build/nsbu-sha-plugin
- Prodotto: nsx-sha-plugins
- Supporto per Gitreview: eseguito dal team VMware
Dopo la convalida e l'approvazione, il plug-in viene confermato nel repository GIT. Tutti i plug-in dinamici vengono creati e firmati quando viene creata una nuova build. Per ogni plug-in viene creato un pacchetto, che viene firmato separatamente. È possibile ottenere il plug-in richiesto dai file pubblicati della build.
Quando un plug-in firmato viene caricato nel piano di gestione, il piano di gestione utilizza una chiave pubblica per verificarne la firma e la validità. Dopo la convalida del plug-in, viene eseguito il push dei file del plug-in negli host di destinazione tramite i canali protetti tra il piano di gestione e il piano di controllo centrale (CCP) e tra il CCP e gli host.
Se un'istanza dell'Agente stato del sistema (SHA) viene riavviata, riceve nuovamente i file di plug-in dal piano di gestione. Poiché tutti i file vengono pubblicati tramite canali protetti e non viene utilizzato alcun file temporaneo, si evita il rischio che gli hacker possano modificare gli script.
Inoltre, per evitare i rischi legati al codice dannoso, l'agente SHA utilizza RestrictedPython per controllare lo script Python del plug-in prima di eseguire lo script.
Gestione delle versioni
Un plug-in si può basare su un comando o su uno strumento non supportato nelle versioni successive di NSX pertanto, per ogni plug-in personalizzato, è necessario definire la versione di NSX supportata nel file manifest.yml. La versione deve essere una stringa REGEX per tutte le versioni supportate. L'agente SHA sul lato host controlla la versione del plug-in personalizzato ed esegue solo quelle corrispondenti a REGEX.
-
Definire la versione di NSX supportata di una versione principale.
Considerando che la maggior parte dei comandi e degli strumenti non cambia tra le versioni minori nella stessa versione principale, si consiglia di definire la versione per tutte le versioni secondarie come indicato di seguito.
Ad esempio
version: ^2\.5\.[0-9.]+ <== The custom plugin supporting all NSX 2.5 releases
- Quando viene pubblicata una nuova versione principale, devono essere esaminati tutti i plug-in dinamici inviati.
- Il writer del plug-in deve aggiornare lo script quando gli strumenti o i comandi correlati vengono modificati.
Installare un plug-in dinamico
Il nodo di trasporto o il nodo Edge in cui è installato il plug-in deve avere almeno 30 MB di spazio di memoria. Inoltre, è possibile installare solo un massimo di 10 plug-in. Se sono installati 10 plug-in, qualsiasi ulteriore installazione non riesce.
- Creare i file di plug-in dinamici nel repository GIT. Per ulteriori informazioni sui file di plug-in, vedere la sezione File di plug-in dinamici.
- Attivare la build del prodotto nel repository GIT per generare il pacchetto compresso dei file di plug-in dinamici e scaricare il pacchetto.
- Creare il plug-in dinamico utilizzando l'API seguente con il metodo POST.
https://<manager_ip>/api/v1/systemhealth/plugins
- Caricare il pacchetto compresso del plug-in nel piano di gestione utilizzando l'API seguente con il metodo POST. Il piano di gestione estrae il file caricato ed esegue la convalida richiesta.
/systemhealth/plugins/<plugin-id>/files/<file-name>/data
Nota: La dimensione massima consentita per il file compresso del plug-in è 500k. - Creare un gruppo dei nodi di trasporto richiesti come membri utilizzando l'API seguente con il metodo POST.
/<manager_ip>/api/v1/ns-groups
- Applicare il profilo del plug-in al gruppo di nodi creando una nuova configurazione del servizio utilizzando l'API seguente. Il framework di configurazione del servizio invia il contenuto del plug-in al gruppo di nodi.
https://<manager_ip>/api/v1/service-configs
Per ulteriori informazioni sulle API, vedere la Guida dell'API di NSX.
Ottenere lo stato del plug-in
Una volta che è in esecuzione, il plug-in dinamico carica automaticamente lo stato sul piano di gestione tramite il canale di messaggio esistente. Il piano di gestione aggrega le informazioni sullo stato del plug-in e le archivia nel database. Per ottenere lo stato di tutti i plug-in per ciascun nodo, utilizzare l'API seguente con il metodo GET.
https://<manager_ip>/api/v1/systemhealth/plugins/status/<transport_node_id>
Esempio di richiesta:
GET https://<manager_ip>/api/v1/systemhealth/plugins/status/a257b981-1a1c-4b95-b16c-8646
{ "result_count":1, "results": [ { "id": "72e1bd4b-6df6-42d0-9c59-a1c31312c9f1", "name": "health-check-compute", "status": "NORMAL", "detail": "" } ] }
Disinstallare un plug-in dinamico
Per disinstallare un plug-in, rimuovere la configurazione servizio utilizzando l'API seguente.
https://<manager_ip>/api/v1/service-configs/<service_config_id>
Altre API per la gestione dei plug-in
Nella tabella seguente sono elencate le API che permettono di gestire i plug-in dinamici. Per ulteriori informazioni sulle API, vedere la Guida dell'API di NSX.
Attività | Metodo | API |
---|---|---|
Eliminare un plug-in | ELIMINA | /systemhealth/plugins/<plugin-id> |
Creare un profilo di integrità del sistema | POST | /systemhealth/profiles |
Guardare lo stato del plug-in |
GET | /systemhealth/plugins/status/<node-id> |
Abilitare il plug-in | L'abilitazione di un plug-in prevede un processo in due passaggi, descritto di seguito:
|
|
Modificare l'intervallo di plug-in | POST | La modifica dell'intervallo di plug-in è un processo in due passaggi, descritto di seguito:
|
File di plug-in dinamici
Un plug-in dinamico comprende i seguenti file:
- File delle specifiche di installazione
Il file delle specifiche di installazione, manifest.yml, contiene le seguenti informazioni per l'Agente stato del sistema:
- Struttura del plug-in
- Vincoli, se presenti
- Come installare e utilizzare il plug-in
- Restrizioni di sicurezza per lo script di controllo di integrità. Ad esempio, autorizzazioni di cui dispone lo script e file a cui lo script può accedere.
Nella tabella seguente sono elencati i campi specificati in un file manifest.yml.Nome Descrizione Obbligatorio/Facoltativo Esempio classes Specifica le classi necessarie nello script del plug-in. Le classi devono essere specificate nel formato seguente. '<nome_modulo>.<nome_classe>'
Facoltativo classes: ['datetime.datetime','datetime.date']
modules Specifica i moduli necessari nello script del plug-in. Facoltativo modules: ['random', 'math'] plugin Specifica la struttura del plug-in nel modo seguente:
config: nome file di configurazione
script: nome file script
Obbligatorio plugin:
config: plugin.cfg.yml
script: plugin.py
version Specifica le versioni di NSX in cui è possibile installare questo plug-in. Obbligatorio version: '^3\.1\.[0-9.]+'
node_type Specifica i tipi di nodo di NSX in cui è possibile installare questo plug-in. I tipi di nodo disponibili sono:
- nsx-esx
- nsx-bms
- nsx-edge
Obbligatorio node_type: ['nsx-esx']
metrics Specifica le metriche che possono essere utilizzate nello script del plug-in. Facoltativo metrics: ['nsx.host.host-metrics']
precondition Specifica la condizione preliminare per il plug-in. La condizione preliminare disponibile è wavefront.
Nota: Questo campo è applicabile solo in un ambiente VMware Cloud (VMC).Facoltativo precondition: ['wavefront']
Non utilizzare i seguenti moduli integrati:-
os
-
subprocess
-
sys
-
multiprocessing
-
importlib
Nella tabella seguente sono elencate le interfacce che è necessario utilizzare al posto delle funzioni integrate dei rispettivi moduli. Queste interfacce vengono fornite dal sistema. È possibile utilizzarle direttamente senza specificarne il modulo o la classe nel file manifest.yml.Esempio di file manifest.yml.Modulo Funzione integrata Interfaccia sostitutiva datetime datetime.date.strftime(self, fmt) datetime_date_strftime(dt, fmt)
:param dt: date instance
:param fmt: format string
datetime datetime.date.today() datetime_date_today() sys sys.getrecursionlimit() sys_getrecursionlimit() sys sys.getrefcount(object) sys_getrefcount(object) sys sys.getsizeof(object, default) sys_getsizeof(object, default) sys sys.maxsize sys_maxsize sys sys.path sys_path # specify classes needed in plugin script classes: ['datetime.datetime','datetime.date'] # specify modules needed in plugin script modules: ['random', 'math'] # plugin structure plugin: config: plugin.cfg.yml script: plugin.py # specify nsx versions on which this plugin can be installed version: '^3\.1\.[0-9.]+' # specify nsx node type where this plugin can be installed node_type: ['nsx-esx'] # specify metrics which can be consumed in plugin script metrics: ['nsx.host.host-metrics'] # specify precondition for plugin precondition: ['wavefront']
- File di profilo predefinito
Il file di profilo predefinito, plugin.cfg.yml, viene utilizzato per configurare il comportamento del plug-in, ad esempio la frequenza di esecuzione dello script di controllo di integrità. Per modificare le configurazioni predefinite, è possibile creare un profilo SHA per un plug-in dinamico specifico e applicarlo al nodo di trasporto tramite il gruppo NS utilizzando il piano di gestione per CCP nel canale NestDB.
Nella tabella seguente sono elencati i campi specificati in un file plugin.cfg.yml.Nome Descrizione Obbligatorio/Facoltativo Esempio CHECK_INTERVAL Specifica l'intervallo predefinito, espresso in secondi, per l'esecuzione dello script del plug-in. Obbligatorio CHECK_INTERVAL: 20 ENABLE Specifica se il plug-in è abilitato per impostazione predefinita. Obbligatorio ENABLE: true Esempio di file plugin.cfg.yml.# Required field - default interval (unit: second) between plugin script executions. CHECK_INTERVAL: 20 # Required field - whether plugin is enabled by default ENABLE: true # Plugin user can add other fields as below if needed to control plugin script logic. EXPORT_DATA: true
- Script del controllo di integrità
Un file di script del controllo di integrità, plugin.py, contiene uno script Python per verificare lo stato di integrità di un nodo di trasporto.
Nella tabella seguente sono elencate le variabili e le funzioni definite dal sistema che si possono utilizzare e i dati che si possono leggere in un file plugin.py.Variabile/Dati/Funzione Descrizione Tipo Esempio logger Scrive informazioni di registro in syslog. La variabile definita dal sistema esistente, logger, può essere utilizzata direttamente nello script del plug-in. Al registro di output vengono aggiunti nome e ID del plug-in, come prefisso, come illustrato nel seguente output di esempio.
2020-10-28T10:47:43Z nsx-sha: NSX 2101378 - [nsx@6876 comp="nsx-esx" subcomp="nsx-sha" username="root" level="INFO"] [name:hl-esx-002-04][id:a3eb14f1-d185-4bc7-bfaa-6cf888bbeb22] dynamic plugin - not export data
Variabile logger.info("this is a demo log")
data_store Il dizionario definito dal sistema esistente utilizzato per recuperare i dati forniti dal sistema. Ad esempio, profile, metric e host_id.
Variabile profile = data_store['profile']
profile I dati del profilo sono un dizionario analizzato dal profilo predefinito (plugin.cfg.yml) o dal profilo SHA effettivo (applicato dall'utente tramite l'API di Manager) letto da data_store. Il formato è il seguente:
{'ENABLE': True, 'CHECK_INTERVAL': 20, 'EXPORT_DATA': True}
Dati profile = data_store['profile']
metric È un dizionario con 'value' e 'timestamp' letti da data_store. Il formato è il seguente:
data_store['metrics'][<nome_metrica>]
Dove
La prima chiave deve essere 'metrics'.
La seconda chiave è il nome di una metrica esistente.
Dati metric = data_store['metrics']['nsx.host.host-metrics']
metric is: {
‘value’:XXX, <== the collected data of the metric
‘timestamp’: XXX <== timestamp of the data collected
}
Nota: la prima esecuzione del plug-in potrebbe non restituire una metrica in quanto attualmente con il plug-in in esecuzione una metrica viene raccolta in modo asincrono, pertanto la metrica potrebbe non essere stata raccolta nella prima esecuzione del plug-in.
host_id È un'istanza della classe uuid.UUID letta da data_store. Dati host_id = data_store['host_id']
run_command Questa funzione esegue i comandi in formato elenco. Il formato è il seguente. run_command(cmd, timeout=4)
Dove
- cmd: comandi da eseguire. Deve essere nel formato dell'elenco come nell'esempio.
- timeout: timeout per l'attesa del risultato del comando. Il timeout predefinito è 4 secondi. Il timeout non deve essere impostato su un valore superiore a 20 s.
Questa funzione restituisce il risultato dell'esecuzione del comando.
Funzione cmd = ['nsxdp-cli', 'ipfix', 'settings', 'granular', 'get', '--dvs-alias', 'nsxvswitch', '--dvport=dafa09ca-33ed-4e04-ae3d-1c53305d5fe6']
res = run_command(cmd)
Exportdata Questa funzione esporta i dati in wavefront. Attualmente un plug-in dinamico supporta l'esportazione solo in wavefront. Nota: Questa funzione è applicabile solo in un ambiente VMware Cloud (VMC).Exportdata: ExportData(data={}, exports=[], source=host_uuid)
Dove
data: dati da esportare; i dati di devono essere in formato dizionario come nell'esempio.
exports: elenco di destinazione per l'esportazione. In HL, è supportato solo wavefront nella destinazione. È obbligatorio.
source: stringa di origine per l'esportazione. È utile solo per la destinazione wavefront. È facoltativo e il valore predefinito è NSX host_uuid.
La funzione non restituisce alcun valore.
Funzione Exportdata(data={'esx.plugin.stats': {'stats': {'gc-esx-001': data}}}, exports=['wavefront'])
Esempio di file plugin.py.def report_test_data(edge_service_status): if edge_service_status == 'running': data = 2 else: data = 3 # examples on how to report data to wavefront. Exportdata(data={'esx.plugin.stats': {'stats': {'esx-dynamic-plugin-001': data}}}, exports=['wavefront']) def run(): # examples on how to write log. logger.debug("this is a debug message!") logger.info("this is a demo message!") # examples on how to use specified module in manifest. Take 'random' as an example. s_res = random.randint(1,10) logger.warn("random.randint(1,10)=%s", s_res) # examples on how to use specified class in manifest. Take 'datetime' and 'date' as an example. logger.info('date.ctime(datetime.now()):{}'.format(date.ctime(datetime.now()))) # examples on how to run cmd via interface run_command cmd = ['nsxdp-cli', 'ipfix', 'settings', 'granular', 'get', '--dvs-alias', 'nsxvswitch', '--dvport=dafa09ca-33ed-4e04-ae3d-1c53305d5fe6'] c_res = run_command(cmd) logger.error("run_command(cmd) res:%s", c_res) # examples on how to read existing metrics from data_store m_res = data_store['metrics']['nsx.host.host-metrics'] # examples on how to read effective profile from data_store profile = data_store['profile'] logger.error("data_store['metrics']['nsx.host.host-metrics']:%s, profile:%s", m_res, profile) # examples on how to read host_id from data_store host_id = data_store['host_id'] logger.info('host_id:{}'.format(host_id)) if profile['EXPORT_DATA']: report_test_data('running') logger.info("dynamic plugin - exported data to wavefront") else: logger.info("dynamic plugin - not export data ") # examples on how to use substitute interfaces for sys. logger.info("sys_path:{}".format(sys_path)) logger.info("sys_getsizeof(1):{}".format(sys_getsizeof(1))) logger.info("sys_getrefcount(cmd):{}".format(sys_getrefcount(cmd))) logger.info("sys_maxsize:{}".format(sys_maxsize)) logger.info("sys_getrecursionlimit():{}".format(sys_getrecursionlimit())) # examples on how to use substitute interfaces for datetime. today = datetime_date_today() logger.info("datetime today:{}".format(today)) logger.info("datetime_date_strftime now:{}".format(datetime_date_strftime(datetime.now(), '%H:%M'))) logger.info('date.ctime(today):{}'.format(date.ctime(today))) run()