Update pve RustDesk GA sync scripts
This commit is contained in:
+112
-38
@@ -25,18 +25,50 @@ SHARE = os.environ.get('BLAIR_SHARE', '/tank/blair-share')
|
||||
REGISTRY = os.path.join(SHARE, 'fleet', 'registry.json')
|
||||
NODE = os.environ.get('BLAIR_PVE_NODE', 'pve')
|
||||
DRY_RUN = os.environ.get('BLAIR_DRY_RUN', '') in ('1', 'true', 'yes')
|
||||
VERBOSE = os.environ.get('BLAIR_VERBOSE', '') in ('1', 'true', 'yes')
|
||||
VMIDS_ENV = os.environ.get('BLAIR_VMIDS', '').strip()
|
||||
|
||||
RUSTDESK_CFG = (
|
||||
r'C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config\RustDesk2.toml'
|
||||
)
|
||||
PS_GET_ID = (
|
||||
r"$p='C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config\RustDesk2.toml'; "
|
||||
r"if (-not (Test-Path -LiteralPath $p)) { exit 1 }; "
|
||||
r"$t=Get-Content -Raw -LiteralPath $p; "
|
||||
r"if ($t -match '(?m)^\s*id\s*=\s*''?(\d{6,12})') { Write-Output $Matches[1]; exit 0 }; "
|
||||
r"exit 1"
|
||||
GUEST_READ_PATHS = [
|
||||
r'C:\ProgramData\Blair\rustdesk-report.json',
|
||||
r'C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config\RustDesk2.toml',
|
||||
r'C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config\RustDesk.toml',
|
||||
r'C:\Windows\System32\config\systemprofile\AppData\Roaming\RustDesk\config\RustDesk2.toml',
|
||||
r'C:\ProgramData\RustDesk\config\RustDesk2.toml',
|
||||
]
|
||||
|
||||
PS_READ_ID = r'''
|
||||
$paths = @(
|
||||
'C:\ProgramData\Blair\rustdesk-report.json',
|
||||
'C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config\RustDesk2.toml',
|
||||
'C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config\RustDesk.toml',
|
||||
'C:\Windows\System32\config\systemprofile\AppData\Roaming\RustDesk\config\RustDesk2.toml',
|
||||
'C:\ProgramData\RustDesk\config\RustDesk2.toml'
|
||||
)
|
||||
foreach ($p in $paths) {
|
||||
if (-not (Test-Path -LiteralPath $p)) { continue }
|
||||
$t = Get-Content -Raw -LiteralPath $p
|
||||
if ($p -like '*.json') {
|
||||
try {
|
||||
$j = $t | ConvertFrom-Json
|
||||
if ($j.rustdesk_id -match '^\d{6,12}$') { Write-Output $j.rustdesk_id; exit 0 }
|
||||
} catch {}
|
||||
}
|
||||
if ($t -match '(?m)^\s*id\s*=\s*[''"]?(\d{6,12})') { Write-Output $Matches[1]; exit 0 }
|
||||
}
|
||||
foreach ($rp in @(
|
||||
'HKLM:\SOFTWARE\RustDesk\Host',
|
||||
'HKLM:\SOFTWARE\RustDesk',
|
||||
'HKLM:\SOFTWARE\WOW6432Node\RustDesk\Host',
|
||||
'HKLM:\SOFTWARE\WOW6432Node\RustDesk'
|
||||
)) {
|
||||
if (-not (Test-Path $rp)) { continue }
|
||||
foreach ($n in @('id', 'Id', 'rustdesk_id', 'peer_id')) {
|
||||
$v = (Get-ItemProperty -Path $rp -Name $n -ErrorAction SilentlyContinue).$n
|
||||
if ($v -match '^\d{6,12}$') { Write-Output $v; exit 0 }
|
||||
}
|
||||
}
|
||||
exit 1
|
||||
'''
|
||||
|
||||
|
||||
def log(msg: str) -> None:
|
||||
@@ -142,40 +174,81 @@ def guest_exec_text(vmid: int, command: list[str], timeout: int = 90) -> tuple[s
|
||||
|
||||
|
||||
def guest_file_read(vmid: int, guest_path: str) -> tuple[str | None, str | None]:
|
||||
proc = subprocess.run(
|
||||
['pvesh', 'get', f'/nodes/{NODE}/qemu/{vmid}/agent/file-read', '--file', guest_path],
|
||||
capture_output=True, text=True, check=False,
|
||||
for path in {guest_path, guest_path.replace('\\', '/')}:
|
||||
proc = subprocess.run(
|
||||
[
|
||||
'pvesh', 'get', f'/nodes/{NODE}/qemu/{vmid}/agent/file-read',
|
||||
'--file', path, '--output-format', 'json',
|
||||
],
|
||||
capture_output=True, text=True, check=False,
|
||||
)
|
||||
if proc.returncode != 0:
|
||||
continue
|
||||
try:
|
||||
payload = json.loads(proc.stdout)
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
data = payload.get('data', payload)
|
||||
content_b64 = data.get('content') if isinstance(data, dict) else None
|
||||
if not content_b64:
|
||||
continue
|
||||
return base64.b64decode(content_b64).decode('utf-8', errors='replace'), None
|
||||
return None, f'file-read failed for {guest_path}'
|
||||
|
||||
|
||||
def parse_id_from_content(text: str, path: str) -> str | None:
|
||||
if path.lower().endswith('.json'):
|
||||
try:
|
||||
rid = str(json.loads(text).get('rustdesk_id', '') or '').strip()
|
||||
if re.fullmatch(r'\d{6,12}', rid):
|
||||
return rid
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
for pattern in (
|
||||
r'(?m)^\s*id\s*=\s*["\']?(\d{6,12})',
|
||||
r'(?m)rustdesk_id\s*=\s*["\']?(\d{6,12})',
|
||||
):
|
||||
match = re.search(pattern, text)
|
||||
if match:
|
||||
return match.group(1)
|
||||
return None
|
||||
|
||||
|
||||
def powershell_encoded(script: str) -> str:
|
||||
return base64.b64encode(script.strip().encode('utf-16-le')).decode('ascii')
|
||||
|
||||
|
||||
def guest_exec_powershell(vmid: int) -> tuple[str | None, str | None]:
|
||||
enc = powershell_encoded(PS_READ_ID)
|
||||
return guest_exec_text(
|
||||
vmid,
|
||||
['powershell.exe', '-NoProfile', '-EncodedCommand', enc],
|
||||
timeout=120,
|
||||
)
|
||||
if proc.returncode != 0:
|
||||
msg = proc.stderr.strip() or proc.stdout.strip() or f'file-read rc={proc.returncode}'
|
||||
return None, msg
|
||||
try:
|
||||
payload = json.loads(proc.stdout)
|
||||
except json.JSONDecodeError:
|
||||
return None, f'file-read json error: {proc.stdout[:160]!r}'
|
||||
data = payload.get('data', payload)
|
||||
content_b64 = data.get('content') if isinstance(data, dict) else None
|
||||
if not content_b64:
|
||||
return None, 'file-read empty content'
|
||||
return base64.b64decode(content_b64).decode('utf-8', errors='replace'), None
|
||||
|
||||
|
||||
def parse_rustdesk_id_from_toml(text: str) -> str | None:
|
||||
match = re.search(r'(?m)^\s*id\s*=\s*["\']?(\d{6,12})', text)
|
||||
return match.group(1) if match else None
|
||||
|
||||
|
||||
def read_rustdesk_id(vmid: int) -> tuple[str | None, str | None]:
|
||||
text, err = guest_file_read(vmid, RUSTDESK_CFG)
|
||||
if text:
|
||||
rid = parse_rustdesk_id_from_toml(text)
|
||||
errors: list[str] = []
|
||||
for path in GUEST_READ_PATHS:
|
||||
text, err = guest_file_read(vmid, path)
|
||||
if not text:
|
||||
if VERBOSE:
|
||||
errors.append(f'{path}: {err}')
|
||||
continue
|
||||
rid = parse_id_from_content(text, path)
|
||||
if rid:
|
||||
if VERBOSE:
|
||||
log(f'VM {vmid}: ID {rid} from file-read {path}')
|
||||
return rid, None
|
||||
err = 'id not found in RustDesk2.toml'
|
||||
return guest_exec_text(
|
||||
vmid,
|
||||
['powershell.exe', '-NoProfile', '-Command', PS_GET_ID],
|
||||
)
|
||||
errors.append(f'{path}: no id in file')
|
||||
|
||||
rid, err = guest_exec_powershell(vmid)
|
||||
if rid:
|
||||
if VERBOSE:
|
||||
log(f'VM {vmid}: ID {rid} from guest exec')
|
||||
return rid, None
|
||||
errors.append(f'exec: {err}')
|
||||
return None, errors[-1] if errors else 'unknown'
|
||||
|
||||
|
||||
def qm_description(vmid: int) -> str:
|
||||
@@ -224,7 +297,8 @@ def main() -> int:
|
||||
|
||||
new_id, err = read_rustdesk_id(vmid)
|
||||
if not new_id:
|
||||
log(f'VM {vmid}: skip (could not read RustDesk ID: {err})')
|
||||
hint = ' (try BLAIR_VERBOSE=1)' if not VERBOSE else ''
|
||||
log(f'VM {vmid}: skip (could not read RustDesk ID: {err}){hint}')
|
||||
continue
|
||||
|
||||
old_registry_id = registry_old_id(registry, vmid)
|
||||
|
||||
Reference in New Issue
Block a user