VMware Usage Meter API

Usage meter is a rather ugly piece of VMware software whose sole purpose is to monitor memory allocation and reservations. It is tied to licensing costs for some implementations (e.g. Service Providers). Unfortunately, it's also a major PITA to use.

Luckily, there is an API! As we have several different Usage Meter instances at each of our datacenters, I opted to make things easier with a site parameter. Depending on the site, the server/authToken are auto-populated. In the event that a different UM server needs to be targeted, the server/authToken can be provided via commandline.

A few key notes: Usage Meter time is in UTC, and in the specific format yyyyMMddHH (e.g. April 1st 2014 at noon UTC is: 2014040112).

On a side note, I'm looking into creating my own syntax higlighting extension for PowerShell as Prism doesn't appear to natively support it.


<#  
    .SYNOPSIS
    Uses the VMware Usage Meter API to return the contents of a specified report, 
    optionally bounded by date/time, and at a particular Gen2 site.

    .DESCRIPTION
    Returns a variety of reports from VMware Usage Meter based on provided report number,
    for the specified timeframe (dateFrom/dateTo) in UTC, for the given Gen 2 site.

    Reports are selected based on number:
    1  "Detailed Billing Report"
    2  "Detailed Usage Report"
    3  "Customer Summary Report"
    4  "License Summary Report"
    5  "Monthly Usage Report"
    6  "Customer Product Report"
    17 "Bundle Report"

    .EXAMPLE
    .\get-UsageMeterReport.ps1 -site JKL -dateFrom 2014041720 -dateTo 2014041721 -reportType 2

    Retrieves the Detailed Usage Report from April 17, 2014 8PM UTC - April 17, 2014 at 9PM UTC

    .PARAMETER dateFrom
    Date/Time UTC in format yyyyMMddHH  - e.g. 2014041720 for April 17, 2014 at 8PM UTC (3PM EST) for start of report

    .PARAMETER dateTo
    Date/Time UTC in format yyyyMMddHH  - e.g. 2014041721 for April 17, 2014 at 9PM UTC (4PM EST) for end of report

    .PARAMETER reportType
    Report number based on table below
    1  "Detailed Billing Report"
    2  "Detailed Usage Report"
    3  "Customer Summary Report"
    4  "License Summary Report"
    5  "Monthly Usage Report"
    6  "Customer Product Report"
    17 "Bundle Report"

    .PARAMETER site
    Specifies which site to target (ABC, DEF, GHI, JKL, MNO)

    .PARAMETER raw
    If provided, raw output from the API call will be output

    .PARAMETER server
    Target Usage Meter Server (used only when manually specifying a UM server that
    differs from the site UMs)

    .PARAMETER authToken
    Target Authorization Token for the specified UM server (only used when manually
    specifying a UM server that differs from the site UMs)

    .NOTES
    Author: Brian Marsh
    Version: 1.0
#>


[CmdletBinding()]
param( $dateFrom,  
       $dateTo,
       [Parameter(Mandatory=$true)]
       [ValidateSet("1","2","3","4","5","6","17")]
       $reportType = "2",
       [Parameter(Mandatory=$true)]
       [ValidateSet("ABC","DEF","GHI","JKL","MNO")] 
       $site,
       [switch] $raw,
       $server,
       $authToken

     )

BEGIN{

    # If we aren't provided a server and authToken via commandline input
    if (!($server -and $authToken)){
        # Set site specific variables for Usage Meter URL & User Token
        switch ($site){
            "ABC" {
                    $server    = "https://ABCUM:8443/um/"
                    $authToken = "TOKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXABC"
                  }
            "DEF" {
                    $server    = "https://DEFUM:8443/um/"
                    $authToken = "TOKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXDEF"
                  }
            "GHI" {
                    $server    = "https://GHIUM:8443/um/"
                    $authToken = "TOKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXGHI"
                  }
            "JKL" {
                    $server    = "https://JKLUM:8443/um"
                    $authToken = "TOKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXJKL"
                  }
            "MNO" {
                    $server    = "https://MNOUM:8443/um/"
                    $authToken = "TOKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXMNO"
                  }
        }
    }

# Enable self signed certs
add-type @"  
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
        public bool CheckValidationResult(
            ServicePoint srvPoint, X509Certificate certificate,
            WebRequest request, int certificateProblem) {
            return true;
        }
    }
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy

}
PROCESS{

    # Compose Request URL
    $requestURL = "$server/api/report/$reportType"

    # If a specified dateFrom/dateTo have been provided, add them to the Request URL
    if ($dateFrom -and $dateTo){
        $requestURL += "?dateFrom=$dateFrom&dateTo=$dateTo"
    }

    # Call the API
    $result   = invoke-webrequest -uri $requestURL -Headers @{"x-usagemeter-authorization"="$authToken"} | select content

    # Split the single string based on new lines
    $resultNL = $result.Content.split("`n")

    # If we're not doing raw output, attempt to clean up and put into an array of custom objects using ConvertFrom-Csv
    if (!($raw)){
        switch($reportType){
            1 {
                # Specify header fields for each column
                $csvHeader = "Time","Host DNS Name","Host RAM (GB)","vSphere License","VM RAM (MB)",
                             "Billing RAM (MB)","VM CPUs","VM Instance UUID"

                # Strip out the first 9 lines (use -raw for full details), treat the remaining as a tab delimited csv
                $csv = ConvertFrom-Csv $resultNL[9..($resultNL.Length -3)] -Delimiter "`t" -Header $csvHeader
                $out = $csv
            }
            2{
                # Specify header fields for each column
                $csvheader = "Time","Customer","Host DNS Name","Host RAM (GB)","vSphere License","VM VC Name",
                              "VM Hostname","VM RAM (MB)","Billing RAM (MB)","VM CPUs","VM Instance UUID"

                # Strip out the first 9 lines (use -raw for full details), treat the remaining as a tab delimited csv
                $csv = ConvertFrom-Csv $resultNL[9..($resultNL.Length -3)] -Delimiter "`t" -Header $csvHeader

                # Sometimes the csv object has weird entries. ProblemVMs is designed to handle that
                $problemVMs = @()

                $csv | %{
                    $thisVM = $_

                    # Try to calculate the RAM Billing percent based on Billing RAM and VM RAM
                    try{
                        $BillingPercent = [Math]::Round(($thisVM."Billing RAM (MB)"/$thisVM."VM RAM (MB)" * 100),2)
                    } catch {
                        # If there's a problem (e.g. divide by zero/invalid numbers), add this VM to the list
                        Write-Error "Problem calculating $thisVM billing percent: $($error[1].Exception)"
                        $problemVMs += $thisvm
                    }

                    # Add the new Billing Percent field to this object
                    $_ | Add-Member -MemberType NoteProperty -Name "Billing Percent" -Value $BillingPercent -Force
                }
                $out = $csv
            }
            3 {
                # Specify header fields for each column
                $csvheader = "Customer","Country","Postal","License Type","Category","Billable","Billed vRAM (GB-Hours)",
                             "Capped Billed vRAM (GB-Hours)"

                # Strip out the first 9 lines (use -raw for full details), treat the remaining as a tab delimited csv
                $csv = ConvertFrom-Csv $resultNL[9..($resultNL.Length -3)] -Delimiter "`t" -Header $csvHeader
                $out = $csv
            }
            4 {
                # Specify header fields for each column
                $csvHeader = "Billable","Category","License Type","Billed vRAM (GB-Hours)","Capped Billed vRAM (GB-Hours)"

                # Strip out the first 9 lines (use -raw for full details), treat the remaining as a tab delimited csv
                $csv = ConvertFrom-Csv $resultNL[9..($resultNL.Length -3)] -Delimiter "`t" -Header $csvHeader
                $out = $csv
            }
            5 {
                # Specify header fields for each column
                $csvHeader = "Product","Unit of Measure","Units to be Reported"

                # Strip out the first 9 lines (use -raw for full details), treat the remaining as a tab delimited csv
                $csv = ConvertFrom-Csv $resultNL[9..($resultNL.Length -3)] -Delimiter "`t" -Header $csvHeader
                $out = $csv
            }
            17 {
                # Specify header fields for each column
                $csvHeader = "vCloud Service Provider Bundle","Capped Billed vRAM (GB-Hours)","Hours","Average"

                # Strip out the first 9 lines (use -raw for full details), treat the remaining as a tab delimited csv
                $csv = ConvertFrom-Csv $resultNL[9..($resultNL.Length -3)] -Delimiter "`t" -Header $csvHeader
                $out = $csv
            }
        }
    } else {
        # Raw output was requested, give the people what they want
        $out = $resultNL
    }
}
END{  
    # Last minute debuging (useful for that problemVMs checking)
    Write-Debug "Anything Else?"
    write-output $out
}
comments powered by Disqus