Powershell/PowerCLI 循环、超时和退出

Powershell/PowerCLI Loop, timeouts and exits

场景如下 - 我正在通过 Powershell/PowerCLI(VMware Powershell 模块)远程启动 VM,一旦 VM 启动,我将 运行针对 VM 执行一系列 cmdlet .

目前我有这段代码:

Start-VM "my VM Name" -runAsync
$vm = Get-VM | where { $_.name -eq "my VM Name" }    

start-sleep -seconds 20
do {
    start-sleep -seconds 5
    $toolsStatus = ($VM | Get-View).Guest.ToolsStatus
} until ($toolsStatus -eq 'toolsOK')

有效 - VM 启动并且循环将检查,直到 VMware Tools 向 VMware 报告(这意味着 VM 已完全启动到 OS)。

然而,这将 运行 无限期地发生,因此在 VM 无法成功启动的情况下 - 脚本会挂起。因此,我尝试添加一个计时器,以便在 运行 5 分钟后退出脚本:

Start-VM "My VM Name" -runAsync
$VM = Get-VM | where { $_.name -eq "My VM Name" }

$timeout = new-timespan -minutes 5
start-sleep -seconds 5
$toolsStatus = ($VM | Get-View).Guest.ToolsStatus
$sw = [diagnostics.stopwatch]::StartNew()
while ($sw.elapsed -lt $timeout) {
    if ($toolsStatus -eq 'toolsOK') {
        return
    }
    start-sleep -seconds 5
}
Exit

5 分钟后成功退出脚本。问题是我正在测试这个 - VM 已经启动并且我收到指示 VMtools 已响应的响应,但循环没有停止。它一直持续到超时然后退出而不是继续下一步。

我以前没有使用过这种类型的循环,所以希望能提供一些意见,因为我确定我已经接近了 - 但我错过了一些东西。

应该只是一个简单的逻辑修复。在最后的 while 循环中,添加带有 -and 的两个表达式并否定 $toolStatus

while ($sw.elapsed -lt $timeout -and $toolsStatus -ne 'toolsOK'){
    start-sleep -seconds 5
}

当一个变为假时,循环结束。

您没有在循环中更新 $toolsStatus,因此即使 VM 已启动,它也永远不会退出。只需将支票移到循环中即可。

Start-VM "My VM Name" -runAsync
$VM = Get-VM | where { $_.name -eq "My VM Name" }

$timeout = new-timespan -minutes 5
start-sleep -seconds 5
$sw = [diagnostics.stopwatch]::StartNew()
while ($sw.elapsed -lt $timeout) {
    $toolsStatus = ($VM | Get-View).Guest.ToolsStatus
    if ($toolsStatus -eq 'toolsOK') {
        return
    }
    start-sleep -seconds 5
}
Exit

VMWare 已有等待命令。

Start-VM "My VM Name" -runAsync
$VM = Get-VM | where { $_.name -eq "My VM Name" } | Wait-Tools -TimeoutSeconds 180

https://www.vmware.com/support/developer/PowerCLI/PowerCLI41U1/html/Wait-Tools.html

我读过有关 VMTools 运行 和 OS 未启动的问题。一旦 Windows 徽标出现,VMTools 似乎报告为 运行,这可能会导致问题。我更喜欢使用 Powershell 命令来 ping 机器,直到得到响应。

$thisisaddress = (Get-NetIPAddress | where {$_.AddressFamily -eq "IPv4" -and $_.InterfaceAlias -eq "Ethernet"}).ipaddress

$pingMachine = New-Object System.Net.NetworkInformation.Ping

$timeout = new-timespan -Minutes 1
$sw = [diagnostics.stopwatch]::StartNew()
while ($sw.elapsed -lt $timeout -and $pingStatus.Status -ne 'Success'){
    $pingStatus = $pingMachine.Send($thisisaddress)
    $pingStatus
}