0

我正在编写一个脚本,该脚本检查服务器是否 ping,然后检查所有 SQL Server 服务状态并将其存储在表中。它还适用于 SQL 实例。但是,我必须检查 70 台服务器,并且运行需要一分钟多的时间。我查看了 AsJob 参数,当我将其添加到所有“Get_WMIObject xxService”命令时,它开始为每个服务返回错误信息。即它开始为所有人返回“运行”状态,所以我怀疑它只是重复先前的数据捕获。尽管它确实只运行了 20 秒。有人可以看看下面并建议我哪里出错了或者我可以做些什么来使服务器服务信息的检索异步?

我认为将所有服务器的服务状态存储在哈希表中并一次性更新 SQL 数据表可能会更好,但事实证明它相当复杂。我觉得我的代码需要完全重新考虑才能异步运行,但我试图避免这种情况!

提前致谢

<#
Tests all Servers for Ping and SQL Server Service Status
#>

# Return the Ping Status
Function GetStatusCode 
{  
    Param([int] $StatusCode)   
    switch($StatusCode) 
    { 
        0         {"Success"} 
        11001   {"Buffer Too Small"} 
        11002   {"Destination Net Unreachable"} 
        11003   {"Destination Host Unreachable"} 
        11004   {"Destination Protocol Unreachable"} 
        11005   {"Destination Port Unreachable"} 
        11006   {"No Resources"} 
        11007   {"Bad Option"} 
        11008   {"Hardware Error"} 
        11009   {"Packet Too Big"} 
        11010   {"Request Timed Out"} 
        11011   {"Bad Request"} 
        11012   {"Bad Route"} 
        11013   {"TimeToLive Expired Transit"} 
        11014   {"TimeToLive Expired Reassembly"} 
        11015   {"Parameter Problem"} 
        11016   {"Source Quench"} 
        11017   {"Option Too Big"} 
        11018   {"Bad Destination"} 
        11032   {"Negotiating IPSEC"} 
        11050   {"General Failure"} 
        default {"Failed"} 
    } 
} 

# Format the Server Up-time
Function GetUpTime 
{ 
    param([string] $LastBootTime) 
    $Uptime = (Get-Date) - [System.Management.ManagementDateTimeconverter]::ToDateTime($LastBootTime) 
    "Days: $($Uptime.Days); Hours: $($Uptime.Hours); Minutes: $($Uptime.Minutes); Seconds: $($Uptime.Seconds)"  
} 

#Main Body
# Populate Table MyDB.dbo.tbl_ServerPingTest

$conn = New-Object System.Data.SqlClient.SqlConnection("Data Source=MyServer; Initial Catalog=MyDB; Integrated Security=SSPI")
$conn.Open()

$cmd = $conn.CreateCommand()
$cmd.CommandText ="DELETE FROM MyDB.dbo.tbl_ServerPingTest
INSERT INTO MyDB.dbo.tbl_ServerPingTest (ServerName, InstanceName)
SELECT ServerName, InstanceName FROM MyDB.dbo.tbl_servers
WHERE ServerCategory <> 'DECOMMED'"
$cmd.ExecuteNonQuery()

$cmd2 = $conn.CreateCommand()
$cmd2.CommandText = "SELECT * FROM MyDB.dbo.tbl_ServerPingTest"
$da = New-Object System.Data.SqlClient.SqlDataAdapter #($cmd2)
$da.SelectCommand = $cmd2
$dt = New-Object System.Data.DataTable
$da.Fill($dt) | Out-Null

# Cycle through Server and Instances and retrieve information
Foreach($row in $dt.rows) 
{ 
    $ServerName = $row.ServerName
    $InstanceName = $row.InstanceName

    $pingStatus = Get-WmiObject -Query "Select * from win32_PingStatus where Address='$ServerName'" 

    $Uptime = $null 
    $SQLServerStatus = $null 
    $SQLAgentStatus = $null 

    # Enter the Loop if a Server is Pingable
    if($pingStatus.StatusCode -eq 0) 
    { 
        # Trap needed for server where Access is Denied causes the SQL Job to fail
        trap {continue}
        $OperatingSystem = Get-WmiObject Win32_OperatingSystem -ComputerName $ServerName -ErrorAction SilentlyContinue -ErrorVariable wmiResults
        $Uptime = GetUptime( $OperatingSystem.LastBootUpTime ) 

        if ($wmiResults -ne $null)
          {
            $tmperr = "Uptime Info Could Not be Obtained"
            $Uptime = $null
          }
        else
          {
            $tmperr = ""

            filter SvcFilter {
                if ($_.StartMode -eq "Disabled") {$_.StartMode }
                else {$_.State}
                }

            $props="Name","StartMode","State"

            if ($InstanceName -eq 'DEFAULT') 
                {
                    $SQLServerStatus = Get-WMIObject win32_service -property $props -filter "name='MSSQLSERVER'" -computer $ServerName | SvcFilter
                    $SQLAgentStatus = Get-WMIObject win32_service -property $props -filter "name='SQLSERVERAGENT'" -computer $ServerName | SvcFilter
                    $RSAgentStatus = Get-WMIObject win32_service -property $props -filter "name='ReportServer'" -computer $ServerName | SvcFilter
                }
                else
                {
                    $NamedInstanceSQLService = "MSSQL$" + $InstanceName
                    $NamedInstanceAgentService = "SQLAgent$" + $InstanceName
                    $NamedInstanceRSService = "ReportServer$" + $InstanceName
                    $SQLServerStatus = Get-WMIObject win32_service -property $props -computer $ServerName | where {$_.name -eq $NamedInstanceSQLService} | SvcFilter
                    $SQLAgentStatus = Get-WMIObject win32_service -property $props -computer $ServerName | where {$_.name -eq $NamedInstanceAgentService} | SvcFilter
                    $RSAgentStatus = Get-WMIObject win32_service -property $props -computer $ServerName | where {$_.name -eq $NamedInstanceRSService} | SvcFilter
                }

            $ASAgentStatus = Get-WMIObject win32_service -property $props -filter "name='MSSQLServerOLAPService'" -computer $ServerName | SvcFilter

          }
    } 

    $IPAddress = $pingStatus.IPV4Address
    $PingTest = GetStatusCode( $pingStatus.StatusCode ) 
    $ErrMSG = $tmperr

    # Update Table MyDB.dbo.tbl_ServerPingTest with all retreived information 
    $updateRow = $dt.Select("ServerName = '$ServerName' AND InstanceName = '$InstanceName'")
    $updateRow[0].IPAddress = $IPAddress
    $updateRow[0].PingTest = $PingTest
    $updateRow[0].ErrMSG = $ErrMSG
    $updateRow[0].Uptime = $Uptime
    $updateRow[0].SQLServerStatus = $SQLServerStatus
    $updateRow[0].SQLAgentStatus = $SQLAgentStatus   
    $updateRow[0].RSAgentStatus = $RSAgentStatus 
    $updateRow[0].ASAgentStatus = $ASAgentStatus 

    $cmdUpd = $conn.CreateCommand()
    $cmdUpd.CommandText = "UPDATE MyDB.dbo.tbl_ServerPingTest
    SET IPAddress = @IPAddress, PingTest = @PingTest, ErrMSG = @ErrMSG, Uptime = @Uptime, SQLServerStatus = @SQLServerStatus, SQLAgentStatus = @SQLAgentStatus, RSAgentStatus = @RSAgentStatus, ASAgentStatus = @ASAgentStatus
    WHERE ServerName = @ServerName AND InstanceName = @InstanceName"

    # Add parameters to pass values to the UPDATE statement
    $cmdUpd.Parameters.Add("@ServerName", "nvarchar", 50, "ServerName") | Out-Null
    $cmdUpd.Parameters["@ServerName"].SourceVersion = "Original"
    $cmdUpd.Parameters.Add("@InstanceName", "nvarchar", 50, "InstanceName") | Out-Null
    $cmdUpd.Parameters["@InstanceName"].SourceVersion = "Original"
    $cmdUpd.Parameters.Add("@IPAddress", "nvarchar", 50, "IPAddress") | Out-Null
    $cmdUpd.Parameters["@IPAddress"].SourceVersion = "Current"
    $cmdUpd.Parameters.Add("@PingTest", "nvarchar", 50, "PingTest") | Out-Null
    $cmdUpd.Parameters["@PingTest"].SourceVersion = "Current"
    $cmdUpd.Parameters.Add("@ErrMSG", "nvarchar", 50, "ErrMSG") | Out-Null
    $cmdUpd.Parameters["@ErrMSG"].SourceVersion = "Current"
    $cmdUpd.Parameters.Add("@Uptime", "nvarchar", 50, "Uptime") | Out-Null
    $cmdUpd.Parameters["@Uptime"].SourceVersion = "Current"
    $cmdUpd.Parameters.Add("@SQLServerStatus", "nvarchar", 50, "SQLServerStatus") | Out-Null
    $cmdUpd.Parameters["@SQLServerStatus"].SourceVersion = "Current"
    $cmdUpd.Parameters.Add("@SQLAgentStatus", "nvarchar", 50, "SQLAgentStatus") | Out-Null
    $cmdUpd.Parameters["@SQLAgentStatus"].SourceVersion = "Current"
    $cmdUpd.Parameters.Add("@RSAgentStatus", "nvarchar", 50, "RSAgentStatus") | Out-Null
    $cmdUpd.Parameters["@RSAgentStatus"].SourceVersion = "Current"
    $cmdUpd.Parameters.Add("@ASAgentStatus", "nvarchar", 50, "ASAgentStatus") | Out-Null
    $cmdUpd.Parameters["@ASAgentStatus"].SourceVersion = "Current"

    # Set the UpdateCommand property
    $da.UpdateCommand = $cmdUpd

    # Update the database
    $RowsUpdated = $da.Update($dt)


} 

$conn.Close()

我尝试将 -AsJob 参数添加到每个 Get-WMIObject 行

IE

$SQLServerStatus = Get-WMIObject win32_service -property $props -computer $ServerName | where {$_.name -eq $NamedInstanceSQLService} -AsJob | SvcFilter

但我没有在代码中的任何地方使用 Receive-Job。我注意到每个变量都会返回“运行”状态,即使它是“停止”或“禁用”。所以我猜测作业的结果没有被正确捕获,实际上它正在返回以前的 Get-WMIObject 调用的输出(其中大部分将处于“运行”状态)。

4

3 回答 3

0

如果您的 get-wmiObject想要短的 timeout,那么get-wmiCustom 函数可能会有所帮助。

于 2013-04-25T06:05:09.683 回答
0

我设法通过使用 Start-Job 重新编写来做到这一点,并将其从 80 秒减少到 20 秒。如果有人对如何提高效率有任何进一步的建议,我将不胜感激。我正在考虑一口气而不是一个一个地检索所有 WMIObjects?谁能建议如何?

#Main Body
# Populate Table MyDB.dbo.tbl_ServerPingTest

$conn = New-Object System.Data.SqlClient.SqlConnection("Data Source=MyServer; Initial Catalog=MyDB; Integrated Security=SSPI")
$conn.Open()

$cmd = $conn.CreateCommand()
$cmd.CommandText ="DELETE FROM MyDB.dbo.tbl_ServerPingTest
INSERT INTO MyDB.dbo.tbl_ServerPingTest (ServerName, InstanceName)
SELECT ServerName, InstanceName FROM MyDB.dbo.tbl_servers
WHERE ServerCategory <> 'DECOMMED'"
$cmd.ExecuteNonQuery()

$cmd2 = $conn.CreateCommand()
$cmd2.CommandText = "SELECT * FROM MyDB.dbo.tbl_ServerPingTest"
$da = New-Object System.Data.SqlClient.SqlDataAdapter #($cmd2)
$da.SelectCommand = $cmd2
$dt = New-Object System.Data.DataTable
$da.Fill($dt) | Out-Null

# Create list
$serverlist = @()

$sb = {
    param ([string] $ServerName, [string] $InstanceName)

    # Return the Ping Status
    Function GetStatusCode 
    {  
        Param([int] $StatusCode)   
        switch($StatusCode) 
        { 
            0         {"Success"} 
            11001   {"Buffer Too Small"} 
            11002   {"Destination Net Unreachable"} 
            11003   {"Destination Host Unreachable"} 
            11004   {"Destination Protocol Unreachable"} 
            11005   {"Destination Port Unreachable"} 
            11006   {"No Resources"} 
            11007   {"Bad Option"} 
            11008   {"Hardware Error"} 
            11009   {"Packet Too Big"} 
            11010   {"Request Timed Out"} 
            11011   {"Bad Request"} 
            11012   {"Bad Route"} 
            11013   {"TimeToLive Expired Transit"} 
            11014   {"TimeToLive Expired Reassembly"} 
            11015   {"Parameter Problem"} 
            11016   {"Source Quench"} 
            11017   {"Option Too Big"} 
            11018   {"Bad Destination"} 
            11032   {"Negotiating IPSEC"} 
            11050   {"General Failure"} 
            default {"Failed"} 
        } 
    }

    # Format the Server Up-time
    Function GetUpTime 
    { 
        param([string] $LastBootTime) 
        $Uptime = (Get-Date) - [System.Management.ManagementDateTimeconverter]::ToDateTime($LastBootTime) 
        "Days: $($Uptime.Days); Hours: $($Uptime.Hours); Minutes: $($Uptime.Minutes); Seconds: $($Uptime.Seconds)"  
    } 

    try {

        # Fetch IP
        $pingStatus = Get-WmiObject -Query "Select * from win32_PingStatus where Address='$ServerName'"

        $Uptime = $null 
        $SQLServerStatus = $null 
        $SQLAgentStatus = $null 

        # Enter the Loop if a Server is Pingable
        if($pingStatus.StatusCode -eq 0) 
        { 
            # Trap needed for server where Access is Denied causes the SQL Job to fail
            trap {continue}
            $OperatingSystem = Get-WmiObject Win32_OperatingSystem -ComputerName $ServerName -ErrorAction SilentlyContinue -ErrorVariable wmiResults
            $Uptime = GetUptime( $OperatingSystem.LastBootUpTime ) 

            if ($wmiResults -ne $null)
              {
                $tmperr = "Uptime Info Could Not be Obtained"
                $Uptime = $null
              }
            else
              {
                $tmperr = ""

                filter SvcFilter {
                    if ($_.StartMode -eq "Disabled") {$_.StartMode }
                    else {$_.State}
                    }

                $props="Name","StartMode","State"

                if ($InstanceName -eq 'DEFAULT') 
                    {
                        #Write-Host $ServerName + '\' + $InstanceName 
                        $SQLServerStatus = Get-WMIObject win32_service -property $props -filter "name='MSSQLSERVER'" -computer $ServerName | SvcFilter
                        #Write-Host $SQLServerStatus
                        $SQLAgentStatus = Get-WMIObject win32_service -property $props -filter "name='SQLSERVERAGENT'" -computer $ServerName | SvcFilter
                        #Write-Host $SQLAgentStatus
                        $RSAgentStatus = Get-WMIObject win32_service -property $props -filter "name='ReportServer'" -computer $ServerName | SvcFilter
                        #Write-Host $RSAgentStatus
                    }
                    else
                    {
                        #Write-Host $ServerName + '\' + $InstanceName 
                        $NamedInstanceSQLService = "MSSQL$" + $InstanceName
                        $NamedInstanceAgentService = "SQLAgent$" + $InstanceName
                        $NamedInstanceRSService = "ReportServer$" + $InstanceName
                        $SQLServerStatus = Get-WMIObject win32_service -property $props -computer $ServerName | where {$_.name -eq $NamedInstanceSQLService} | SvcFilter
                        $SQLAgentStatus = Get-WMIObject win32_service -property $props -computer $ServerName | where {$_.name -eq $NamedInstanceAgentService} | SvcFilter
                        $RSAgentStatus = Get-WMIObject win32_service -property $props -computer $ServerName | where {$_.name -eq $NamedInstanceRSService} | SvcFilter
                    }

                $ASAgentStatus = Get-WMIObject win32_service -property $props -filter "name='MSSQLServerOLAPService'" -computer $ServerName | SvcFilter

              }
        } 

        $IPAddress = $pingStatus.IPV4Address
        $PingTest = GetStatusCode( $pingStatus.StatusCode ) 
        $ErrMSG = $tmperr

        # Save info about server
        $serverInfo = New-Object -TypeName PSObject -Property @{
        ServerName = $ServerName
        InstanceName = $InstanceName
        IPAddress = $IPAddress
        PingTest = $PingTest
        ErrMSG = $ErrMSG
        Uptime = $Uptime
        SQLServerStatus = $SQLServerStatus
        SQLAgentStatus = $SQLAgentStatus   
        RSAgentStatus = $RSAgentStatus 
        ASAgentStatus = $ASAgentStatus 

        }
        return $serverInfo
    } catch {
        throw 'Failed to process server named {0}. The error was "{1}".' -f $ServerName, $_
    }
}

# Loop servers
$max = 5
$jobs = @()
foreach($row in $dt.rows) {
    #$InstanceName = "DEFAULT"
    $server = $row.ServerName
    $InstanceName = $row.InstanceName
    $jobs += Start-Job -ScriptBlock $sb -ArgumentList @($server, $InstanceName) 
    $running = @($jobs | ? {$_.State -eq 'Running'})

    # Throttle jobs.
    while ($running.Count -ge $max) {
        $finished = Wait-Job -Job $jobs -Any
        $running = @($jobs | ? {$_.State -eq 'Running'})
    }
}

# Wait for remaining.
Wait-Job -Job $jobs > $null

# Check for failed jobs.
$failed = @($jobs | ? {$_.State -eq 'Failed'})
if ($failed.Count -gt 0) {
    $failed | % {
        $_.ChildJobs[0].JobStateInfo.Reason.Message
    }
}

# Collect job data.
$jobs | % {
    $serverlist += $_ | Receive-Job | Select-Object ServerName,InstanceName,IPAddress,PingTest,ErrMSG,Uptime,SQLServerStatus,SQLAgentStatus,RSAgentStatus,ASAgentStatus
}

#$serverlist

Foreach($row in $serverlist) 
{ 
    $ServerName = $row.ServerName
    $InstanceName = $row.InstanceName

    #$dt.Select("ServerName = '$ServerName' AND InstanceName = '$InstanceName'")

    $IPAddress = $row.IPAddress
    $PingTest = $row.PingTest
    $ErrMSG = $row.ErrMSG
    $Uptime = $row.Uptime
    $SQLServerStatus = $row.SQLServerStatus
    $SQLAgentStatus = $row.SQLAgentStatus   
    $RSAgentStatus = $row.RSAgentStatus 
    $ASAgentStatus = $row.ASAgentStatus 


   # Write-Host $ServerName
   # Write-Host $InstanceName

    $updateRow = $dt.Select("ServerName = '$ServerName' AND InstanceName = '$InstanceName'")
    $updateRow[0].IPAddress = $IPAddress
    $updateRow[0].PingTest = $PingTest
    $updateRow[0].ErrMSG = $ErrMSG
    $updateRow[0].Uptime = $Uptime
    $updateRow[0].SQLServerStatus = $SQLServerStatus
    $updateRow[0].SQLAgentStatus = $SQLAgentStatus   
    $updateRow[0].RSAgentStatus = $RSAgentStatus 
    $updateRow[0].ASAgentStatus = $ASAgentStatus 

    $cmdUpd = $conn.CreateCommand()
    $cmdUpd.CommandText = "UPDATE MyDB.dbo.tbl_ServerPingTest
    SET IPAddress = @IPAddress, PingTest = @PingTest, ErrMSG = @ErrMSG, Uptime = @Uptime, SQLServerStatus = @SQLServerStatus, SQLAgentStatus = @SQLAgentStatus, RSAgentStatus = @RSAgentStatus, ASAgentStatus = @ASAgentStatus
    WHERE ServerName = @ServerName AND InstanceName = @InstanceName"

    # Add parameters to pass values to the UPDATE statement
    $cmdUpd.Parameters.Add("@ServerName", "nvarchar", 50, "ServerName") | Out-Null
    $cmdUpd.Parameters["@ServerName"].SourceVersion = "Original"
    $cmdUpd.Parameters.Add("@InstanceName", "nvarchar", 50, "InstanceName") | Out-Null
    $cmdUpd.Parameters["@InstanceName"].SourceVersion = "Original"
    $cmdUpd.Parameters.Add("@IPAddress", "nvarchar", 50, "IPAddress") | Out-Null
    $cmdUpd.Parameters["@IPAddress"].SourceVersion = "Current"
    $cmdUpd.Parameters.Add("@PingTest", "nvarchar", 50, "PingTest") | Out-Null
    $cmdUpd.Parameters["@PingTest"].SourceVersion = "Current"
    $cmdUpd.Parameters.Add("@ErrMSG", "nvarchar", 50, "ErrMSG") | Out-Null
    $cmdUpd.Parameters["@ErrMSG"].SourceVersion = "Current"
    $cmdUpd.Parameters.Add("@Uptime", "nvarchar", 50, "Uptime") | Out-Null
    $cmdUpd.Parameters["@Uptime"].SourceVersion = "Current"
    $cmdUpd.Parameters.Add("@SQLServerStatus", "nvarchar", 50, "SQLServerStatus") | Out-Null
    $cmdUpd.Parameters["@SQLServerStatus"].SourceVersion = "Current"
    $cmdUpd.Parameters.Add("@SQLAgentStatus", "nvarchar", 50, "SQLAgentStatus") | Out-Null
    $cmdUpd.Parameters["@SQLAgentStatus"].SourceVersion = "Current"
    $cmdUpd.Parameters.Add("@RSAgentStatus", "nvarchar", 50, "RSAgentStatus") | Out-Null
    $cmdUpd.Parameters["@RSAgentStatus"].SourceVersion = "Current"
    $cmdUpd.Parameters.Add("@ASAgentStatus", "nvarchar", 50, "ASAgentStatus") | Out-Null
    $cmdUpd.Parameters["@ASAgentStatus"].SourceVersion = "Current"

    # Set the UpdateCommand property
    $da.UpdateCommand = $cmdUpd

    # Update the database
    $RowsUpdated = $da.Update($dt)

}

$conn.Close()
于 2013-04-25T13:54:54.663 回答
0

get-wmiobject有一个-asjob参数,但where-object没有。

此外,当您使用 -asjob 时,返回值是一个“作业对象”,而不是由 get-wmiobject 调用返回的对象。

一旦在远程服务器上完成,您可以使用receive-jobcmdlet 从每个作业中取回实际对象(您可以使用wait-jobandget-job来帮助了解它何时完成)。

我建议你做get-help about_Remote_Jobs一些关于如何构建代码的背景知识。

于 2013-04-24T14:53:31.857 回答