29 November 2011

Speed Up First PowerCLI 5 cmdlet -- Precompile XMLSerializers

Updated for v5.1 - 02 Oct 2012 -- see below

On 30 Jun 2011 there was a post on the official PowerCLI Blog about How to speed-up the execution of the first PowerCLI cmdlet.  They talked about how the first PowerCLI cmdlet run in a PowerShell session is considerably slower than all other PowerCLI cmdlet calls in the same session, and how that was "due to the fact that the .NET framework compiles the underlying code on first use."

So, they listed out the commands for precompiling some assemblies, so that the first PowerCLI cmdlet call would not be extra slow due to just-in-time compilation.  The commands add the compiled code to the "native image cache" on the local machine, and so only need performed once for the computer on which they are run. Note:  that is once per computer per version, so when a new PowerCLI version comes out and you install the new version, you would compile the assembly for the new version.

I wanted to keep the speed alive when I upgraded to PowerCLI v5, but found no mention of doing this for the new release.  So, I looked into it, and the commands for precompiling the XmlSerializers used by PowerCLI are roughly the same as those found at the PowerCLI Blog post, just with updating the version-specific items:
PS vN:\> C:\Windows\Microsoft.NET\Framework\v2.0.50727\ngen.exe install "VimService50.XmlSerializers, Version=5.0.0.0, Culture=neutral, PublicKeyToken=10980b081e887e9f"

And, if running on a 64-bit OS, also run:
PS vN:\> C:\Windows\Microsoft.NET\Framework64\v2.0.50727\ngen.exe install "VimService50.XmlSerializers, Version=5.0.0.0, Culture=neutral, PublicKeyToken=10980b081e887e9f"

These can both be run from PowerShell or a cmd prompt -- the Native Image Generator (ngen.exe) is doing the work.  The VimService50.XmlSerializers info can be found in the assembly folder at C:\Windows\assembly\.  There, one can get its Version and Public Key Token info used in the commands.

* Update - 02 Oct 2012:
With the release of PowerCLI v5.1 Release 1, there is a new XmlSerializers version to pre-compile to keep the speed streak alive.  This new one is named VimService51.XmlSerializers, and the version is "5.1.0.0".  So, here is another example of the speed difference of before and after using ngen.exe to precompile the XmlSerializers, along with the updated commandlines to run:

Speed results before:
PS vN:\> Measure-Command {Connect-VIServer myVcenter.dom.com}
...
TotalSeconds      : 14.4464282
TotalMilliseconds : 14446.4282

PS vN:\> Measure-Command {Get-VMHost}
...
TotalSeconds      : 11.859625
TotalMilliseconds : 11859.625

PS vN:\> Measure-Command {Get-VMHost}
...
TotalSeconds      : 0.0677823
TotalMilliseconds : 67.7823
The pre-compile commands for PowerCLI v5.1:
PS vN:\> C:\Windows\Microsoft.NET\Framework\v2.0.50727\ngen.exe install "VimService51.XmlSerializers, Version=5.1.0.0, Culture=neutral, PublicKeyToken=10980b081e887e9f"
 
If running on a 64-bit OS, also run:
PS vN:\> C:\Windows\Microsoft.NET\Framework64\v2.0.50727\ngen.exe install "VimService51.XmlSerializers, Version=5.1.0.0, Culture=neutral, PublicKeyToken=10980b081e887e9f"
 

Speed results after precompiling:
PS vN:\> Measure-Command {Connect-VIServer myVcenter.dom.com}
...
TotalSeconds      : 2.6289308
TotalMilliseconds : 2628.9308

PS vN:\> Measure-Command {Get-VMHost}
...
TotalSeconds      : 0.4143238
TotalMilliseconds : 414.3238

PS vN:\> Measure-Command {Get-VMHost}
...
TotalSeconds      : 0.0890656
TotalMilliseconds : 89.0656
End of update - 02 Oct 2012

Further info abut using NGen can be found at the MSDN site http://msdn.microsoft.com/en-us/library/6t9t5wcf%28v=VS.100%29.aspx.  There you can read all about:
  • how the precompilation helps with faster application startup 
    • loads faster, requires smaller initial working set
  • displaying the Native Image Cache (seeing one/all compiled native images)
  • uninstalling native images that have been compiled/installed
  • a "Summary of Usage Considerations" -- pros and cons of using native images
The speed increases for the first PowerCLI cmdlet call in a PowerShell session after compiling were on par with those reported in the PowerCLI blog post for previous versions.  Definitely worth running, to help keep away the slow-sies (SAS)!

11 November 2011

Get HP Firmware Info for HP VMware Hosts Using PowerShell

In trying to keep our HP hosts current on firmware, we had the need to find what the hosts' current firmware levels were. So, we whipped up a quick spot of code to retrieve such info.  It relies on only Get-View to get the data, and we were sure to specify only the properties we needed, so you know it will be FAF:
## Script function: quickly get BIOS date, Smart Array FW version, and iLO FW version for HP hosts in a given location (folder, cluster, datacenter, etc.)
## Author: vNugglets.com -- Sep 2011

## folder in which hosts in which we are interested reside
#$strHostsFolderName = "myFolder"
#Get-View -ViewType HostSystem -Property Name, Runtime.HealthSystemRuntime.SystemHealthInfo.NumericSensorInfo -SearchRoot (Get-View -ViewType Folder -Property Name -Filter @{"Name" = "^$([RegEx]::escape($strHostsFolderName))$"}).MoRef | %{
## cluster in which hosts in which we are interested reside
$strHostsClusterName = "myCluster"
Get-View -ViewType HostSystem -Property Name, Runtime.HealthSystemRuntime.SystemHealthInfo.NumericSensorInfo -SearchRoot (Get-View -ViewType ClusterComputeResource -Property Name -Filter @{"Name" = "^$([RegEx]::escape($strHostsClusterName))$"}).MoRef | %{
    $arrNumericSensorInfo = @($_.Runtime.HealthSystemRuntime.SystemHealthInfo.NumericSensorInfo)
    # HostNumericSensorInfo for BIOS, iLO, array controller
    $nsiBIOS = $arrNumericSensorInfo | ? {$_.Name -like "*System BIOS*"}
    $nsiArrayCtrlr = $arrNumericSensorInfo | ? {$_.Name -like "HP Smart Array Controller*"}
    $nsiILO = $arrNumericSensorInfo | ? {$_.Name -like "Hewlett-Packard BMC Firmware*"}
    New-Object PSObject -Property @{
        VMHost = $_.Name
        "SystemBIOS" = $nsiBIOS.name
        "HPSmartArray" = $nsiArrayCtrlr.Name
        "iLOFirmware" = $nsiILO.Name
    } ## end new-object
} ## end Foreach-Object

Lines 5-6 can be used instead of 8-9, in the case that one wants to get info on hosts in a particular inventory folder, instead of in a particular cluster.  Or, one can remove the -SearchRoot portion altogether, to get firmware info for all hosts in the given vCenter.

Note:  This technique relies on the HP CIM provider being installed and running properly on the VMware hosts.

An example of running the script and selecting a couple of the properties (for the sake of display width here) would look something like:
PS C:\> \\path\to\getHPHostFirmwareScript.ps1 | ft -a VMHost,SystemBIOS,HPSmartArray

VMHost            SystemBIOS                                  HPSmartArray
------            ----------                                  ------------
host0.domain.com  HP System BIOS P61 2009-07-10 00:00:00.000  HP Smart Array Controller HPSA1 Firmware 6.86
host1.domain.com  HP System BIOS P61 2009-07-10 00:00:00.000  HP Smart Array Controller HPSA1 Firmware 6.86
host2.domain.com  HP System BIOS P61 2010-10-25 00:00:00.000  HP Smart Array Controller HPSA1 Firmware 7.22
...

And, as for the speed:  for 175 hosts (some of which are in WAN-connected datacenters), this took about 16 seconds.  Not bad, not bad!

09 November 2011

Evacuate Templates from a Host -- on the Quick!

We recently had the need to move templates off a VMHost in a cluster. As you may know, the Move-Template cmdlet in PowerCLI does not allow for moving a template from one host to another. The cmdlet only allows for the -Destination to be a Datacenter or a VM Folder (a.k.a. "blue" inventory folders in the vSphere client).

There are posts out there that discuss ways to move templates to different datastores, how to move them to different inventory folders in vCenter, how to move templates to a particular host, etc. (links to a few such items are provided at the end of this post), but none fit the bill for our need.

So, with speed in mind, I wrote the following script that is careful to stay fast and low maintenance -- move all templates off of the given host, placing them on the other hosts in the given cluster.

The script:
## Script function: migrate templates from one VMhost to the rest of the hosts in the cluster (at random, for good dispersion)
## Author: vNugglets.com -- Aug 2011

## full name of VMHost from which to move templates
$strSourceVMHost = "someVMHost.domain.com"
## name of resource pool in the cluster into which to migrate the template (default ResPool name is "Resources")
$strDestResPoolName = "Resources"
#### end config section

## .NET View object of VMHost on which templates reside
$viewSourceVMHost = Get-View -ViewType HostSystem -Property Parent -Filter @{"Name" = [RegEx]::escape($strSourceVMHost)}
## .NET View object of cluster in which the VMHost (and, so, templates) reside
$viewTemplsCluster = Get-View -Id $viewSourceVMHost.Parent -Property Name

## get the view object of the destination resource pool
$viewDestResPool = Get-View -ViewType ResourcePool -Property Name -SearchRoot $viewTemplsCluster.MoRef -Filter @{"Name" = [RegEx]::escape($strDestResPoolName)}

## array of .NET View objects of templates to move
$arrTemplViewsToMove = Get-View -ViewType VirtualMachine -Property Name -SearchRoot $viewSourceVMHost.MoRef -Filter @{"Config.Template" = "true"}
## array of .NET View objects of VMHosts to which to move templates
$arrDestHostViews = Get-View -ViewType HostSystem -Property Name -SearchRoot $viewTemplsCluster.MoRef | ?{$_.Name -ne $strSourceVMHost}

## move the templates to other hosts
$arrTemplViewsToMove | %{
    ## MigrateVM_Task() not supported on templates (does not work), so go this route:
    ## mark template as a VM, putting the template on a different host in the process (takes advantage of the Host param to change the Host on which the template resides in the process of marking it as a VM)
    #   http://pubs.vmware.com/vsphere-50/topic/com.vmware.wssdk.apiref.doc_50/vim.VirtualMachine.html?path=5_0_2_5_12_4#markAsVirtualMachine
    $_.MarkAsVirtualMachine($viewDestResPool.MoRef, ($arrDestHostViews | Get-Random).MoRef)

    ## mark VM as template again
    #   http://pubs.vmware.com/vsphere-50/topic/com.vmware.wssdk.apiref.doc_50/vim.VirtualMachine.html?path=5_0_2_5_12_3#markAsTemplate
    $_.MarkAsTemplate()
} ## end foreach-object

This takes advantage of the second parameter of the MarkAsVirtualMachine() method that is the VMHost on which to place the VM.  When it is time for the target host to be done with its maintenance, moving the templates back is almost easier. Use:
## move them back to their original host
$arrTemplViewsToMove | %{
    ## MigrateVM_Task() not supported on templates (does not work), so go this route:
    ## mark template as a VM, putting the template on a different host in the process (takes advantage of the Host param to change the Host on which the template resides in the process of marking it as a VM)
    $_.MarkAsVirtualMachine($viewDestResPool.MoRef, $viewSourceVMHost.MoRef)

    ## mark VM as template again
    $_.MarkAsTemplate()
} ## end foreach-object

and the templates that were evacuated from the host are migrated back to that host. As you can see, that is going to be FaF.

Some of the other posts/ways previously mentioned, for reference: