본문 바로가기

Management

[Operations] Fails to install telegraf agent manually on windows server with Korean Language

## vRealize Suite Operations 8.10

 

vROPS에서 Guest OS 내부를 모니터링하기 위해 telegraf agent를 사용할 수 있습니다.

telegraf agent를 설치하기 위해서는 cloud proxy가 필요한데, 정상적으로 telegraf agent가 설치되지 않는 케이스가 있어 이에 대해 공유 드립니다.

 

[구성 환경]

vRealize Operations

vRealize Operations Cloud Proxy

Windows Server(Korean Language)

 

[문제 증상]

우선, telegraf agent가 vRealize Operations Manager UI를 통해 설치가 실패한 후, Windows Server에서 telegraf agent를 수동으로 설치할 때, 다음과 같은 오류가 발생합니다.

PS C:\> powershell -file .\download.ps1  -o install -v 192.168.1.31 -u admin -p P@ssw0rd -d c:\temp -c 192.168.1.32
 
    디렉터리: C:\temp
 
 
Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----     2022-12-16   오전 8:43                arc_install_tmp_dir
Executing install operation...
 
install agents failed at FAILED - runBootstrapOrch. please check logs for more detail.

 

수동 설치 방법은 아래 링크를 참조하시면 됩니다.

Install/Uninstall an Agent Using a Script on a Windows Platform

https://docs.vmware.com/en/vRealize-Operations/8.10/com.vmware.vcom.config.doc/GUID-83B814AC-F9C1-4F19-A0CB-0678AE327BC0.html

 

[분석 내용]

1. 우선, 설치 과정의 로그를 확인하기 위해 agent 설치 파일 다운로드 경로에 있는 uaf_bootstrap.log 파일을 살펴봐야 합니다. 

최초 설치 시작은 powershell을 통해 download.ps1을 실행하는데, 실제로는 download.ps1 내에서 uaf-bootstrap.bat 배치 파일을 호출하기 때문에 uaf_bootstrap.log 파일을 확인하면 됩니다.

download.ps1
 
####################
# Parameter Check
####################
$OPERATION=$o
$VROPS_IP=$v
$VROPS_USER=$u
$VROPS_USER_PASSWORD=$p
$BOOTSTRAP_DIR=$d
$SLEEP_TIME=$s
$COLLECTOR_IP=$c
$INPUT_IPS=$i
 
if( $BOOTSTRAP_DIR -eq $null) {
    $BOOTSTRAP_DIR = $pwd.ToString() + "\" + "arc_${OPERATION}_tmp_dir"
} else {
    $BOOTSTRAP_DIR = $BOOTSTRAP_DIR + "\" + "arc_${OPERATION}_tmp_dir"
}
 
#Execute bootstrap operation
Write-Host "Executing $OPERATION operation..."
 
#add remotedir key to grains
Add-Content $BOOTSTRAP_DIR/grains "remotedir: $BOOTSTRAP_DIR"
 
$GRAINS_PATH="C:\VMware\UCP\salt\conf\grains"
if ( $OPERATION -eq "uninstall" -and (Test-Path $GRAINS_PATH -PathType Leaf)) {
    $ARC_INSTALL_TMP_DIR=Select-String -path $GRAINS_PATH -Pattern "remotedir: (.*)" |Foreach-Object {$_.Matches}|Foreach-Object {$_.Groups[1].Value}
} elseif ( $OPERATION -eq "uninstall" ) {
    $ARC_INSTALL_TMP_DIR=$BOOTSTRAP_DIR.Replace('arc_uninstall_tmp_dir','arc_install_tmp_dir')
}
 
$VC_ID=Select-String -path $BOOTSTRAP_DIR/grains -Pattern "vc_id: (.*)" |Foreach-Object {$_.Matches}|Foreach-Object {$_.Groups[1].Value}
 
$VM_ID=Select-String -path $BOOTSTRAP_DIR/grains -Pattern "vm_id: (.*)" |Foreach-Object {$_.Matches}|Foreach-Object {$_.Groups[1].Value}
 
$VM_NAME=Select-String -path $BOOTSTRAP_DIR/grains -Pattern "vm_name: (.*)" |Foreach-Object {$_.Matches}|Foreach-Object {$_.Groups[1].Value}
 
$SALT_FQDN=Select-String -path $BOOTSTRAP_DIR/grains -Pattern "salt_master_fqdn: (.*)" |Foreach-Object {$_.Matches}|Foreach-Object {$_.Groups[1].Value}
 
$DL_URL=Select-String -path $BOOTSTRAP_DIR/grains -Pattern "dl_url: (.*)" |Foreach-Object {$_.Matches}|Foreach-Object {$_.Groups[1].Value}
 
& $BOOTSTRAP_DIR\uaf-bootstrap.bat $OPERATION $VC_ID $VM_ID $VM_NAME $SALT_FQDN $DL_URL download_job $BOOTSTRAP_DIR  2>&1 | Out-File -FilePath $BOOTSTRAP_DIR/uaf_bootstrap.log -Append

 

2. 설치 과정의 마지막 오류 메시지가 "install agents failed at FAILED - runBootstrapOrch" 였기 때문에, runBootstrapOrch가 무엇인지를 우선 확인해야 합니다. 

uaf-bootstrap.bat 파일을 살펴보면, 몇 가지 단계를 나누어서 구성되어 있는 것을 알 수 있습니다.

대략적으로 정리해보면, 설치할 파일들을 download하고, "Salt Minion"과 "Ucp Minion"을 설치 및 등록한 후 마지막으로 runBootstrapOrch 단계를 실행합니다.

즉, 마지막 단계인 runBootstrapOrch 에서 문제가 발생했음을 추측할 수 있습니다.

uaf-bootstrap.bat

:install
    call :logMesg INFO "Starting Installation..."

    :: 1. Download required components
    :: 2. Install Salt Minion
    :: 3. Install Ucp Minion

    call :initializeVars

    call :runBootstrapStages downloadAll,installSaltMinion,registerSaltMinionService,installUcpMinion,registerUcpMinionService,runBootstrapOrch

    exit /b %errorlevel%

...
...
...

:runBootstrapOrch
    set FETCH_ARTIFACTS_AND_RETRY=false
    :: Setup Bootstrap Orch Environment
    call:logMesg INFO "Setup Bootstrap Orch Environment..."
    %CONFIG_UTILS_CMD% unzip "%PROGDIR%\ucp-minion.zip" "%PROGDIR%"
    set _exitStatus=!errorlevel!

...
...
...

 

3. 이제 설치 과정 시 생성된 uaf_bootstrap.log 파일을 확인해보겠습니다.

downloadAll > installSaltMinion > registerSaltMinionService > installUcpMinion > registerUcpMinionService 단계가 순서대로 완료된 후 runBootstrapOrch 단계에서 config-utils.py 내부의 status_windows 함수를 호출할 때, decode 관련 오류가 기록되어 있습니다.

uaf_bootstrap.log

C:\Users\system-mgr.pub>call :logMesg INFO "Starting Installation..." 
C:\Users\system-mgr.pub>call :initializeVars 
C:\Users\system-mgr.pub>call :runBootstrapStages downloadAll,installSaltMinion,registerSaltMinionService,installUcpMinion,registerUcpMinionService,runBootstrapOrch 

C:\Users\system-mgr.pub>echo [2022-11-22,10:06:25.70] - INFO - 2. Execution the stage 'install - reinstall install downloadAll' 
[2022-11-22,10:06:25.70] - INFO - 2. Execution the stage 'install - reinstall install downloadAll'

C:\Users\system-mgr.pub>echo [2022-11-22,10:06:26.64] - INFO - 2. Execution the stage 'install - reinstall install installSaltMinion' 
[2022-11-22,10:06:26.64] - INFO - 2. Execution the stage 'install - reinstall install installSaltMinion'

C:\Users\system-mgr.pub>echo [2022-11-22,10:06:31.57] - INFO - 2. Execution the stage 'install - reinstall install registerSaltMinionService' 
[2022-11-22,10:06:31.57] - INFO - 2. Execution the stage 'install - reinstall install registerSaltMinionService'

C:\Users\system-mgr.pub>echo [2022-11-22,10:06:31.67] - INFO - 2. Execution the stage 'install - reinstall install installUcpMinion' 
[2022-11-22,10:06:31.67] - INFO - 2. Execution the stage 'install - reinstall install installUcpMinion'

C:\Users\system-mgr.pub>echo [2022-11-22,10:06:32.01] - INFO - 2. Execution the stage 'install - reinstall install registerUcpMinionService' 
[2022-11-22,10:06:32.01] - INFO - 2. Execution the stage 'install - reinstall install registerUcpMinionService'

C:\Users\system-mgr.pub>echo [2022-11-22,10:06:32.12] - INFO - 2. Execution the stage 'install - reinstall install runBootstrapOrch' 
[2022-11-22,10:06:32.12] - INFO - 2. Execution the stage 'install - reinstall install runBootstrapOrch'

C:\Users\system-mgr.pub>echo [2022-11-22,10:06:32.14] - INFO - Setup Bootstrap Orch Environment... 
[2022-11-22,10:06:32.14] - INFO - Setup Bootstrap Orch Environment...

C:\Users\system-mgr.pub>echo [2022-11-22,10:06:32.45] - INFO - Running Bootstrap Orchestration... 
[2022-11-22,10:06:32.45] - INFO - Running Bootstrap Orchestration...

2022-11-22 10:06:32 - INFO - root - Running action: reinstall, run_dependent_action: install

2022-11-22 10:06:32 - INFO - root - Running the stage: SETUP_BOOTSTRAP_ENV configure_uaf_infra verify_configure_uaf_infra Setup Bootstrap Environment

2022-11-22 10:06:32 - INFO - root - Stage action run info - action=install_salt_minion ret=True stage_status=SUCCESS

Traceback (most recent call last):
  File "C:\Users\system-mgr.pub\arc_install_tmp_dir\config-utils.py", line 1199, in <module>
    exit_status = 0 if u.run_orchestration() else 1
  File "C:\Users\system-mgr.pub\arc_install_tmp_dir\config-utils.py", line 396, in run_orchestration
    stage_status = self.run_stage(stage)
  File "C:\Users\system-mgr.pub\arc_install_tmp_dir\config-utils.py", line 444, in run_stage
    stage_status = self.run_stage_action(stage, action, verify)
  File "C:\Users\system-mgr.pub\arc_install_tmp_dir\config-utils.py", line 460, in run_stage_action
    ret = verify_cb()
  File "C:\Users\system-mgr.pub\arc_install_tmp_dir\config-utils.py", line 725, in is_salt_minion_installed
    return SvcStatus.NOT_INSTALLED != ServiceControl(SvcNames.SALT_MINION).status()['STATUS']
  File "C:\Users\system-mgr.pub\arc_install_tmp_dir\config-utils.py", line 137, in status
    return self.status_windows()
  File "C:\Users\system-mgr.pub\arc_install_tmp_dir\config-utils.py", line 209, in status_windows
    strStdout = strStdout.decode().replace("\r\n", "\n").replace("\r", "\n")
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc1 in position 42: invalid start byte

C:\Users\system-mgr.pub>exit /b 1

C:\Users\system-mgr.pub>echo Run Bootstrap Orch _exitStatus=!_exitStatus! 
Run Bootstrap Orch _exitStatus=1

C:\Users\system-mgr.pub>call:exitHandler

C:\Users\system-mgr.pub>if "" == "" (call:setActionCurrentStatusMesg "FAILED - runBootstrapOrch" ) 

C:\Users\system-mgr.pub>set ACTION_CURRENT_STATUS_MESG=FAILED - runBootstrapOrch 

 

4. 그렇다면, 다음으로 config-utils.py 파일에서 status_windows 함수를 찾아봐야 합니다.

문제가 생기기 전까지의 코드를 살펴보면, "sc query service_name"을 이용해서 서비스의 상태를 확인하고, 이 결과값을 읽어들인 후 이 값을 decode 하는 과정을 수행합니다.

    def status_windows(self):
        status_dict = {'STATE': 'NA'}
        state_found = False
        cmd = "sc query \"%s\"" %(self.service_name)
        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
        retval = proc.wait()
        if retval == 0:
            strStdout = proc.stdout.read() + proc.stderr.read()
            strStdout = strStdout.decode().replace("\r\n", "\n").replace("\r", "\n") ## an error occurs here
            lines = []
            for line in strStdout.split("\n"):
                keyval = line.split(":")
                if len(keyval) < 2:
                    continue
                lines.append(line)
                key = keyval[0].strip()
                val = keyval[1].strip()
                status_dict[key] = val
                if key == 'STATE': state_found = True

        ## i18N/G11N case - STATE, TYPE keys are locale strings
        if not state_found and retval == 0 and len(lines) >= 3:
            key, val = (k.strip() for k in lines[1].split(':'))
            status_dict['TYPE'] = val
            key, val = (k.strip() for k in lines[2].split(':'))
            status_dict['STATE'] = val

        status_dict['STATUS'] = status_dict['STATE']
        if status_dict['STATE'] == 'NA':
            status_dict['STATUS'] = SvcStatus.NOT_INSTALLED
        if status_dict['STATE'].endswith('STOPPED'):
            status_dict['STATUS'] = SvcStatus.STOPPED
        if status_dict['STATE'].endswith('RUNNING'):
            status_dict['STATUS'] = SvcStatus.RUNNING
        if status_dict['STATE'].endswith('PAUSED'):
            status_dict['STATUS'] = SvcStatus.PAUSED

        return status_dict

 

5. 위 과정을 Sample Code로 작성하여, 다음과 같이 테스트를 진행하였습니다.

Sample Code를 이용해서 테스트를 해보면, 실제로 설치 과정과 동일한 오류가 발생하는 것을 알 수 있습니다.

test.py

import subprocess

cmd = "sc query \"ucp-salt-minion\""
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
retval = proc.wait()
strStdout = proc.stdout.read() + proc.stderr.read()
strStdout = strStdout.decode().replace("\r\n", "\n").replace("\r", "\n")

C:\Temp\arc_install_tmp_dir>"%PYTHON_BIN%" C:\Temp\arc_install_tmp_dir\test.py
Traceback (most recent call last):
  File "C:\Temp\arc_install_tmp_dir\test.py", line 7, in <module>
    strStdout = strStdout.decode().replace("\r\n", "\n").replace("\r", "\n")
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc1 in position 42: invalid start byte

 

6. 그렇다면, 왜 해당 과정에서 오류가 발생하는지를 알아봐야 합니다.

이를 위해서 Python의 pdb를 이용하여 Sample Code가 실행될 때 어떤 값이 Return 되는지를 확인해봤습니다.

아래 결과를 보시면 "strStdout = proc.stdout.read() + proc.stderr.read()" 과정에서 strStdoutbyte class로 "sc query" 결과값을 전달받은 것을 볼 수 있습니다.

C:\Temp\arc_install_tmp_dir>"%PYTHON_BIN%" -m pdb C:\Temp\arc_install_tmp_dir\test.py
> c:\temp\arc_install_tmp_dir\test.py(1)<module>()
-> import subprocess

(Pdb) n

> c:\temp\arc_install_tmp_dir\test.py(3)<module>()
-> cmd = "sc query \"ucp-salt-minion\""

(Pdb) n

> c:\temp\arc_install_tmp_dir\test.py(4)<module>()
-> proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)

(Pdb) n

> c:\temp\arc_install_tmp_dir\test.py(5)<module>()
-> retval = proc.wait()

(Pdb) n

> c:\temp\arc_install_tmp_dir\test.py(6)<module>()
-> strStdout = proc.stdout.read() + proc.stderr.read()

(Pdb) n

> c:\temp\arc_install_tmp_dir\test.py(7)<module>()
-> strStdout = strStdout.decode().replace("\r\n", "\n").replace("\r", "\n")

(Pdb) print(type(strStdout))
<class 'bytes'>

(Pdb) print(strStdout)

b'\r\nSERVICE_NAME: ucp-salt-minion \r\n        \xc1\xbe\xb7\xf9               : 10  WIN32_OWN_PROCESS  \r\n        \xbb\xf3\xc5\xc2              : 1  STOPPED \r\n        WIN32_EXIT_CODE    : 1077  (0x435)\r\n        SERVICE_EXIT_CODE  : 0  (0x0)\r\n        \xb0\xcb\xbb\xe7\xc1\xa1         : 0x0\r\n        WAIT_HINT          : 0x0\r\n'

(Pdb) n

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc1 in position 42: invalid start byte
> c:\temp\arc_install_tmp_dir\test.py(7)<module>()
-> strStdout = strStdout.decode().replace("\r\n", "\n").replace("\r", "\n")

 

7. strStdout 변수가 가지고 있는 값을 확인해보면, 실제 "sc query" 명령어의 결과에서 한글로 표현된 글자가 encoding된 값으로 표시되는 것을 알 수 있습니다. 

C:\Temp\arc_install_tmp_dir>sc query ucp-salt-minion

SERVICE_NAME: ucp-salt-minion
        종류               : 10  WIN32_OWN_PROCESS
        상태              : 1  STOPPED
        WIN32_EXIT_CODE    : 1077  (0x435)
        SERVICE_EXIT_CODE  : 0  (0x0)
        검사점         : 0x0
        WAIT_HINT          : 0x0

(Pdb) print(strStdout)
b'\r\nSERVICE_NAME: ucp-salt-minion \r\n        \xc1\xbe\xb7\xf9               : 10  WIN32_OWN_PROCESS  \r\n        \xbb\xf3\xc5\xc2              : 1  STOPPED \r\n        WIN32_EXIT_CODE    : 1077  (0x435)\r\n        SERVICE_EXIT_CODE  : 0  (0x0)\r\n        \xb0\xcb\xbb\xe7\xc1\xa1         : 0x0\r\n        WAIT_HINT          : 0x0\r\n'

 

8. 문제는 해당 글자가 encoding된 방식이 UTF가 아닌 euc-kr 이라는 것입니다. 이는 Windows Server에서 기본적으로 제공하는 완성형 방식의 글자이며, 개별 글자의 encode 값은 다음과 같이 정리해 볼 수 있습니다.

종류 
   종 \xc1\xbe
   류 \xb7\xf9

상태
   상 \xbb\xf3
   태 \xc5\xc2

검사점
   검 \xb0\xcb
   사 \xbb\xe7
   점 \xc1\xa1

 

9. 비교를 위해서 동일한 글자를 UTF-8로 encoding 하면 다음과 같은 값으로 결과를 확인할 수 있습니다.

대충봐도 확연히 다른 값들인 것을 알 수 있습니다.

종류 \xec\xa2\x85\xeb\xa5\x98
상태 \xec\x83\x81\xed\x83\x9c
검사점 \xEA\xB2\x80\xEC\x82\xAC\xEC\xA0\x90

 

10. 위 내용에서 문제가 발생한 부분은 strStdout 변수의 class가 byte인 경우 해당 class에서 decode() 함수를 호출할 때 별도의 argument를 지정하지 않으면, 기본적으로 UTF-8을 사용하여 decode를 하는데요. 실제로 strStdout 변수에 담겨있던 값 중 한글은 UTF-8이 아닌 euc-kr로 되어 있었기 때문에 올바르게 decoding이 되지 않던 현상이었습니다.

 

완성형 방식이 아닌 경우에는 위와 같은 문제가 발생하지는 않는 것으로 확인하였으며, 현재 해당 문제는 개발팀에서 수정 및 테스트를 진행하고 있는 단계입니다.

 

본 케이스를 통해서 Windows Server의 다국어 지원 시 어떤 방식으로 글자를 encoding 하는지를 확인할 수 있었으며, Python을 테스트 할 때 PDB라는 도구를 이용하여 변수를 확인해가면서 Debugging을 할 수 있던 기회였습니다.