webCommander and Google Table Integration

Introduction

This time, I decided to integrate Google Table with webCommander. It will be very similar to what I’ve already written, Google Chart integration and it can be found here.

Two benefits of using Google Table is:

  1. Sort functionality is embedded into the table
  2. Allows easier writing/managing scripts

These will be discussed and explained in more depth through the blog.

Configuration

Let us look at an example of Google Table (link is here) first:

<html>
  <head>
    <scripttype='text/javascript'src='https://www.google.com/jsapi'></script>
    <scripttype='text/javascript'>
      google.load('visualization','1',{packages:['table']});
      google.setOnLoadCallback(drawTable);
      function drawTable(){
        var data =new google.visualization.DataTable();
        data.addColumn('string','Name');
        data.addColumn('number','Salary');
        data.addColumn('boolean','Full Time Employee');
        data.addRows([
          ['Mike',  {v:10000, f:'$10,000'},true],
          ['Jim',   {v:8000,   f:'$8,000'},  false],
          ['Alice',{v:12500, f:'$12,500'},true],
          ['Bob',   {v:7000,  f:'$7,000'},  true]
        ]);

        var table =new google.visualization.Table(document.getElementById('table_div'));
        table.draw(data,{showRowNumber:true});
      }
    </script>
  </head>

  <body>
    <divid='table_div'></div>
  </body>
</html>

Take a closer look at the underlined above, which are the structure of how columns and rows should be defined.

Same as last Google Chart integration, I defined a xsl:if instance in webCmd.xsl. I called it <xsl:if test=”result/table”>:

<xsl:if test="result/table">
<html>
  <head>
    <scripttype='text/javascript'src='https://www.google.com/jsapi'></script>
    <scripttype='text/javascript'>
      google.load('visualization','1',{packages:['table']});
      google.setOnLoadCallback(drawTable);
      function drawTable(){
        var data =new google.visualization.DataTable();
        data.addColumn('string','Name');
        data.addColumn('number','Salary');
        data.addColumn('boolean','Full Time Employee');
        data.addRows([
          ['Mike',  {v:10000, f:'$10,000'},true],
          ['Jim',   {v:8000,   f:'$8,000'},  false],
          ['Alice',{v:12500, f:'$12,500'},true],
          ['Bob',   {v:7000,  f:'$7,000'},  true]
        ]);

        var table =new google.visualization.Table(document.getElementById('table_div'));
        table.draw(data,{showRowNumber:true});
      }
    </script>
  </head>
  
  <divid='table_div'></div></html>
</xsl:if>

It’s time to replace underlined static values into variables. JavaScript and xsl works quite well that it is simple enough to define a JavaScript variable and value to be retrieved using xsl. However, I realised that JavaScript variables were not necessary to be defined. What I mean is:

 <xsl:if test="result/table">
    <head>
    <script type='text/javascript' src='https://www.google.com/jsapi'></script>
    <script type='text/javascript'>
        google.load('visualization', '1', {packages:['table']});
        google.setOnLoadCallback(drawTable);
        function drawTable() {
        var data = new google.visualization.DataTable();
        <xsl:value-of select="result/table/listcolumns" />
        <xsl:value-of select="result/table/listrows" />
        var table = new google.visualization.Table(document.getElementById('table_div'));
        table.draw(data, {showRowNumber: true});
        }
    </script>
    </head>
    <div id='table_div'></div>
</xsl:if>

It looks much cleaner, isn’t it?

Now, it is time to load columns and rows, i.e. list columns & listrows from PowerCLI. I wrote a simple PowerCLI function called Google-Table. What this function do is that it converts the result into columns & rows format that Google Table recognises. Bear in mind that the result I am referring here is a normal output, for example Get-VMHost | Select Name, NumCPU. This function could be inserted into objects.ps1 file and loaded from your script:

Update: added one more parameter, attributes. The reason was that the columns defined by the user was sorted automatically.

 function Google-Table {
    Param($attributes,$output)
    Write-Output "<table>"
    $property = Get-Member -InputObject $output | where {$_.MemberType -eq "NoteProperty"}
    $listcolumns = ""
    $listrows = "data.addRows(["    
    # Column
    foreach ($a in ($attributes -split ",")) {
        $definition  = $property | where {$_.Name -eq $a} | %{$_.Definition}
        if ($definition -match "System.Int32" -or $definition -match "System.Decimal") {
            $listcolumns += "data.addColumn('number', '$a');"
        } else {
            $listcolumns += "data.addColumn('string', '$a');"
        }
    }    
    # Row
    foreach ($row in $output) {
        $listrows += "["
        foreach ($a in ($attributes -split ",")) {
            $definition  = $property | where {$_.Name -eq $a} | %{$_.Definition}
            if ($definition -match "System.Int32" -or $definition -match "System.Decimal") {
                $listrows += $row | %{$_.($a)}
            } else {
                $listrows += "'"
                $listrows += $row | %{$_.($a)}
                $listrows += "'"
            }
            $listrows += ","
        }
        $listrows = $listrows -replace ",$"
        $listrows += "],"
    }
    $listrows = $listrows -replace ",$"
    $listrows += "]);"
    Write-Output "<listcolumns>$listcolumns</listcolumns>"
    Write-Output "<listrows>$listrows</listrows>"
    Write-Output "</table>"
}

Time to run script. For the testing, I used NTP-Query script which can be found here:

. .\objects.ps1
$attributes = "Cluster,ESXi,NTPServer,Service,Policy,Running"
$result = '' | select Cluster,ESXi,NTPServer,Service,Policy,Running 
$output = foreach ($esxi in (Get-Cluster -Name $cluster | Get-VMHost | Sort Name)) {         
    $service = Get-VMHostService -VMHost $esxi | Where {$_.Key -eq "ntpd"}
    $policy = switch ($service.Policy) { 
                "off" { "Start and stop manually"} 
                "on" { "Start and stop with host" } 
                "automatic" { "Start automatically if any ports are open, and stop when all ports are closed" }
              }
    $ntp = Get-VMHostNtpServer -VMHost $esxi
    $result.Cluster = $esxi.Parent
    $result.ESXi = $esxi.Name
    $result.NTPServer = $ntp
    $result.Service = $service.Label
    $result.Policy = $policy
    $result.Running = $service.Running
  }

Google-Table $attributes,$output

Let me re-cap one of the benefits I outlined above, “Allows easier writing/managing scripts”. Why? Here is an example of what I’ve been using:

. .\objects.ps1
$attributes = "Cluster,ESXi,NTPServer,Service,Policy,Running"
$result = '' | select Cluster,ESXi,NTPServer,Service,Policy,Running 
$output = foreach ($esxi in (Get-Cluster -Name $cluster | Get-VMHost | Sort Name)) {         
    $service = Get-VMHostService -VMHost $esxi | Where {$_.Key -eq "ntpd"}
    $policy = switch ($service.Policy) { 
                "off" { "Start and stop manually"} 
                "on" { "Start and stop with host" } 
                "automatic" { "Start automatically if any ports are open, and stop when all ports are closed" }
              }
    $ntp = Get-VMHostNtpServer -VMHost $esxi
    $result.Cluster = $esxi.Parent
    $result.ESXi = $esxi.Name
    $result.NTPServer = $ntp
    $result.Service = $service.Label
    $result.Policy = $policy
    $result.Running = $service.Running
  }
foreach ($information in $output) {    
    $cluster = $Cluster
    $esxi = $information.Name
    $ntpserver = $information.NTPServer
    $service = $information.NTPService
    $status = $information.Status

    write-output "<custom>"
    write-output "<cluster>$cluster</cluster>"
    write-output "<esxi>$esxi</esxi>"
    write-output "<ntpserver>$ntpserver</ntpserver>"
    write-output "<service>$service</service>"
    write-output "<status>$status</status>"
    write-output "</custom>"
} 

Do you see the difference (Bold and Underlined) between two? Instead of parsing xsl values to webCmd.xsl manually, i.e. Write-Output …… a simple Google-Table function could be loaded, no matter how many columns you define.

Everything is in place. Time to run the script via webCommander.

Screen Shot 2014-05-30 at 10.01.29 am

 

Looks pretty, isn’t it? Make sure you try sorting.

Future

In future, I will be trying to create a dashboard based on Google Chart and Table under a static page. This is going to be a tricky one but hopefully I make it happen.

Hope you guys have fun and always welcome to ping me for any corrections/questions 🙂

PowerCLI – Alarms

Introduction

I’ve decided to share my PowerCLI scripts that I think were useful.

First script I would like to share is to generate Alarm definitions. One thing to notice is that this script will only load alarms with Actions, e.g. Send SNMP, Send Mail…etc.

The following script should be executed once you are connected to vCenter server(s).

Script

output = foreach ($alarm in (Get-AlarmDefinition | Sort Name | Get-AlarmAction))
{
    $threshold = foreach ($expression in ($alarm | %{$_.AlarmDefinition.ExtensionData.Info.Expression.Expression}))
    {
        if ($expression.EventTypeId -or ($expression | %{$_.Expression}))
        {
            if ($expression.Status) { switch ($expression.Status) { "red" {$status = "Alert"} "yellow" {$status = "Warning"} "green" {$status = "Normal"}}; "" + $status + ": " + $expression.EventTypeId } else { $expression.EventTypeId }
        }
        elseif ($expression.EventType)
        {
            $expression.EventType
        }
        if ($expression.Yellow -and $expression.Red)
        {
            if (!$expression.Yellow) { $warning = "Warning: " + $expression.Operator } else { $warning = "Warning: " + $expression.Operator + " to " + $expression.Yellow };
            if (!$expression.Red) { $alert = "Alert: " + $expression.Operator } else { $alert = "Alert: " + $expression.Operator + " to " + $expression.Red };
            $warning + " " + $alert
        }
    }
    $alarm | Select-Object @{N="Alarm";E={$alarm | %{$_.AlarmDefinition.Name}}},
                           @{N="Description";E={$alarm | %{$_.AlarmDefinition.Description}}},
                           @{N="Threshold";E={[string]::Join(" // ", ($threshold))}},
                           @{N="Action";E={if ($alarm.ActionType -match "SendEmail") { "" + $alarm.ActionType + " to " + $alarm.To } else { "" + $alarm.ActionType }}}
}
$output | Export-Csv alarm.csv -UseCulture -NoTypeInformation

Sample Output

Alarm Description Threshold Action
Cannot connect to storage Default alarm to monitor host connectivity to storage device vprob.storage.connectivity.lost // vprob.storage.redundancy.lost // vprob.storage.redundancy.degraded // esx.problem.vmfs.nfs.server.disconnect SendSNMP
Exit standby error Default alarm to monitor if a host cannot exit standby mode ExitStandbyModeFailedEvent // DrsExitStandbyModeFailedEvent SendSNMP
Health status changed alarm Default alarm to monitor changes to service and extension health status HealthStatusChangedEvent SendSNMP
Virtual Machine with Snapshots Testing alarm to notify if someone creates a snapshot on a certain virtual machine. Warning: isAbove to 1048576 Alert: isAbove to 2097152

Hope this helps to people who wants to review alarms created within vCenter servers.

vMotion VMKernel Missing

Introduction

Recently, DRS was failing to vMotion virtual machines from a specific ESXi server. The error message was:

The vMotion interface is not configured (or is misconfigured) on the Source host “esxi.test.com”

error

Checked the VMKernels on the dvSwitch and there was only one management VMKernel. vMotion VMKernel was disappeared.

To identify the issue, I’ve tried:

  • Adding a new vMotion VMKernel
  • esxcli to check the status of VMKernels

Identification

First attempt was to add a vMotion VMKernel to dvSwitch. However, got an error message saying the VMKernel already exists.

error_1

This was telling me that somehow, the vMotion VMKernel is hidden. Next step performed was to use esxcli to identify the issue.

Running esxcli network ip interface list, gave me an interesting output. vmk1 for vMotion was disabled.

interface_list

Resolution

The output above gave me a clear idea on what’s mis-configured. The first thing I tried was to enable vmk1 by running esxcli network ip interface set  -e true -i vmk1. But, it outputted an error message saying:

Sysinfo error on operation returned status: Operation not permitted. Please see the VMKernel log for detailed error information

Enabling the VMKernel didn’t work as the ESXi thought vMotion VMKernel was busy. So, the next configuration I tried was to delete the VMKernel, esxcli network ip interface remove -i vmk1. Surprisingly, it didn’t cause any problems! Listed VMKerenls and confirmed that vMotion VMKernel was removed.

interface_list_1

Added a new vMotion VMKernel and DRS was able to execute vMotion virtual machines successfully.

Wrap-Up

Unfortunately, I wasn’t able to find the root cause of this problem. In future if I face this issue again, I will update this post.