mardi 22 avril 2014

VMWare/Powershell - Get Virtual Machine CPU/RAM/Disk/Network usage reports minute by minute and without aggregation of time units

Hi !

The initial goal here is to measure the Virtual machines activity hosted on a ESXi infrastructure. We want to measure activity with an interval of one minute during a period of 24 hours.

The major problem is that Veeam One (tool we use to monitor our virtualized infrastructure), makes aggregation of time units to restrict the size of database and to accelerate reports and graphs generation.

Veeam One, despite the configuration performed at the VMWare hypervisor itself, these rules apply:
  • Raw data (data with 20-second resolution) is stored for 1 hour.
  • After 1 hour, raw data is aggregated to 5-minute resolution data.
  • After 1 week, data with 5-minute resolution is aggregated to 2-hour resolution data. Data with this level of detail is stored in the database for up to 1 year.

We opened a Veeam online ticket to determine with them if there was a possibility to change this behavior and respond well to our needs. Here is the central point of their response:
  • Veeam One Reporter is not able to send out reports with the discression of 1 minute
  • Veeam One Monitor does not send out reports

Bad news :-(

Let's go to script ^^

First sterp, download VMware vSphere PowerCLI: 


Ok, let's load the snap-in which allow us to use the VMWare cmdlets to automate tasks and managementof virtualized infrastructure:
Add-PSSnapin VMware.VimAutomation.Core

Connect to VCenter server :
Connect-VIServer –Server VotreServeurVcenter

Retrieve, for example, CPU values in %, minute by minute, for yesterday :
$statCPU = Get-Stat -Entity $vmToQuery -Stat cpu.usage.average -Start $start -Finish $stop | where{$_.Instance -eq ""}

The simplest way: 
$statCPU = Get-Stat -Entity $vmToQuery -CPU -Start $start -Finish $stop | where{$_.Instance -eq ""}

We get an array with the CPU values in %, minute by minute according to the specified interval.

For example, yesterday with a 24h period, you can use:
$start = (Get-Date -Hour 0 -Minute 0 -Second 0).AddDays(-1)
$stop = (Get-Date -Hour 23 -Minute 59 -Second 0).AddDays(-1)

The first idea is to use this array to generate an Excel file with values in % and timestamp. The second idea is to generate a graph plotting the curves according to the data.

To make these graphs, we can use the great .net 3.5 (or higher) framework : 
[void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms.DataVisualization")

Create the chart and the chartarea :
$chart = new-object System.Windows.Forms.DataVisualization.Charting.Chart
$chartarea = new-object system.windows.forms.datavisualization.charting.chartarea

Give appropriate dimensions:
$chart.width = 1500
$chart.Height = 600
$chart.Left = 40
$chart.top = 30

Manage the title:
$titlefont=new-object system.drawing.font("ARIAL",12,[system.drawing.fontstyle]::bold)
$title =New-Object System.Windows.Forms.DataVisualization.Charting.title
$chart.titles.add($title)
$chart.titles[0].text = "CPU/Ram - " + $vmToQuery + " (" + $reportDate + ")"
$chart.titles[0].font = $titlefont
$chart.titles[0].forecolor = "Black"
$chart.Name = $vmToQuery
$chart.BackColor = [System.Drawing.Color]::White

A little bit of colors :-) (Never hesitate to look at the complete list here: http://msdn.microsoft.com/fr-fr/library/system.drawing.color_properties(v=vs.90).aspx) :
$chartarea.BackColor = [System.Drawing.Color]::FloralWhite

Ok, add the chartarea to your chart: 
$chart.ChartAreas.Add($chartarea)

$legend = New-Object system.Windows.Forms.DataVisualization.Charting.Legend
$chart.Legends.Add($legend)

Add the vertical legend of the X-axis with a 15 minutes interval (each point represents one minute)
$chartarea.AxisX.LabelStyle.Angle = -90
$chartarea.AxisX.Interval = 15 

Following lines allow to make a series with a data type and thus if we want, manage different traces on the same graph:
$chart.Series.Add("CPU")
$chart.Series["CPU"].ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Line
$chart.Series["CPU"].color = "blue"

Go through the array, and add each value to a psobject (http://msdn.microsoft.com/en-us/library/system.management.automation.psobject(v=vs.85).aspx)  

Then, we cast our timestamp and we add our points to the chart series "CPU" ($chart.Series["CPU"].Points.AddXY($dtToString, $t.Value)):

 foreach($t in $statCPU) {  
    $out = new-object psobject

    $out | Add-Member noteproperty timeStampCPU $t.Timestamp
    $out | Add-Member noteproperty valueCPU $t.Value
   
    $dtToString = $t.Timestamp
    [string]$dtToString = $dtToString -f "hh:mm"

    $chart.Series["CPU"].Points.AddXY($dtToString, $t.Value)

    $result += $out   

$chartarea.AxisY.Title = $unitCPU

$filename = $logFolder + "\" + $vmToQuery + "_" + $reportDate + ".png"
$chart.SaveImage($filename, "PNG")

CSV export (parameter –append needs Powershell v3.0) :
$result | Export-CSV -path $logFile –append –NoTypeInformation  

I consolidated the CPU and RAM usage information on the same graph to finally get this:


Same things for Network and Disk usage:

$statRAM = Get-Stat -Entity $vmToQuery -Stat mem.usage.average -Start $start -Finish $stop
$statNetwork = Get-Stat -Entity $vmToQuery -Network -Start $start -Finish $stop
$statDisk = Get-Stat -Entity $vmToQuery -Disk -Start $start -Finish $stop  

You can get this result:






Have fun :-)