動態外掛程式是可為任何支援的傳輸節點 (例如 ESXi 主機) 建立的自訂外掛程式,用來檢查節點的健全狀況。

動態外掛程式可在執行階段安裝。它會執行以下功能:

  • 為受影響的傳輸節點寫入系統記錄。
  • 透過 run_command() 函數執行命令或 CLI。
  • 讀取現有度量。
  • 將資料匯出至 Wavefront (僅限 VMware Cloud 環境中)。

如果您具有管理 NSX 的專業技能,則可以建立動態外掛程式。動態外掛程式也可由 VMware 支援建立。建立的外掛程式必須提交至 GIT 存放庫,以進行驗證。提交之前,該外掛程式必須先經過檢閱和測試。

安全性管理

完成外掛程式的檢閱和測試後,外掛程式、其測試結果和程式碼變更便會提交以供在 GIT 存放庫中進行驗證。使用下列 GIT 存放庫詳細資料

  • GitLab:https://gitlab.eng.vmware.com/core-build/nsbu-sha-plugin
  • 產品:nsx-sha-plugins
  • Gitreview 支援:由 VMware 團隊執行

在外掛程式經過驗證和核准之後,GIT 存放庫便會予以認可。建立新組建時,系統會建置並簽署所有動態外掛程式。每個外掛程式會單獨進行封裝和簽署。您可以從組建的已發佈檔案取得所需的外掛程式。

將已簽署的外掛程式上傳至管理平面時,管理平面會使用公開金鑰來驗證簽章,並確認此外掛程式有效。驗證外掛程式後,外掛程式的檔案會透過管理平面與中央控制平面 (CCP) 之間以及 CCP 與主機之間的安全通道推送至目的地主機。

如果系統健全狀況代理程式 (SHA) 執行個體已重新啟動,則會再次從管理平面取得外掛程式檔案。由於所有檔案皆透過安全通道發佈,並且不使用任何暫存檔案,因此可以防止駭客修改指令碼的風險。

此外,為了防止有害程式碼的風險,SHA 會先使用 RestrictedPython 來檢查外掛程式 python 指令碼,然後再執行該指令碼。

版本管理

外掛程式可能會基於較新版本 NSX 中不支援的命令或工具,因此每個自訂外掛程式必須在 manifest.yml 檔案中定義支援的 NSX 版本。對於所有支援的版本,版本應為 REGEX 字串。主機端上的 SHA 會檢查自訂外掛程式的版本,並僅執行 REGEX 符合的版本。

建議的版本管理原則為:
  • 定義主要版本支援的 NSX 版本。

    假設多數命令和工具在相同主要版本中的次要版本之間不會變更,則下列方法是為所有次要版本定義版本的建議方式。

    例如,

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

  • 發佈新的主要版本時,應檢閱所有提交的動態外掛程式。
  • 當相關的命令或工具變更時,外掛程式寫入器必須更新指令碼。

安裝動態外掛程式

已安裝外掛程式的傳輸節點或 Edge 節點必須至少有 30 MB 的記憶體空間。此外,請注意,您最多僅能安裝 10 個外掛程式。外掛程式計數到達 10 個後,任何外掛程式的進一步安裝都會失敗。

若要安裝外掛程式,請執行下列工作:
  1. 在 GIT 存放庫中建立動態外掛程式檔案。如需有關外掛程式檔案的詳細資訊,請參閱〈動態外掛程式檔案〉一節。
  2. 在 GIT 存放庫中觸發產品組建,以產生動態外掛程式檔案的壓縮套件並下載該套件。
  3. 透過使用下列 API 搭配 POST 方法來建立動態外掛程式。

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

  4. 使用下列 API 搭配 POST 方法,將外掛程式壓縮的套件上傳至管理平面。管理平面會擷壓縮上傳的檔案,並執行所需的驗證。

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

    備註: 外掛程式壓縮檔案的大小上限為 500k。
  5. 透過使用下列 API 搭配 POST 方法,來建立具有所需傳輸節點作為成員的節點群組。

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

  6. 透過使用下列 API 來建立新的服務組態,將外掛程式設定檔套用至節點群組。服務組態架構會將外掛程式內容傳送至節點群組。

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

如需有關 API 的詳細資訊,請參閱NSX API 指南》說明文件。

取得外掛程式狀態

動態外掛程式執行後,它會透過現有的訊息通道自動將狀態上傳至管理平面。管理平面會彙總外掛程式狀態資訊,並將其儲存至資料庫。若要取得每個節點上所有外掛程式的狀態,請使用下列 API 搭配 GET 方法。

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

要求範例:

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": ""
 }
 ]
 }

解除安裝動態外掛程式

若要解除安裝外掛程式,請使用下列 API 移除服務組態。

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

用於管理外掛程式的其他 API

下表列出用於管理動態外掛程式的 API。如需有關 API 的詳細資訊,請參閱NSX API 指南》說明文件。

工作 方法 API
刪除外掛程式 刪除 /systemhealth/plugins/<plugin-id>
建立系統健全狀況設定檔 POST /systemhealth/profiles

監控外掛程式狀態

GET /systemhealth/plugins/status/<node-id>
啟用外掛程式 啟用外掛程式為兩個步驟的程序,如下所示:
  1. 使用下列 API 將 enabled 內容設定為 truefalse

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

  2. 使用下列 API 將 SHA 設定檔套用至 NS 群組。

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

變更外掛程式間隔 POST

變更外掛程式間隔為兩個步驟的程序,如下所示:

  1. 使用下列 API 設定 config 內容。

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

  2. 使用下列 API 將 SHA 設定檔套用至 NS 群組。

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

動態外掛程式檔案

動態外掛程式包含下列檔案:

  • 安裝規格檔案
    安裝規格檔案 manifest.yml,包含系統健全狀況代理程式的下列資訊:
    • 外掛程式結構
    • 限制 (如果有)
    • 如何安裝和使用外掛程式
    • 健全狀況檢查指令碼的安全性限制。例如,指令碼具有的權限和指令碼可存取的檔案。
    下表列出 manifest.yml 檔案中指定的欄位。
    名稱 說明 必要/選用 範例
    classes 指定外掛程式指令碼中所需的類別。

    類別必須以下列格式指定。「<module_name>.<class_name>」

    選擇性 classes: ['datetime.datetime','datetime.date']
    modules 指定外掛程式指令碼中所需的模組。 選擇性 modules: ['random', 'math']
    plugin

    指定外掛程式結構,如下所示:

    config: 組態檔案名稱

    script: 指令碼檔案名稱

    必要

    plugin:

    config: plugin.cfg.yml

    script: plugin.py

    version 指定可在其上安裝此外掛程式的 NSX 版本。 必要 version: '^3\.1\.[0-9.]+'
    node_type

    指定可在其中安裝此外掛程式的 NSX 節點類型。可用節點類型為:

    • nsx-esx
    • nsx-bms
    • nsx-edge
    必要 node_type: ['nsx-esx']
    metrics 指定可在外掛程式指令碼中使用的度量。 選擇性 metrics: ['nsx.host.host-metrics']
    precondition

    指定外掛程式的先決條件。可用的先決條件為 Wavefront。

    備註: 此欄位僅適用 VMware Cloud (VMC) 環境。
    選擇性 precondition: ['wavefront']
    請勿使用下列內建模組:
    • os

    • subprocess

    • sys

    • multiprocessing

    • importlib

    下表列出您必須使用的介面,以取代個別模組的內建函數。這些介面是由系統提供。您可以直接使用它們,而不需在 manifest.yml 檔案中指定其模組/類別。
    模組 內建函數 替代介面
    datetime datetime.date.strftime(self, fmt)

    datetime_date_strftime(dt, fmt)

    :param dt: 日期執行個體

    :param fmt: 格式字串

    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
    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']
  • 預設設定檔檔案

    預設設定檔檔案 plugin.cfg.yml 可用來設定外掛程式行為,例如健全狀況檢查指令碼的執行頻率。若要變更預設組態,您可以為特定動態外掛程式建立 SHA 設定檔,並透過 NS 群組將其套用至傳輸節點,方法是使用管理平面到 CCP 到 NestDB 通道。

    下表列出 plugin.cfg.yml 檔案中指定的欄位。
    名稱 說明 必要/選用 範例
    CHECK_INTERVAL 指定外掛程式指令碼執行的預設間隔 (以秒為單位)。 必要 CHECK_INTERVAL: 20
    ENABLE 指定是否依預設啟用外掛程式。 必要 ENABLE: true
    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
  • 健全狀況檢查指令碼

    健全狀況檢查指令碼檔案 plugin.py 包含 python 指令碼,用於檢查傳輸節點的健全狀況狀態。

    下表列出可供使用的系統定義變數和函數,以及可在 plugin.py 檔案中讀取的資料。
    變數/資料/函數 說明 類型 範例
    logger 在 Syslog 中寫入記錄資訊。現有的系統定義變數 logger 可直接用於外掛程式指令碼。

    輸出記錄的首碼會是外掛程式名稱和識別碼,如下列範例輸出所示。

    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

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

    用於擷取系統所提供資料的現有系統定義字典。例如,profile、metric 和 host_id。

    變數 profile = data_store['profile']
    profile

    設定檔資料是從預設設定檔 (plugin.cfg.yml) 或從 data_store 所讀取有效 SHA 設定檔 (使用者透過 Manager API 套用) 剖析的字典。它具有下列格式:

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

    資料 profile = data_store['profile']
    metric

    度量是從 data_store 讀取、具有「value」和「timestamp」的字典。它具有下列格式:

    data_store['metrics'][<metric_name>]

    其中,

    第一個索引鍵必須為「metrics」。

    第二個索引鍵是現有的度量名稱。

    資料

    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

    }

    附註:外掛程式的第一次執行可能不會傳回度量,由於目前收集度量與外掛程式執行並非同步進行,因此可能無法在外掛程式第一次執行時收集度量。

    host_id host_id 是從 data_store 所讀取類別 uuid.UUID 的執行個體。 資料 host_id = data_store['host_id']
    run_command 此函數會以清單格式執行命令。它具有下列格式。

    run_command(cmd, timeout=4)

    其中,

    • cmd:要執行的命令。必須採用如範例所示的清單格式。
    • timeout:等待命令結果的逾時。預設逾時為 4 秒。不應將逾時設定為大於 20 秒。

    此函數會傳回命令執行結果。

    函數

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

    res = run_command(cmd)

    Exportdata 此函數會將資料匯出至 Wavefront。目前,動態外掛程式僅支援匯出至 Wavefront。
    備註: 此函數僅適用 VMware Cloud (VMC) 環境。
    它具有下列格式:

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

    其中,

    data:要匯出的資料;資料應採用如範例所示的字典格式。

    exports:匯出的目的地清單。在 HL 中,目的地僅支援 Wavefront。必要。

    source:匯出的來源字串。這僅適用 Wavefront 目的地。此為選用,預設值為 NSX host_uuid。

    此函數不會傳回任何值。

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