vNugglets

Looking for virtual nugglets? Then you've come to the right place.

06 February 2012

Change VM Boot Order via PowerShell

There was a recent question in the VMware PowerCLI community about changing the boot order for a VM (Change Boot Sequence with new Extensiondata option).  In looking into this, I found that the VirtualMachineBootOptions item has a BootOrder property with which one can change the VM's boot order (new in vSphere API 5.0).

Upon searching about to see if there were already examples of using this property for changing boot order, I found none.  There are places that talk about changing/limiting what boot devices a VM may use in order to boot from the given device, but none that dealt with changing the actual boot order on the VM.  So, I whipped out an example (that person was wanting to use the ExtensionData property of a VM object).

The BootOrder property can have four (4) different types of devices (which are VirtualMachineBootOptionsBootableDevice objects): CDRom, disk, ethernet, and floppy.

The CDRom boot device does not correspond to any particular CDRom on the VM -- per the docs, when the boot order specifies CDRom, the first CDRom with bootable media found is used.

The disk and ethernet boot devices are related to the actual VirtualDisk and VirtualEthernetCard devices on the VM by specifying the corresponding Keys for the given devices.

Here is an example of setting the boot order for a VM to NIC2, Disk1, CD:
## the VM to configure
$strVMName = "myVM0"
## the device name of the NIC to which to boot
$strBootNICDeviceName = "Network adapter 2"
## the device name of the hard disk to which to boot
$strBootHDiskDeviceName = "Hard disk 1"
## get the .NET View object for the VM, with a couple of select properties
$viewVM = Get-View -ViewType VirtualMachine -Property Name, Config.Hardware.Device -Filter @{"Name" = "^$strVMName$"}

## get the VirtualEthernetCard device, and then grab its Key (DeviceKey, used later)
$intNICDeviceKey = ($viewVM.Config.Hardware.Device | ?{$_.DeviceInfo.Label -eq $strBootNICDeviceName}).Key
## bootable NIC BootOption device, for use in setting BootOrder (the corresponding VirtualEthernetCard device on the VM has PXE enabled, assumed)
$oBootableNIC = New-Object -TypeName VMware.Vim.VirtualMachineBootOptionsBootableEthernetDevice -Property @{"DeviceKey" = $intNICDeviceKey}

## get the VirtualDisk device, then grab its Key (DeviceKey, used later)
$intHDiskDeviceKey = ($viewVM.Config.Hardware.Device | ?{$_.DeviceInfo.Label -eq $strBootHDiskDeviceName}).Key
## bootable Disk BootOption device, for use in setting BootOrder (the corresponding VirtualDisk device is bootable, assumed)
$oBootableHDisk = New-Object -TypeName VMware.Vim.VirtualMachineBootOptionsBootableDiskDevice -Property @{"DeviceKey" = $intHDiskDeviceKey}

## bootable CDROM device (per the docs, the first CDROM with bootable media found is used)
$oBootableCDRom = New-Object -Type VMware.Vim.VirtualMachineBootOptionsBootableCdromDevice

## create the VirtualMachineConfigSpec with which to change the VM's boot order
$spec = New-Object VMware.Vim.VirtualMachineConfigSpec -Property @{
    "BootOptions" = New-Object VMware.Vim.VirtualMachineBootOptions -Property @{
        ## set the boot order in the spec as desired
        BootOrder = $oBootableNIC, $oBootableHDisk, $oBootableCDRom
    } ## end new-object
} ## end new-object

## reconfig the VM to use the spec with the new BootOrder
$viewVM.ReconfigVM_Task($spec)

To display the VM's BIOS boot order after the update (not terribly obvious from the output):
PS C:\> ## get the updated View info about the given property
PS C:\> $viewVM.UpdateViewData("Config.BootOptions.BootOrder")
PS C:\> ## display the BootOrder
PS C:\> $viewVM.Config.BootOptions

BootDelay        :
EnterBIOSSetup   :
BootRetryEnabled :
BootRetryDelay   :
BootOrder        : {4001, 2000, VMware.Vim.VirtualMachineBootOptionsBootableCdromDevice}
DynamicType      :
DynamicProperty  :

...where 4001 is the DeviceKey of the second virtual NIC in the VM, and 2000 is the DeviceKey for the first virtual disk.

Original BIOS Boot settings:
spacer

BIOS Boot settings after updating them with the script:
spacer

Notes about setting the boot order in this manner:  once you have done so on a VM, it appears that the boot order can only be changed via script.  Notice in the first screenshot that one may use keys to navigate between items, whereas the second screenshot states that "All items on this menu cannot be modified in user mode". Something to keep in mind.  Additionally, this method does not seem to have control over where the "Removable Devices" item appears in the boot order.

Enjoy!

16 January 2012

Show Currently Connected vCenter Servers in PowerCLI 5.0.1

PowerCLI 5.0.1 was recently released and vNuggs decided to check it out. Almost immediately we noticed something was missing. PowerCLI was no longer reporting in the title bar the vCenter server(s) to which we were connected.

Example of a single connected server before PowerCLI 5.0.1:

spacer

Example of multiple connected servers before PowerCLI 5.0.1:

spacer

AC decided to make a quick post to the PowerCLI community to see if others had noticed it and was hoping the PowerCLI team would respond as to whether this was intentional or not and what the future plans are for this functionality.  So far, no word from the team, but others have noticed the change as well.

However, in the meantime, we decided to solve the problem ourselves with some functions.  The first function is the one that does all the real work--ChangeTitleBar:

## Function to change title bar to reflect currently connected vCenters
## Author: vNugglets.com -- Jan 2012

function Change-TitleBar() {
    ## check to see if there are any currently connected servers
    if ($global:DefaultVIServers.Count -gt 0) {
        ## since there is at least one connected server, modify the window title variable accordingly
        $strWindowTitle = "[PowerCLI] Connected to {0} server{1}:  {2}" -f $global:DefaultVIServers.Count, $(if ($global:DefaultVIServers.Count -gt 1) {"s"}), (($global:DefaultVIServers | %{$_.Name}) -Join ", ")
    }
    else {
        ## since there are no connected servers, modify the window title variable to show "not connected"
        $strWindowTitle = "[PowerCLI] Not Connected"
    }
    ## perform the window title change
    $host.ui.RawUI.WindowTitle = $strWindowTitle
}

In line 6 the function merely checks the Count property of the array DefaultVIServers to ensure that you are connected to at least one server, then updates a variable with the new window title text in line 8. Note that we are omitting the name of the connected user.  We went this route to keep the function simple and because even VMware didn't include this info when connected to multiple servers (see screen shots above) and in the end, it is not something we find all that important/useful. It could simply be added by using the User property of the DefaultVIServers variable.

In line 12 (the "else" portion of the "if" statement) we just set the variable to what we selected as our default window title text.  Change this to your own preference (note that it doesn't bother reporting the PowerCLI version either, but that's easy enough with the Get-PowerCLIVersion cmdlet).

Finally, line 15 actually sets the window title to the defined value.  Now you just need to place this function in your PowerShell profile and make calls to it when you connect to/disconnect from VI servers.  You can do this by writing custom functions for Connect-VIServer and Disconnect-VIServer as follows:

## Modified "Connect- and Disconnect-VIServer" functions that call "Change-TitleBar" function
## Author: vNugglets.com -- Jan 2012

## Function for the "Connect-VIServer" cmdlet
function ConnectServer([string] $strVCenterName) {
    Connect-VIServer -Server $strVCenterName
    Change-TitleBar
}

## Function for the "Disconnect-VIServer" cmdlet
function DisconnectServer([string] $strVCenterName = "*") {
    Disconnect-VIServer -Server $strVCenterName -Confirm:$false
    Change-TitleBar
}

There is absolutely nothing fancy about these functions and they are not intended as direct feature-for-feature replacements of the actual Connect- and Disconnect-VIServer cmdlets.  The ConnVIServer function simply takes a string parameter and calls the actual Connect-VIServer cmdlet with that string specified as the server to connect to.  It then calls the Change-TitleBar function to do the title bar magic.  The DisconnVIServer function does the same, except in this case we've set a default parameter value of "*" just for convenience when you want to disconnect from all connected servers.  You'd merely type DisconnVIServer and it would immediately disconnect from all servers.

Lastly, it is probably a good idea to make a call to Change-TitleBar somewhere after the function definitions in your PowerShell profile.  Then it'll set the "default" title bar text as you launch PowerShell since the initial text with PowerCLI 5.0.1's initialization script just shows the name of the product and version.

29 November 2011

Speed Up First PowerCLI 5 cmdlet -- Precompile XMLSerializers

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 a while back, 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 above, just with updating the version-specific items:
PS C:\> 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 C:\> 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.

Further info abut using NGen can be found at the MSDN site 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)
    #   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
    #   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:
  • ict-freak.nl/2010/01/21/powercli-move-template/ by Arne Fokkema
    (moves templates to different datastore)
  • halr9000.com/article/591 by HalR
    (moves templates into different inventory folders)
  • get-admin.com/blog/how-to/vmware/provisioning-server-for-vms/ by Glenn Sizemore
    (moves templates all onto one host and into one inventory folder)
gipoco.com is neither affiliated with the authors of this page nor responsible for its contents. This is a safe-cache copy of the original web site.