PowerShell based Monitoring and Reporting

A while ago I was asked to look at the tools available for monitoring storage and VMware related resources. I was overwhelmed by the number of options available as well as the complexity involved in setting up, maintaining and learning how to use some of them. One such product is vCenter Operations Manager, this being a beast of a product.

Initially, I was asked to provide a simple report showing storage utilization on NetApp filers as well as the resources used by a number of deployed ESXi hosts. Deploying a fully-fledged monitoring tool was at this point overkill so I decided to dive in and try and build something myself. I identified the following components required for the task at hand;

  • Windows 2008 R2 server + IIS
  • DataOntap Powershell Module + install.ps1 script
  • Vmware PowerCLI v4 or later release
  • An HTML editor
  • Familiarity with Powershell
  • Familiarity with Microsoft Chart Controls for the .NET framework

To cut a long story short, the project grew to encompass other areas where monitoring and reporting was required but as the virtualised and storage environments grew, dispersed and increased in complexity, the project outgrew its purpose in favour of commercially available solutions, obviously far better suited for enterprise scale monitoring.

Nevertheless, for small deployments and where cost is an issue (isn’t it always?), a little bit of elbow grease goes a long way provided you’re prepared to put in the effort. If anything, it gives you insight on how things work.Below is a screenshot and a video of the end result. In essence, it’s a basic html page using Iframes to display a menu at the top and a results pane below it. It is hosted on an IIS web server (you need to add a mime type for csv files) running on Windows Server 2008. MS Expression Web was used for editing.

Capture
NetApp aggregate storage utilisation

The table below is an an example of one of the available reports.  The table lists the ESXi servers comprising each cluster discovered. A breakdown of the individual hosts and their cumulative resource utilization per cluster is also given.

2

I also included a graph showing the growth rate of the virtual machines deployed over a fixed period of time. The drops indicate instances where the script(s) either stopped working or did not manage, for whatever reason, to connect to a vCenter server. I could easily have fixed it by trapping the exception but never really bothered to; my bad, I know!

3

Next is a chart displaying networked storage utilization, in this case NetApp volumes.

4

The method used to record data eventually became unwieldy mainly because the environments and resources kept growing at a rate faster than the time I required to update and maintain the scripts. One other drawback was the file based method – csv format – I chose to store data gathered by the scripts. In hindsight this was an unwise decision and should have used a proper DBMS solution using MySQL or SQL Server Express for instance. Note to self for the next project.

Capture (1)

As mentioned, the data gathered is stored in a csv file which is parsed as needed to generate graphs and reports.

All in all it’s been a worthwhile experience given the many lessons learned.

There’s one script I can share that does not require any editing – removing host names, addresses, etc. The script reads the data collected from the previous script run. The tricky bit is knowing about hash tables and how to use them to store the dataset used to generate the graph. There are countless examples of how to do this on the net!.

Last but not least, there’s no error trapping included and the code can probably be optimized. I am not a developer by profession so please feel free to amend.


Script:

#Assemblies for Chart controls
[void][Reflection.Assembly]::LoadWithPartialName(“System.Windows.Forms”)
[void][Reflection.Assembly]::LoadWithPartialName(“System.Windows.Forms.DataVisualization”)
#These are the fields (columns) we need to read from the csv file. They contain values representing used/free aggregate storage on Netapp(s)
#Node1
$PHX_agg1_Free=”F4″ ;$PHX_agg1_Total=”F6″
$PHX_agg2_Free=”F8″ ;$PHX_agg2_Total=”F10″
$PHX_agg3_Free=”F12″ ;$PHX_agg3_Total=”F14″
$PHX_agg4_Free=”F16″ ;$PHX_agg4_Total=”F18″
$PHX_aggName_1=”F3″
$PHX_aggName_2=”F7″
$PHX_aggName_3=”F11″
$PHX_aggName_4=”F15″
#Node2
$AMS_aggName_1=”F20″
$AMS_agg1_Free=”F21″
$AMS_agg1_Total=”F23″;
#Node3
$ASH_aggName_1=”F25″
$ASH_agg1_Free=”F26″
$ASH_agg1_Total=”F28″;
#File Paths
$global:f_NetappRawdata_csv=”C:\inetpub\CloudStats\WWW\Parent\RawData\NetappData.csv”
$global:ChartPath=”C:\inetpub\CloudStats\WWW\Parent\Graphs\Netapp\”
#######################################################################
#Create chart object
Function CreateChart{param([string]$Site)
$Chart = New-object System.Windows.Forms.DataVisualization.Charting.Chart
$Chart.Width = 1000
$Chart.Height = 700
#Create a chartarea to draw on and add to chart
$ChartArea = New-Object System.Windows.Forms.DataVisualization.Charting.ChartArea
$Chart.ChartAreas.Add($ChartArea)
#Get data from csv (Cumulative data generated by main script)
$Data=(Import-Csv $global:f_NetappRawdata_csv -Delimiter “,” -Header(‘F1′,’F2′,’F3′,’F4′,’F5′,’F6′,’F7′,’F8’, `
‘F9′,’F10′,’F11′,’F12′,’F13′,’F14′,’F15’, `
‘F16′,’F17′,’F18′,’F19′,’F20′,’F21′,’F22’, `
‘F23′,’F24′,’F25′,’F26′,’F27′,’F28’))
#Hashtables will contain the dataseries needed to construct the graphs
hashtable]$sTotal=@{}; [hashtable]$sFree=@{} ; [hashtable]$sUsed=@{}
$xVal=$null;$yVal=$null
if ($site -eq “PHX”)
{
$dFreeAgg1=($Data[$Data.length-1].$PHX_agg1_Free)
$sFree.Add($Data[0].$PHX_aggName_1,$dFreeAgg1)
$sTotal.Add($Data[0].$PHX_aggName_1,$Data[$Data.length-1].$PHX_agg1_Total)
$sUsed.Add($Data[0].$PHX_aggName_1,$Data[$Data.length-1].$PHX_agg1_Total-$Data[$Data.length-1].$PHX_agg1_Free)
$dFreeAgg2=($Data[$Data.length-1].$PHX_agg2_Free)
$sFree.Add($Data[0].$PHX_aggName_2,$dFreeAgg2)
$sTotal.Add($Data[0].$PHX_aggName_2,$Data[$Data.length-1].$PHX_agg2_Total)
$sUsed.Add($Data[0].$PHX_aggName_2,$Data[$Data.length-1].$PHX_agg2_Total-$Data[$Data.length-1].$PHX_agg2_Free)
$dFreeAgg3=($Data[$Data.length-1].$PHX_agg3_Free)
$sFree.Add($Data[0].$PHX_aggName_3,$dFreeAgg3)
$sTotal.Add($Data[0].$PHX_aggName_3,$Data[$Data.length-1].$PHX_agg3_Total)
$sUsed.Add($Data[0].$PHX_aggName_3,$Data[$Data.length-1].$PHX_agg3_Total-$Data[$Data.length-1].$PHX_agg3_Free)
$dFreeAgg4=($Data[$Data.length-1].$PHX_agg4_Free)
$sFree.Add($Data[0].$PHX_aggName_4,$dFreeAgg4)
$sTotal.Add($Data[0].$PHX_aggName_4,$Data[$Data.length-1].$PHX_agg4_Total)
$sUsed.Add($Data[0].$PHX_aggName_4,$Data[$Data.length-1].$PHX_agg4_Total-$Data[$Data.length-1].$PHX_agg4_Free) }
if ($site -eq “AMS”)
{
$sFree.Add($Data[0].$AMS_aggName_1,$Data[$Data.length-1].$AMS_agg1_Free)
$sTotal.Add($Data[0].$AMS_aggName_1,$Data[$Data.length-1].$AMS_agg1_Total)
$sUsed.Add($Data[0].$AMS_aggName_1,$Data[$Data.length-1].$AMS_agg1_Total-$Data[$Data.length-1].$AMS_agg1_Free)
}
if ($site -eq “ASH”)
{
$sFree.Add($Data[0].$ASH_aggName_1,$Data[$Data.length-1].$ASH_agg1_Free)
$sTotal.Add($Data[0].$ASH_aggName_1,$Data[$Data.length-1].$ASH_agg1_Total)
$sUsed.Add($Data[0].$ASH_aggName_1,$Data[$Data.length-1].$ASH_agg1_Total-$Data[$Data.length-1].$ASH_agg1_Free)
}
#Populate Data Series
$Chart.Series.Add(“Total”)
$Chart.Series[“Total”].Points.DataBindXY($sTotal.Keys,$sTotal.Values)
$Chart.Series.Add(“Free”)
$Chart.Series[“Free”].Points.DataBindXY($sFree.Keys,$sFree.Values)
$Chart.Series.Add(“Used”)
$Chart.Series[“Used”].Points.DataBindXY($sUsed.Keys,$sUsed.Values)
#Legend
$legend = New-Object system.Windows.Forms.DataVisualization.Charting.Legend
$legend.name = “Legend1”
$Chart.Legends.Add($legend)
#Title and axis labels
if ($site -eq “PHX”)
{
$Chart.Titles.Add(“Netapp Aggregate Utilization (Filer-B)”)}
else {$Chart.Titles.Add(“Netapp Aggregate Utilization”)}
$chartArea.AxisX.Title = “Aggregates”
$ChartArea.AxisY.Title = “Terabytes”
$ChartArea.AxisX.Linewidth=1
$ChartArea.AxisY.Linewidth=1
$ChartArea.AxisY.Interval=2
$ChartArea.AxisX.Interval=1
#Cosmetic changes
$ChartArea.Area3DStyle.Enable3D=$True
$ChartArea.Area3DStyle.Perspective=10
$Chart.Series[“Total”][“PointWidth”]=”0.4″
$Chart.Series[“Used”][“PointWidth”]=”0.4″
$Chart.Series[“Free”][“PointWidth”]=”0.4″
$Chart.Series[“Total”].label = “#VALY”
$Chart.Series[“Used”].label = “#VALY”
$Chart.Series[“Free”].label = “#VALY”
$Chart.Titles[0].Font = “Arial Rounded,14pt”
$Chart.BackColor = [System.Drawing.Color]::Transparent
$Chart.Series[“Total”][“DrawingStyle”] = “Cylinder”
$Chart.Series[“Used”][“DrawingStyle”] = “Cylinder”
$Chart.Series[“Free”][“DrawingStyle”] = “Cylinder”
$Chart.Series[“Total”].Color = [System.Drawing.Color]::LightGreen
$Chart.Series[“Total”].ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Column
$Chart.Series[“Used”].Color = [System.Drawing.Color]::LightBlue
$Chart.Series[“Used”].ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Column
$Chart.Series[“Free”].Color = [System.Drawing.Color]::Red
$Chart.Series[“Free”].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Column
#Save to file
$Chart.SaveImage($global:ChartPath+$Site+”_aggregates.png”, “PNG”)}
CreateChart “PHX”
CreateChart “AMS”
CreateChart “ASH”

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s