Un plug-in dynamique est un plug-in personnalisé que vous pouvez créer pour tous les nœuds de transport pris en charge, comme un hôte ESXi, pour vérifier la santé du nœud.

Un plug-in dynamique peut être installé au moment de l'exécution. Il exécute les fonctions suivantes :

  • Rédiger un journal système pour le nœud de transport affecté.
  • Exécuter une commande ou une interface de ligne de commande via la fonction run_command().
  • Lire les mesures existantes.
  • Exporter les données vers Wavefront (uniquement dans un environnement VMware Cloud).

Vous pouvez créer un plug-in dynamique si vous disposez d'une expertise dans la gestion de NSX. Un plug-in dynamique peut également être créé par le support VMware. Un plug-in créé doit être envoyé à un référentiel GIT pour validation. Avant l'envoi, le plug-in doit être examiné et testé.

Gestion de la sécurité

Après l'examen et les tests, le plug-in, ses résultats de test et les modifications de code sont envoyés pour validation dans un référentiel GIT. Utilisez les détails du référentiel GIT suivant :

  • GitLab : https://gitlab.eng.vmware.com/core-build/nsbu-sha-plugin
  • Produit : nsx-sha-plugins
  • Prise en charge de Gitreview : effectuée par l'équipe VMware

Une fois le plug-in validé et approuvé, il est transmis dans le référentiel GIT. Tous les plug-ins dynamiques sont générés et signés lors de la création d'un nouveau build. Chaque plug-in est empaqueté et signé séparément. Vous pouvez obtenir le plug-in requis dans les fichiers publiés du build.

Lorsqu'un plug-in signé est téléchargé dans le plan de gestion, ce dernier utilise une clé publique pour vérifier la signature et confirmer que ce plug-in est valide. Une fois le plug-in validé, les fichiers du plug-in sont envoyés aux hôtes de destination via les canaux sécurisés entre le plan de gestion et le plan de contrôle central (CCP) et entre le CCP et les hôtes.

Si une instance de l'agent de santé système (SHA) est redémarrée, elle récupère les fichiers de plug-in à partir du plan de gestion. Comme tous les fichiers sont publiés via des canaux sécurisés et qu'aucun fichier temporaire n'est utilisé, le risque de modification des scripts par des pirates est évité.

De plus, pour éviter les risques de code nuisible, SHA utilise RestrictedPython pour vérifier le script Python du plug-in avant d'exécuter le script.

Gestion des versions

Un plug-in peut être basé sur une commande ou un outil qui n'est pas pris en charge dans les versions ultérieures de NSX. Par conséquent, chaque plug-in personnalisé doit définir la version de NSX prise en charge dans le fichier manifest.yml. La version doit être une chaîne REGEX pour toutes les versions prises en charge. SHA sur le côté hôte vérifie la version du plug-in personnalisé et exécute uniquement les correspondances REGEX.

Les stratégies recommandées de gestion des versions sont les suivantes :
  • Définissez la version de NSX prise en charge d'une version majeure.

    Comme la plupart des commandes et des outils ne changent pas entre les versions mineures d'une même version majeure, la méthode suivante est suggérée pour définir la version de toutes les versions mineures.

    Par exemple,

    version: ^2\.5\.[0-9.]+ <== The custom plugin supporting all NSX 2.5 releases

  • Lorsqu'une nouvelle version majeure est publiée, tous les plug-ins dynamiques envoyés doivent être examinés.
  • L'auteur du plug-in doit mettre à jour le script lorsque les commandes ou les outils associés sont modifiés.

Installer un plug-in dynamique

Le nœud de transport ou le nœud Edge sur lequel le plug-in est installé doit disposer d'un minimum de 30 Mo d'espace de mémoire. De plus, notez que vous ne pouvez installer que 10 plug-ins maximum. Lorsque le nombre de plug-ins atteint 10, toute installation ultérieure d'un plug-in échouera.

Pour installer le plug-in, effectuez les tâches suivantes :
  1. Créez les fichiers de plug-in dynamique dans le référentiel GIT. Pour plus d'informations sur les fichiers de plug-in, reportez-vous à la section Fichiers de plug-in dynamique.
  2. Déclenchez le build du produit dans le référentiel GIT pour générer le module zippé des fichiers de plug-in dynamique et téléchargez le module.
  3. Créez le plug-in dynamique à l'aide de l'API suivante avec la méthode POST.

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

  4. Chargez le module zippé du plug-in dans le plan de gestion à l'aide de l'API suivante avec la méthode POST. Le plan de gestion extrait le fichier chargé et effectue la validation requise.

    /systemhealth/plugins/<plugin-id>/files/<file-name>/data

    Note : La taille maximale du fichier zippé de plug-in est de 500 Ko.
  5. Créez un groupe de nœuds avec les nœuds de transport requis en tant que membres à l'aide de l'API suivante avec la méthode POST.

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

  6. Appliquez le profil de plug-in au groupe de nœuds en créant une nouvelle configuration de service à l'aide de l'API suivante. La structure de configuration de service envoie le contenu du plug-in au groupe de nœuds.

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

Pour plus d'informations sur les API, reportez-vous à la documentation Guide des API de NSX.

Obtenir l'état du plug-in

Une fois que le plug-in dynamique est en cours d'exécution, il télécharge automatiquement l'état sur le plan de gestion via le canal de message existant. Le plan de gestion agrège les informations d'état du plug-in et les stocke dans la base de données. Pour obtenir l'état de tous les plug-ins sur chaque nœud, utilisez l'API suivante avec la méthode GET.

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

Exemple de demande :

GET https://<manager_ip>/api/v1/systemhealth/plugins/status/a257b981-1a1c-4b95-b16c-8646

Exemple de réponse :
{
 "result_count":1,
 "results": [
 {
 "id": "72e1bd4b-6df6-42d0-9c59-a1c31312c9f1",
 "name": "health-check-compute",
 "status": "NORMAL",
 "detail": ""
 }
 ]
 }

Désinstaller un plug-in dynamique

Pour désinstaller un plug-in, supprimez la configuration du service à l'aide de l'API suivante.

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

Autres API pour la gestion des plug-ins

Le tableau suivant répertorie les API pour gérer les plug-ins dynamiques. Pour plus d'informations sur les API, reportez-vous à la documentation Guide des API de NSX.

Tâche Méthode API
Supprimer un plug-in SUPPRIMER /systemhealth/plugins/<plugin-id>
Créer un profil de santé du système POST /systemhealth/profiles

Surveiller l'état du plug-in

GET /systemhealth/plugins/status/<node-id>
Activer le plug-in Pour activer un plug-in, les deux étapes suivantes sont requises :
  1. Utilisez l'API suivante pour définir la propriété enabled sur true ou false.

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

  2. Utilisez l'API suivante pour appliquer le profil SHA au groupe NS.

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

Modifier l'intervalle du plug-in POST

Pour modifier l'intervalle du plug-in, les deux étapes suivantes sont requises :

  1. Utilisez l'API suivante pour définir la propriété config.

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

  2. Utilisez l'API suivante pour appliquer le profil SHA au groupe NS.

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

Fichiers de plug-in dynamique

Un plug-in dynamique comprend les fichiers suivants :

  • Fichier de spécification d'installation
    Le fichier de spécification d'installation, manifest.yml, contient les informations suivantes pour l'agent de santé du système :
    • Structure du plug-in
    • Contraintes, le cas échéant
    • Comment installer et utiliser le plug-in
    • Restrictions de sécurité pour le script de contrôle de santé. Par exemple, les autorisations dont dispose le script et les fichiers auxquels il peut accéder.
    Le tableau suivant répertorie les champs qui sont spécifiés dans un fichier manifest.yml.
    Nom Description Requis/facultatif Exemple
    classes Spécifie les classes nécessaires dans le script de plug-in.

    Les classes doivent être spécifiées au format suivant. '<module_name>.<class_name>'

    Facultatif classes: ['datetime.datetime','datetime.date']
    modules Spécifie les modules nécessaires dans le script de plug-in. Facultatif modules: ['random', 'math']
    plugin

    Spécifie la structure du plug-in comme suit :

    config :nom du fichier de configuration

    script : nom du fichier de script

    Requis

    plugin:

    config: plugin.cfg.yml

    script: plugin.py

    version Spécifie les versions de NSX sur lesquelles ce plug-in peut être installé. Requis version: '^3\.1\.[0-9.]+'
    node_type

    Spécifie les types de nœuds NSX sur lesquels ce plug-in peut être installé. Les types de nœuds disponibles sont les suivants :

    • nsx-esx
    • nsx-bms
    • nsx-edge
    Requis node_type: ['nsx-esx']
    metrics Spécifie les mesures qui peuvent être utilisées dans le script de plug-in. Facultatif metrics: ['nsx.host.host-metrics']
    precondition

    Spécifie la condition préalable pour le plug-in. La condition préalable disponible est Wavefront.

    Note : Ce champ s'applique uniquement dans un environnement VMware Cloud (VMC).
    Facultatif precondition: ['wavefront']
    N'utilisez pas les modules intégrés suivants :
    • os

    • subprocess

    • sys

    • multiprocessing

    • importlib

    Le tableau suivant répertorie les interfaces que vous devez utiliser à la place des fonctions intégrées des modules respectifs. Ces interfaces sont fournies par le système. Vous pouvez les utiliser directement sans spécifier leur module/classe dans le fichier manifest.yml.
    Module Fonction intégrée Interface de remplacement
    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
    Exemple de fichier 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']
  • Fichier de profil par défaut

    Le fichier de profil par défaut, plugin.cfg.yml, est utilisé pour configurer le comportement du plug-in, tel que la fréquence d'exécution du script de contrôle de santé. Pour modifier les configurations par défaut, vous pouvez créer un profil SHA pour un plug-in dynamique spécifique et l'appliquer au nœud de transport via le groupe NS à l'aide du plan de gestion vers le CCP vers le canal NestDB.

    Le tableau suivant répertorie les champs spécifiés dans un fichier plugin.cfg.yml.
    Nom Description Requis/facultatif Exemple
    CHECK_INTERVAL Spécifie l'intervalle par défaut en secondes pour l'exécution du script de plug-in. Requis CHECK_INTERVAL: 20
    ACTIVER Spécifie si le plug-in est activé par défaut. Requis ENABLE: true
    Exemple de fichier 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 de contrôle de santé

    Un fichier de script de contrôle de santé, plugin.py, contient un script Python pour vérifier l'état de santé d'un nœud de transport.

    Le tableau suivant répertorie les variables et fonctions définies par le système qui peuvent être utilisées et les données pouvant être lues dans un fichier plugin.py.
    Variable/Données/Fonction Description Type Exemple
    logger Écrit les informations de journal dans Syslog. La variable existante définie par le système, logger, peut être utilisée directement dans le script de plug-in.

    Le nom du journal de sortie est précédé du nom et de l'ID du plug-in, comme indiqué dans l'exemple de sortie suivant.

    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

    Variable logger.info("this is a demo log")
    data_store

    Dictionnaire existant défini par le système qui est utilisé pour extraire les données transmises par le système. Par exemple, profile, metric et host_id.

    Variable profile = data_store['profile']
    profile

    Les données de profil sont un dictionnaire analysé à partir du profil par défaut (plugin.cfg.yml) ou du profil SHA effectif (appliqué via l'API Manager) qui est lu depuis data_store. Son format est le suivant :

    {'ENABLE': True, 'CHECK_INTERVAL': 20, 'EXPORT_DATA': True}

    Données profile = data_store['profile']
    metric

    La mesure est un dictionnaire avec « value » et « timestamp » qui est lu depuis data_store. Son format est le suivant :

    data_store['metrics'][<metric_name>]

    Où,

    La première clé doit être « metrics ».

    La deuxième clé est un nom de mesure existant.

    Données

    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

    }

    Remarque : la première exécution du plug-in peut ne pas renvoyer de mesure, car actuellement une mesure est collectée de manière asynchrone avec le plug-in en cours d'exécution. Par conséquent, il se peut que la mesure n'ait pas été collectée lors de la première exécution du plug-in.

    host_id Host_id est une instance de classe uuid.UUID lue depuis data_store. Données host_id = data_store['host_id']
    run_command Cette fonction exécute les commandes sous forme de liste. Son format est le suivant.

    run_command(cmd, timeout=4)

    Où,

    • cmd : commandes à exécuter. Doit être sous forme de liste comme dans l'exemple.
    • timeout : délai d'expiration de l'attente du résultat de la commande. Le délai d'expiration par défaut est de 4 s. Le délai d'expiration ne doit pas être supérieur à 20 s.

    Cette fonction renvoie le résultat de l'exécution de la commande.

    Fonction

    cmd = ['nsxdp-cli', 'ipfix', 'settings', 'granular', 'get', '--dvs-alias', 'nsxvswitch', '--dvport=dafa09ca-33ed-4e04-ae3d-1c53305d5fe6']

    res = run_command(cmd)

    Exportdata Cette fonction exporte les données vers Wavefront. Actuellement, un plug-in dynamique prend en charge l'exportation vers Wavefront uniquement.
    Note : Cette fonction s'applique uniquement dans un environnement VMware Cloud (VMC).
    Son format est le suivant :

    Exportdata: ExportData(data={}, exports=[], source=host_uuid)

    Où :

    data : données à exporter ; les données doivent être au format de dictionnaire, par exemple.

    exports : liste de destination pour l'exportation. En HL, ne prend en charge que Wavefront dans la destination. Il est obligatoire.

    source : chaîne source pour l'exportation. Elle est utile uniquement pour la destination Wavefront. Elle est facultative, la valeur par défaut est NSX host_uuid.

    La fonction ne renvoie aucune valeur.

    Fonction Exportdata(data={'esx.plugin.stats': {'stats': {'gc-esx-001': data}}}, exports=['wavefront'])
    Exemple de fichier 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()