Query multiple servers for performance counters in parallel

This topic contains 4 replies, has 3 voices, and was last updated by  Taparshi Pandit 1 year, 1 month ago.

  • Author
    Posts
  • #16043

    Aftab Hussain
    Participant

    I've written a script to query multiple Hyper-V hosts for some metrics, then using some logic, returns the name of host, that I can then use to create a vm on. Here is my script, note being I haven't parameterized it yet.

    Workflow Pick-VMHost {
    
        $Servers = @('host00', 'host01', 'host02', 'host03')
        $UseHost = @{}
        $VMRAM = 1.5GB
        $VMDiskSize = 40GB
    
        ForEach -Parallel ($Server in $Servers) {
            $CPU = InlineScript { (Get-Counter -Counter "\Processor(_Total)\% Processor Time" -ComputerName $using:Server -SampleInterval 1 -MaxSamples 5).CounterSamples.CookedValue | Measure-Object -Average | Select-Object -ExpandProperty Average }
            $RAM = InlineScript { (Get-Counter -Counter "\Memory\Available Bytes" -ComputerName $using:Server -SampleInterval 1 -MaxSamples 5).CounterSamples.CookedValue | Measure-Object -Average | Select-Object -ExpandProperty Average }
            $Disk = Get-WmiObject win32_logicaldisk -PSComputerName $Server -Filter "DeviceID='D:'" | Select-Object -ExpandProperty FreeSpace
            
            If ( ($CPU -le 80) -and ($RAM -ge ($VMRAM + 3GB)) -and ($Disk -ge ($VMDiskSize + 25GB)) ) {
                If ($UseHost.Count -eq 0) {
                    $Workflow:UseHost = @{ "Server" = $Server; "Disk" = $Disk }
                }
    
                ElseIf ($Disk -gt $UseHost.Disk) {
                    $Workflow:UseHost = @{ "Server" = $Server; "Disk" = $Disk }
                }
            }
        }
    
        $UseHost.Server
    }
    
    $Result = Pick-VMHost
    

    I feel like this code isn't well written and there is a better way to do this, I've tried use just Get-Counter on its own but didn't find a way to get average results for the counters, tried using Jobs, but couldn't figure out how to return values properly, I've ended up using a Workflow. I've also posted part of this script on StackOverflow, because I couldn't at the time figure out how to return the values I wanted in a meaningful way.

    Finally I know that Hyper-V has VM Metric functionality, but as far as I'm aware it only queries the VM's and not the host.

  • #16045

    Dave Wyatt
    Moderator

    This code still collects each counter synchronously on the remote computers, but process the computers themselves in parallel. If you need to parallelize the counter collection as well, that's probably also possible, but would involve a lot more code, and may not be worth the effort (depends on how long this takes to run, and if it meets your needs already.)

  • #16046

    Aftab Hussain
    Participant

    I didn't know about New-Object, more reading to do. If I want to run the commands synchronously on each remote machine, would you recommend using a workflow? As it is at the moment, I will keep the code as is, but if I add more counters then I might need to speed up the process.

    This makes it so much easier to read, thanks.

  • #45666

    Taparshi Pandit
    Participant

    a simple approach ...
    Get-counter -Counter “\PhysicalDisk(_Total)\Avg. Disk Read Queue Length”,”\Processor(_Total)\% Processor Time”,”\PhysicalDisk(_Total)\Avg. Disk Write Queue Length”,”\Memory\Available MBytes”,”\Memory\Pages/sec” -computername computername1,computername2,computername3 -Continuous | export-counter desktop\counter.blg

    Multiple counters , multiple remote computers...

  • #16044

    Dave Wyatt
    Moderator

    If you have PSRemoting enabled on the target servers, the simplest approach would be to use Invoke-Command. When you pass it an array to the ComputerName parameter, it automatically does the operation in parallel (up to a maximum number at a time, which you can specify with the -ThrottleLimit parameter). For example:

    Function Pick-VMHost {
    
        $Servers = @('host00', 'host01', 'host02', 'host03')
        $UseHost = @{}
        $VMRAM = 1.5GB
        $VMDiskSize = 40GB
    
        $serverMetrics = Invoke-Command -ComputerName $Servers -ScriptBlock {
            $CPU = (Get-Counter -Counter "\Processor(_Total)\% Processor Time" -SampleInterval 1 -MaxSamples 5).CounterSamples.CookedValue | Measure-Object -Average | Select-Object -ExpandProperty Average
            $RAM = (Get-Counter -Counter "\Memory\Available Bytes" -SampleInterval 1 -MaxSamples 5).CounterSamples.CookedValue | Measure-Object -Average | Select-Object -ExpandProperty Average
            $Disk = Get-WmiObject win32_logicaldisk -Filter "DeviceID='D:'" | Select-Object -ExpandProperty FreeSpace
    
            New-Object psobject -Property @{
                CPU = $CPU
                RAM = $RAM
                Disk = $Disk
            }
        }
    
        # Code here locally to determine which host to use from the $serverMetrics array.  Each object will have a PSComputerName property automatically added by PSRemoting.
    
        $UseHost.PSComputerName
    
    }
    
    $Result = Pick-VMHost
    

You must be logged in to reply to this topic.