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.

I criteri di gestione delle versioni consigliati sono:
  • 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.

Per installare il plug-in, eseguire le seguenti attività:
  1. 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.
  2. Attivare la build del prodotto nel repository GIT per generare il pacchetto compresso dei file di plug-in dinamici e scaricare il pacchetto.
  3. Creare il plug-in dinamico utilizzando l'API seguente con il metodo POST.

    https://<manager_ip>/api/v1/systemhealth/plugins

  4. 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.
  5. Creare un gruppo dei nodi di trasporto richiesti come membri utilizzando l'API seguente con il metodo POST.

    /<manager_ip>/api/v1/ns-groups

  6. 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

Esempio di risposta:
{
 "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:
  1. Utilizzare l'API seguente per impostare la proprietà enabled su true oppure su false.

    https://<manager_ip>/api/v1/systemhealth/profiles/

  2. Utilizzare l'API seguente per applicare il profilo SHA al gruppo NS.

    https://<manager_ip>/api/v1/service-configs

Modificare l'intervallo di plug-in POST

La modifica dell'intervallo di plug-in è un processo in due passaggi, descritto di seguito:

  1. Utilizzare l'API seguente per impostare la proprietà config.

    https://<manager_ip>/api/v1/systemhealth/profiles/

  2. Utilizzare l'API seguente per applicare il profilo SHA al gruppo NS.

    https://<manager_ip>/api/v1/service-configs

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.
    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
    Esempio di file manifest.yml.
    # 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).
    Il formato è il seguente:

    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()