동적 플러그인은 ESXi 호스트와 같은 지원되는 전송 노드에 대해 노드의 상태를 확인하기 위해 생성할 수 있는 사용자 지정 플러그인입니다.

동적 플러그인은 런타임에 설치할 수 있습니다. 다음 기능을 수행합니다.

  • 영향을 받는 전송 노드에 대한 시스템 로그를 작성합니다.
  • run_command() 함수를 통해 명령 또는 CLI를 실행합니다.
  • 기존 메트릭을 읽습니다.
  • 데이터를 wavefront로 내보냅니다(VMware Cloud 환경에서만).

NSX 관리에 대한 전문 지식이 있는 경우 동적 플러그인을 생성할 수 있습니다. VMware 지원 서비스에서 동적 플러그인을 생성할 수도 있습니다.

보안 관리

플러그인을 생성한 후에는 VMware 엔지니어링 팀에 제출하여 플러그인의 코드 검토를 받아야 합니다. 검토가 완료되면 엔지니어링 팀은 검증을 위해 GIT 저장소에 플러그인, 검토 테스트 결과 및 코드 변경 내용을 제출합니다. 동적 플러그인이 유효성 검사를 통과하면 커밋되고 GIT 저장소에서 제품 빌드가 트리거되어 모든 동적 플러그인을 게시할 새 빌드를 생성합니다. 다음 GIT 저장소 세부 정보가 사용됩니다.

  • GitLab: https://gitlab.eng.vmware.com/core-build/nsbu-sha-plugin
  • 제품: nsx-sha-plugins
  • Gitreview 지원: VMware 팀에서 수행

빌드의 게시된 파일에서 필요한 플러그인을 가져올 수 있습니다. 서명된 플러그인을 관리부에 업로드하면 관리부는 공개 키를 사용하여 서명을 확인하고 이 플러그인이 유효한지 확인합니다. 관리부에서 플러그인을 검증한 후에는 관리부와 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

  • 새 주요 릴리스가 게시되면 VMware 엔지니어링 팀은 제출된 모든 동적 플러그인을 검토해야 합니다.
  • 플러그인 작성자는 관련 명령이나 도구가 변경될 때 스크립트를 업데이트해야 합니다.

동적 플러그인 설치

플러그인이 설치된 전송 노드 또는 Edge 노드에는 최소 30MB의 메모리 공간이 있어야 합니다. 또한 플러그인은 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>
플러그인 사용 플러그인을 사용하도록 설정하는 프로세스는 다음과 같이 2단계로 진행됩니다.
  1. 다음 API를 사용하여 enabled 속성을 true 또는 false로 설정합니다.

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

  2. 다음 API를 사용하여 SHA 프로파일을 NS 그룹에 적용합니다.

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

플러그인 간격 변경 POST

플러그인 간격을 변경하는 프로세스는 다음과 같이 2단계로 진행됩니다.

  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: config file name

    script: script file name

    필요

    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 환경에만 적용됩니다.
    선택 사항 precondition: ['wavefront']
    다음과 같은 기본 제공 모듈을 사용하지 마십시오.
    • os

    • subprocess

    • sys

    • multiprocessing

    • importlib

    다음 표에는 각 모듈의 기본 제공 기능 대신 사용해야 는 인터페이스가 나열되어 있습니다. 이러한 인터페이스는 시스템으로 제공됩니다. manifest.yml 파일에서 module/class를 지정하지 않고 직접 사용할 수 있습니다.
    모듈 기본 제공 기능 대체 인터페이스
    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
    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 프로파일을 생성한 다음에 CCP-NestDB 채널에 관리부를 사용하여 NS 그룹을 통해 이 프로파일을 전송 노드에 적용하면 됩니다.

    다음 표에는 plugin.cfg.yml 파일에 지정된 필드가 나열되어 있습니다.
    이름 설명 필수/선택 사항
    CHECK_INTERVAL 플러그인 스크립트 실행에 대한 기본 간격(초)을 지정합니다. 필요 CHECK_INTERVAL: 20
    사용 플러그인이 기본적으로 사용하도록 설정되어 있는지 여부를 지정합니다. 필요 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는 플러그인 스크립트에서 직접 사용할 수 있습니다.

    다음 샘플 출력에 표시된 것처럼 출력 로그에는 플러그인 이름 및 ID 접두사가 붙습니다.

    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 프로파일(관리자 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 환경에만 적용됩니다.
    형식은 다음과 같습니다.

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