VMware Cloud Community
G0nz0UK
Enthusiast
Enthusiast

Possible to retrieve the combine total CPU and Memory of all VMs that are in a given VLAN/vSwitch?

Hello,

We have various network as you would expect in vCenter that our many VMs are placed into.  For example in one of our Distributed Switches we have a network named VLAN119 that is a dev VLAN with about 40 VMs in it.

I've been asked to output the the combined GHz CPU these VMs use every 2mins and their total RAM to csv or to an Influx DB so it can be graphed in Grafana.

Has anyone tried a PS script like this before as I'm not sure it's even possible?  So if you are in this network called 'VLAN123' then give me the combined CPU in GHz and memory.

We can then work out what teams use what amount of CPU and memory out of the total for that cluster.

DEV (VLAN119) CPU = xGHz

DEV (VLAN119) Mem = xRAM

PROD (VLAN123) etc etc

I hope that made sense.

Reply
0 Kudos
39 Replies
G0nz0UK
Enthusiast
Enthusiast

No problem, I'll do some research.

Reply
0 Kudos
G0nz0UK
Enthusiast
Enthusiast

Hi @LucD 

I've got me InfluxDB server working again, so I thought I'd give this one more try.  What am I doing wrong here with this error do you think?

 

$pgName = 'WHT_VLAN108','WHT_VLAN109'
Get-VDPortgroup -Name $pgName -PipelineVariable vdpg |

$vms = Get-VDPortgroup -Name $pgName | Get-VM | Sort-Object -Property Name
$sStat = @{
  Entity = $vms.Where{$_.PowerState -eq 'PoweredOn'}
  Stat = 'cpu.usageMhz.average', 'mem.granted.average'
  Realtime = $true
  Start = (Get-Date).AddMinutes(-2)
  Instance = ''
}
$tabVM = @{}
Get-Stat @SStat | Group-Object -Property {$_.Entity.Name} |
ForEach-Object -Process {
  $tabVM.Add($_.Name,$_.Group)
}
$result = $vms | ForEach-Object -Process {
  $obj = New-Object -TypeName PSObject -Property ([ordered]@{
      VM = $_.Name
      'Provisioned Space' = [math]::Round($_.ProvisionedSpaceGB, 2)
      'Used Space' = [math]::Round($_.UsedSpaceGB, 2)
      CPUMHz = 0
      MemGB = 0
  })
  if($tabVm.ContainsKey($_.Name)){
      $obj.CPUMHz = [math]::Round(($tabVM[$_.Name].Where{ $_.MetricId -eq 'cpu.usageMhz.average' }.Value | Measure-Object -Average).Average, 0)
      $obj.MemGB = [math]::Round(($tabVM[$_.Name].Where{ $_.MetricId -eq 'mem.granted.average' }.Value | Measure-Object -Average).Average / 1MB, 2)
  }
  $obj
} | Measure-Object -Property 'Provisioned Space','Used Space',CPUMHz,MemGB -Sum

$metrics = @{
    Portgroup = $pgName
    'Provisioned Space' = $result.Where{ $_.Property -eq 'Provisioned Space' }.Sum
    'Used Space' = $result.Where{ $_.Property -eq 'Used Space' }.Sum
    CPUMHz = $result.Where{ $_.Property -eq 'CPUMHz' }.Sum
    MemGB = $result.Where{ $_.Property -eq 'MemGB' }.Sum
}

Write-Influx -Measure CorpCombinedStats -Tags @{host="Corp"} -Metrics $Metrics -Database "tempDB2" -Server http://10.1.2.5:8086 -Credential $credentialsinflux
    Start-Sleep -Seconds 10

 

 

Error

 At C:\Scripts\VMware\vmware-combined-stats-influx.ps1:16 char:1

+ $vms = Get-VDPortgroup -Name $pgName | Get-VM | Sort-Object -Property ...

+ ~~~~

Expressions are only allowed as the first element of a pipeline.

    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException

    + FullyQualifiedErrorId : ExpressionsMustBeFirstInPipeline 

Reply
0 Kudos
LucD
Leadership
Leadership

The 2nd line has a pipeline symbol at the end.
And I  thnk this 2nd line is not needed at all.

Get-VDPortgroup -Name $pgName -PipelineVariable vdpg |


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
G0nz0UK
Enthusiast
Enthusiast

Oh ok, I'll try that shortly thanks!

Regarding the other fantastic script where it pushes the data to CSV. I've managed to push the CSV into the InfluxDB, but it doesn't like the fact that I have no time column at the start for when the data was polled/pulled.

What would need to be added for that?

 

$pgName = 'WHT_VLAN108','WHT_VLAN109'
Get-VDPortgroup -Name $pgName -PipelineVariable vdpg |

ForEach-Object -Process {
    $vms = Get-VM -RelatedObject $vdpg | Sort-Object -Property Name
    $sStat = @{
        Entity = $vms.Where{ $_.PowerState -eq 'PoweredOn' }
        Stat = 'cpu.usageMhz.average', 'mem.granted.average'
        Realtime = $true
        Start = (Get-Date).AddMinutes(-2)
        Instance = ''
        ErrorAction = 'SilentlyContinue'
    }
    $tabVM = @{}
    Get-Stat <li-user uid="2039969" login="SStat"></li-user> | Group-Object -Property { $_.Entity.Name } |
    ForEach-Object -Process {
        $tabVM.Add($_.Name, $_.Group)
    }
    $result = $vms | ForEach-Object -Process {
        $obj = New-Object -TypeName PSObject -Property ([ordered]@{
                Portgroup = $vdpg.Name
                VM = $_.Name
                'Provisioned Space' = [math]::Round($_.ProvisionedSpaceGB, 2)
                'Used Space' = [math]::Round($_.UsedSpaceGB, 2)
                CPUMHz = 0
                MemGB = 0
            })
        if ($tabVm.ContainsKey($_.Name)) {
            $obj.CPUMHz = [math]::Round(($tabVM[$_.Name].Where{ $_.MetricId -eq 'cpu.usageMhz.average' }.Value | Measure-Object -Average).Average, 0)
            $obj.MemGB = [math]::Round(($tabVM[$_.Name].Where{ $_.MetricId -eq 'mem.granted.average' }.Value | Measure-Object -Average).Average / 1MB, 2)
        }
        $obj
    } | Measure-Object -Property 'Provisioned Space', 'Used Space', CPUMHz, MemGB -Sum

    New-Object -TypeName PSObject -Property ([ordered]@{
        Portgroup = $vdpg.Name
        'Provisioned Space' = $result.Where{ $_.Property -eq 'Provisioned Space' }.Sum
        'Used Space' = $result.Where{ $_.Property -eq 'Used Space' }.Sum
        CPUMHz = $result.Where{ $_.Property -eq 'CPUMHz' }.Sum
        MemGB = $result.Where{ $_.Property -eq 'MemGB' }.Sum
    })
} | Export-Csv -Path "C:\vmware\powercli\reports\vmware-stats.csv" -NoTypeInformation -UseCulture

 

 

Thanks!

Reply
0 Kudos
G0nz0UK
Enthusiast
Enthusiast

Oh wow and on the InfluxDB PS script it does now go into InfluxDB, well done! 

However the 'Portgroup' names don't seem to be on separate lines

G0nz0UK_0-1673456233782.png

In Grafana I see 'Portgroup' appear like this, instead of 'where' as it should be listed under 'host' as an option, then after that I should be able to show select the portgroup/vlan, I can't think why it would be like this:

G0nz0UK_1-1673456567074.png

 

So close on both!

 

Reply
0 Kudos
LucD
Leadership
Leadership

Are the values in the CSV for the Portgroup column correct?


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
G0nz0UK
Enthusiast
Enthusiast

Sure are:

G0nz0UK_0-1673458249612.png

 

It would be great to have the CSV show the timestamp in column 1 and also as another option got InfluxDB right.

So excited!  Crazy what can be done.

 

Reply
0 Kudos
LucD
Leadership
Leadership

Not sure what datetime format Influx requires, but you can add a Timestamp like this

New-Object -TypeName PSObject -Property ([ordered]@{
  Timestamp = (Get-Date)
  Portgroup = $vdpg.Name
  'Provisioned Space' = $result.Where{ $_.Property -eq 'Provisioned Space' }.Sum
  'Used Space' = $result.Where{ $_.Property -eq 'Used Space' }.Sum
  CPUMHz = $result.Where{ $_.Property -eq 'CPUMHz' }.Sum
  MemGB = $result.Where{ $_.Property -eq 'MemGB' }.Sum
}) 


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
G0nz0UK
Enthusiast
Enthusiast

Thanks, well that certainly added the timestamp to the CSV but InfluxDB/Grafana doesn't seem to want to use it so I can't graph it, so I'll do some research on the Influx/Grafana site for that I think.

Up above you managed to get the data into InfluXDB so maybe that is the route to go, but there is a slight issue on how the PS script is sending it to InfluxDB where those 'PortGroups' need to be where the 'where' option like this below.  From the dropdown menu I get the option of 'host' and 'vcenter' from the script, but I was hoping it would list all the 'Portgroups' I have added to the script.

Do you think this is even possible?

G0nz0UK_0-1673522872044.png

 

Reply
0 Kudos
LucD
Leadership
Leadership

It looks like InfluxDB wants the timestamp in epoch time.
Can you try this?

New-Object -TypeName PSObject -Property ([ordered]@{
    Timestamp = ([System.DateTimeOffset]::new((Get-Date))).ToUnixTimeMilliseconds()
    Portgroup = $vdpg.Name
    'Provisioned Space' = $result.Where{ $_.Property -eq 'Provisioned Space' }.Sum
    'Used Space' = $result.Where{ $_.Property -eq 'Used Space' }.Sum
    CPUMHz = $result.Where{ $_.Property -eq 'CPUMHz' }.Sum
    MemGB = $result.Where{ $_.Property -eq 'MemGB' }.Sum
  })


On why InfluxDB does not seem to take Portgroup as a valid Field in a Where query, I have no clue.
You should perhaps talk to the InfluxDB DBA.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
G0nz0UK
Enthusiast
Enthusiast

I'll try that thanks.

Yeah there is no InfluxDB guy, I'm just using their Stack forum for help, the just sent me this and said don't worry about sending to CSV and use PS to send into INflux https://github.com/markwragg/PowerShell-Influx not too helpful.

Reply
0 Kudos
LucD
Leadership
Leadership

Sorry, can't help you there.

But at least in one of the issues on that repo it seems to confirm that InfluxDB needs an epochtime.
My previous suggestion for the Timestamp should work as far as I can tell.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
G0nz0UK
Enthusiast
Enthusiast

Hello,

This script you created is working so well (thanks!!), now I'm sure this is not possible, but can it exclude names of VM in the vDS port group.

For example if we have a portgroup with 200 VMs but there are 5 VMs we want excluded from the combined total is that possible?

This is the current script:

$pgName = 'WHT_VLAN108','WHT_VLAN109'
Get-VDPortgroup -Name $pgName -PipelineVariable vdpg |

ForEach-Object -Process {
    $vms = Get-VM -RelatedObject $vdpg | Sort-Object -Property Name
    $sStat = @{
        Entity = $vms.Where{ $_.PowerState -eq 'PoweredOn' }
        Stat = 'cpu.usageMhz.average', 'mem.granted.average'
        Realtime = $true
        Start = (Get-Date).AddMinutes(-2)
        Instance = ''
        ErrorAction = 'SilentlyContinue'
    }
    $tabVM = @{}
    Get-Stat @SStat | Group-Object -Property { $_.Entity.Name } |
    ForEach-Object -Process {
        $tabVM.Add($_.Name, $_.Group)
    }
    $result = $vms | ForEach-Object -Process {
        $obj = New-Object -TypeName PSObject -Property ([ordered]@{
                Portgroup = $vdpg.Name
                VM = $_.Name
                'Provisioned Space' = [math]::Round($_.ProvisionedSpaceGB, 2)
                'Used Space' = [math]::Round($_.UsedSpaceGB, 2)
                CPUMHz = 0
                MemGB = 0
            })
        if ($tabVm.ContainsKey($_.Name)) {
            $obj.CPUMHz = [math]::Round(($tabVM[$_.Name].Where{ $_.MetricId -eq 'cpu.usageMhz.average' }.Value | Measure-Object -Average).Average, 0)
            $obj.MemGB = [math]::Round(($tabVM[$_.Name].Where{ $_.MetricId -eq 'mem.granted.average' }.Value | Measure-Object -Average).Average / 1MB, 2)
        }
        $obj
    } | Measure-Object -Property 'Provisioned Space', 'Used Space', CPUMHz, MemGB -Sum

    New-Object -TypeName PSObject -Property ([ordered]@{
        Timestamp = (Get-Date)
        Portgroup = $vdpg.Name
        'Provisioned Space' = $result.Where{ $_.Property -eq 'Provisioned Space' }.Sum
        'Used Space' = $result.Where{ $_.Property -eq 'Used Space' }.Sum
        CPUMHz = $result.Where{ $_.Property -eq 'CPUMHz' }.Sum
        MemGB = $result.Where{ $_.Property -eq 'MemGB' }.Sum
    })
} | Export-Csv -Path "C:\vmware\powercli\reports\vmware-combined-stats.csv" -NoTypeInformation -UseCulture

Imagine the VMs could be Server1, Server2, Server3, LuCD1, LuCDx

Thanks

 

Reply
0 Kudos
LucD
Leadership
Leadership

You could do something like this

$pgName = 'WHT_VLAN108','WHT_VLAN109'
$excludeVM = 'Server1', 'Server2', 'Server3', 'LuCD1', 'LuCDx'
Get-VDPortgroup -Name $pgName -PipelineVariable vdpg |

ForEach-Object -Process {
    $vms = Get-VM -RelatedObject $vdpg | 
        where{$_.Name -notin $excludeVM} |
        Sort-Object -Property Name


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

G0nz0UK
Enthusiast
Enthusiast

Amazing!

I don't suppose it does wildcards such as anything starting with Server* or containing *Server* etc

No worries if not.

Reply
0 Kudos
LucD
Leadership
Leadership

You can combine the conditions in the Where-clause.
This also excludes names starting with "Server".
 (the ^ symbol indicates the start of the String in RegEx)

With -match and -notmatch you can use RegEx to specify the condition

ForEach-Object -Process {
    $vms = Get-VM -RelatedObject $vdpg | 
        where{$_.Name -notin $excludeVM -and $_.Name -notmatch "^Server" } |
        Sort-Object -Property Name

 


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

G0nz0UK
Enthusiast
Enthusiast

Do I need remove this line too as I no longer need to manually type each server with you Regex option?

 

$excludeVM = 'Server2', 'Server3', 'LuCD1', 'LuCDx'

 

Like this 

 

$pgName = 'WHT_VLAN108','WHT_VLAN109'
$excludeVM = 'Server2', 'Server3', 'LuCD1', 'LuCDx'
Get-VDPortgroup -Name $pgName -PipelineVariable vdpg |

ForEach-Object -Process {
    $vms = Get-VM -RelatedObject $vdpg | 
        where{$_.Name -notin $excludeVM -and $_.Name -notmatch "^Server" } |
        Sort-Object -Property Name

 

 

Reply
0 Kudos
LucD
Leadership
Leadership

If you keep the -notin operator and not all VMs are filtered by the -match or -notmatch, then yes.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
G0nz0UK
Enthusiast
Enthusiast

So if something like this if we want to exclude/filter out anything starting with Server?

$pgName = 'WHT_VLAN108','WHT_VLAN109'
Get-VDPortgroup -Name $pgName -PipelineVariable vdpg |

ForEach-Object -Process {
    $vms = Get-VM -RelatedObject $vdpg | 
        where{$_.Name -notin $excludeVM -and $_.Name -notmatch "^Server" } |
        Sort-Object -Property Name

 

Reply
0 Kudos
LucD
Leadership
Leadership

Then you leave out the -notin condition

$pgName = 'WHT_VLAN108','WHT_VLAN109'
Get-VDPortgroup -Name $pgName -PipelineVariable vdpg |

ForEach-Object -Process {
    $vms = Get-VM -RelatedObject $vdpg | 
        where{$_.Name -notmatch "^Server" } |
        Sort-Object -Property Name


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos