PowerCLI Report Tip – Part 2

Introduction

In this blog post, I will be deep-diving into the following to improve your report:

  • Select *
  • ExtensionData (also known as Get-View)

This is the second series continued from Part 1 which could be found here.

Select *

Select is used to filter an output. For example, Get-VM | Select Name, NumCPU, MemoryGB would give you 3 properties only. What would Select * give you? In regular expression world ,* normally means “everything” and the output is attached below:

PowerState : PoweredOn
Version : v9
Description :
Notes :
Guest : testVM
NumCpu : 4
MemoryMB : 2048
MemoryGB : 2
HardDisks : {Hard disk 1}
NetworkAdapters : {Network adapter 1}
UsbDevices : {}
CDDrives : {CD/DVD drive 1}
FloppyDrives : {Floppy drive 1}
Host : esxi1.test.com
HostId : HostSystem-host-19843
VMHostId : HostSystem-host-19843
VMHost : esxi1.test.com
VApp :
FolderId : Folder-group-v23748
Folder : TestFolder
ResourcePoolId : ResourcePool-resgroup-22471
ResourcePool : Resources
PersistentId : 501c2be6-da23-928f-7d58-e278c8a2cf62
UsedSpaceGB : 102.13691748306155204772949219
ProvisionedSpaceGB : 102.13691792357712984085083008
DatastoreIdList : {Datastore-datastore-24700}
HARestartPriority : ClusterRestartPriority
HAIsolationResponse : AsSpecifiedByCluster
DrsAutomationLevel : AsSpecifiedByCluster
VMSwapfilePolicy : Inherit
VMResourceConfiguration : CpuShares:Normal/4000 MemShares:Normal/20480
GuestId : debian6Guest
Name : testVM
CustomFields :
ExtensionData : VMware.Vim.VirtualMachine
Id : VirtualMachine-vm-25976
Uid : /VIServer=administrator@vcenter.test.com:443/VirtualMachine=VirtualMachine-vm-25976/
Client : VMware.VimAutomation.ViCore.Impl.V1.VimClient

The reason I wanted to go through Select * is to show you guys that Get-VM function itself has a lot of properties already. Let’s take a look at a report below:

ESXi,ResourcePool,VM,CPU,Memory
ESXi_test1,RP1,test1,1,1
ESXi_test1,RP2,test2,1,1
ESXi_test2,RP2,test3,1,4

Without knowing the properties well, to generate the report above, some might come up with a script like the following:

foreach ($vm in Get-VM) {
 $resourcepool = Get-ResourcePool -VM $vm
 $esxi = Get-VMHost -VM $vm
 $vm | Select @{N=“ESXi”;E={$esxi.Name}}, @{N=“ResourcePool”;E={$resourcepool.Name}}, Name, NumCPU, MemoryGB
}

The above script looks good, it has no problem. But looking at properties, some of them could actually be retrieved from Get-VM, i.e. without running the commands Get-ResourcePool and Get-VMHost. Let’s do some performance testing. For the testing, I selected a cluster with 300 virtual machines and with the script above, it has taken 62 minutes. Suppose there are 10000 virtual machines, I do not want to imagine that. Using the properties already queried (script attached below), it improved the script significantly. It took 28 seconds. Seconds, not minutes!

Get-VM | Select @{N=“ESXi”;E={$_.VMHost.Name}}, @{N=“ResourcePool”;E={$_.ResourcePool}}, Name, NumCPU, MemoryGB

The point here I made is that since Get-VM itself already runs Get-ResourcePool, Get-VMHost, do not run them again! Otherwise, it will significantly reduce the performance of your script. It’s not only for Get-VM, it could be Get-Cluster, Get-VMHost and so on. I strongly recommend you to get familiar with properties.

ExtensionData

ExtensionData, also known as Get-View is another property that I would want to talk about. I will take Get-VMHost as an example this time and the following two commands produce the same output:

  • Get-VMHost -Name esxi1.test.com | Get-View
  • (Get-VMHost -Name esxi1.test.com).ExtensionData
Runtime : VMware.Vim.HostRuntimeInfo
Summary : VMware.Vim.HostListSummary
Hardware : VMware.Vim.HostHardwareInfo
Capability : VMware.Vim.HostCapability
LicensableResource : VMware.Vim.HostLicensableResourceInfo
ConfigManager : VMware.Vim.HostConfigManager
Config : VMware.Vim.HostConfigInfo
Vm : {VirtualMachine-vm-7950, VirtualMachine-vm-7941, VirtualMachine-vm-4299, VirtualMachine-vm-4583...}
Datastore : {Datastore-datastore-3500, Datastore-datastore-3501, Datastore-datastore-3502, Datastore-datastore-3503...}
Network : {DistributedVirtualPortgroup-dvportgroup-7890, DistributedVirtualPortgroup-dvportgroup-4523, DistributedVirtualPortgroup-dvportgroup-4580, DistributedVirtualPortgroup-dvportgroup-3272...}
DatastoreBrowser : HostDatastoreBrowser-datastoreBrowser-host-7674
SystemResources : VMware.Vim.HostSystemResourceInfo
LinkedView :
Parent : ClusterComputeResource-domain-c3265
CustomValue : {}
OverallStatus : green
ConfigStatus : green
ConfigIssue : {}
EffectiveRole : {285213786}
Permission : {}
Name : esxi1.test.com
DisabledMethod : {ExitMaintenanceMode_Task, PowerUpHostFromStandBy_Task, ReconnectHost_Task}
RecentTask : {}
DeclaredAlarmState : {alarm-1.host-7674, alarm-12.host-7674, alarm-13.host-7674, alarm-14.host-7674...}
TriggeredAlarmState : {}
AlarmActionsEnabled : True
Tag : {}
Value : {}
AvailableField : {}
MoRef : HostSystem-host-7674
Client : VMware.Vim.VimClientImpl

It looks quite complex but actually it’s not. Let us take a look at one example:

  • (Get-VMHost -Name esxi1.test.com).ExtensionData.Hardware
SystemInfo : VMware.Vim.HostSystemInfo
CpuPowerManagementInfo : VMware.Vim.HostCpuPowerManagementInfo
CpuInfo : VMware.Vim.HostCpuInfo
CpuPkg : {0 1 2 3 4 5 6 7 8 9 10 11, 12 13 14 15 16 17 18 19 20 21 22 23}
MemorySize : 206144823296
NumaInfo : VMware.Vim.HostNumaInfo
SmcPresent : False
PciDevice : {00:00.0, 00:01.0, 00:03.0, 00:04.0...}
CpuFeature : {VMware.Vim.HostCpuIdInfo, VMware.Vim.HostCpuIdInfo, VMware.Vim.HostCpuIdInfo, VMware.Vim.HostCpuIdInfo...}
BiosInfo : VMware.Vim.HostBIOSInfo
ReliableMemoryInfo :
DynamicType :
DynamicProperty :

The output above is representing hardware details of this ESXi server. Isn’t it surprising? Single PowerCLI command could generate a report with a lot of information if you know the properties well enough.

This is how you generate advanced reports and it’s quite simple to achieve that – get familiar with properties especially ExtensionData. I will leave the rest to you guys to play with, such as BiosInfo, SystemInfo and so on.

Wrap-Up

Throughout the blog, it discussed:

  • Taking a closer look at Select * to avoid running repeated commands
  • Generating advanced reports with ExtensionData

Hope this helps and on the next series, I will be deep-diving into esxcli

PowerCLI Report Tip – Part 1

Introduction

For the last years, I’ve been writing a lot of PowerCLI scripts to automate repeated tasks and produce custom reports for managers to review the infrastructure. For the next 4 series of blog, I will be sharing a number tips on how to write an efficient and tidy scripts.
This will be the first series and it will discuss the ways to improve the performance.
Note: PowerCLI 5.8 Release 1 & vCenter Server 5.5 were used.

Getting Started

Let’s get started with a simple script:

$report = foreach ($vm in (Get-VM -Location (Get-Cluster -Name cluster1))) {
   $vm | Select @{N="ESXi";E={$vm.VMHost.Name}}, Name, NumCPU, MemoryGB, @{N="Datastore";E={ [string]::Join(",", (Get-Datastore -VM $vm | %{$_.Name})) }}
}

An example output is attached below:

ESXi,VM,CPU,Memory,Datastore
ESXi_test1,test1,1,1,datastore1,datastore2,datastore3
ESXi_test1,test2,1,1,datastore1
ESXi_test2,test3,1,4,datastore4,datastore5

For the testing, I selected a cluster with 300 virtual machines to measure how long it takes to run the script above and it took 5 minutes. 5 minutes does look OK, however, how about 2000~3000 virtual machines? It will take about an hour which is very inefficient.

How could we improve the performance?

Trick

Many people might think that saving a full list of output would take longer than applying a filter. For example, Get-Datastore vs Get-Datastore -VM $vm.

Is this the case? Let’s have a look:

  • Get-Datastore => To retrieve 565 datastores, it took 2.2 seconds
  • Get-Datastore -VM “VM” => It took 8 seconds

Surprising result, right? Get-Datastore without a filter is approximately 4 times faster, even it queried for 565 datastores. With this result, it was safe to assume that for the script above, it does Get-Datastore for 300 times which is roughly 300 * 8 = 2400 seconds, about 4 minutes. Sounds about right.

To improve the performance, one of the ways I could came up was to:

  • Save Get-Datastore output in a variable, e.g. $datastore_list = Get-Datastore
  • Utilise the $datastore_list to find which datastores are allocated to virtual machines

This way, instead of executing Get-Datastore 300 times, it will run Get-Datastore once, save the result in a variable and query datastore information from the variable. This does look much more efficient. However, how could we achieve this?

If you look at the properties of Get-VM closely (run Get-VM | Select * to view all properties, this will be discussed in depth on the next series), there is a property called “DatastoreIdList”. Each datastore has a unique datastore id and Get-VM has this datastore id value. This means, we could:

  • Run a foreach loop against DatastoreIdList
  • If datastore ID matches to any datastore id in $datastore_list variable, output

Translating the above into a PowerCLI command:

$datastore_list = Get-Datastore
(Get-VM -Name “VM”).DatastoreIdList | Foreach-Object { $datastore_id = $_; $datastore_list | where {$_.id -match $datastore_id} }

Converting the script in Getting Started section:

$datastore_list = Get-Datastore
$report = foreach ($vm in (Get-VM -Location (Get-Cluster -Name cluster1))) {
   $vm | Select @{N="ESXi";E={$_.VMHost.Name}}, Name, NumCPU, MemoryGB, @{N="Datastore";E={ [string]::Join(",", ( $_.DatastoreIdList | Foreach-Object { $datastore_id = $_; $datastore_list | where {$_.id -match $datastore_id} } )) }}
}

The above script took 27 seconds, producing the same output. This is approximately 20 times faster than the original one.

Wrap-Up

Throughout the blog, it discussed a few ways on how to improve the performance of PowerCLI scripts:

  • Instead of applying a filter, save the whole output
  • Avoid executing a same command over and over
  • Take a closer look at properties to avoid running a command

Hope this helped and on the next series, I will deep dive into properties.

ESXi Upgrade – Virtual Machines Drop From Network

Introduction

Was in the process of upgrading ESXi servers, 5.0 to 5.5 and noticed a number of virtual machines were disconnected from network randomly.

Throughout this blog post, I will be going through:

  • Symptom
  • Workaround

 

Symptom

The cluster had 8 nodes (n+1) and it was scheduled to upgrade one ESXi server per day. On the next day, after one ESXi server was upgraded to 5.5, a few incidents were raised saying some virtual machines aren’t accessible over network.

To summarise:

  • One ESXi server was upgraded to 5.5
  • Some virtual machines disconnected from network across multiple ESXi servers
  • ESXi servers were manageable via vCenter Server
  • Virtual machines were also manageable, e.g. reconfigure virtual machine

The first method I’ve chosen for this issue was to check the ESXi logs and found interesting lines in VMKernel.log:

2014-11-07T22:07:47.202Z cpu16:1804972)Net: 1652: connected test_vm1.eth1 eth1 to vDS, portID 0x200002e
2014-11-07T22:07:47.202Z cpu16:1804972)Net: 1985: associated dvPort 513 with portID 0x200002e
2014-11-07T22:07:47.202Z cpu16:1804972)etherswitch: L2Sec_EnforcePortCompliance:247: client test_vm1.eth1 requested mac address change to 00:00:00:00:00:00 on port 0x200002e, disallowed by vswitch policy2014-11-07T22:07:47.202Z cpu16:1804972)etherswitch: L2Sec_EnforcePortCompliance:356: client test_vm1.eth1 has policy vialations on port 0x200002e. Port is blocked
2014-11-07T22:07:47.202Z cpu16:1804972)etherswitch: L2Sec_EnforcePortCompliance:247: client test_vm1.eth1 requested mac address change to 00:00:00:00:00:00 on port 0x200002e, disallowed by vswitch policy
2014-11-07T22:07:47.202Z cpu16:1804972)etherswitch: L2Sec_EnforcePortCompliance:356: client test_vm1.eth1 has policy vialations on port 0x200002e. Port is blocked
2014-11-07T22:07:47.202Z cpu16:1804972)WARNING: NetPort: 1245: failed to enable port 0x200002e: Bad parameter
2014-11-07T22:07:47.202Z cpu16:1804972)NetPort: 1427: disabled port 0x200002e
2014-11-07T22:07:47.202Z cpu16:1804972)WARNING: Net: vm 1804972: 377: cannot enable port 0x200002e: Bad parameter
2014-11-07T23:50:39.821Z cpu4:4151)Net: 2191: dissociate dvPort 513 from port 0x200002e
2014-11-07T23:50:39.821Z cpu4:4151)Net: 2195: disconnected client from port 0x200002e

While Googling, could find a KB article related to this issue which can be found hereThe problem was quite simple, ESXi 4.x or 5.0 supports only DVUplinks less than 31 characters and guess what, DVUplinks were longer than 31 characters!

Investigating the history of vMotions, the root cause of this problem was that DRS was load balancing workloads and during this process, some of virtual machines were spread out from ESXi 5.5 to ESXi 5.0 servers.

Workaround

The resolutions the above KB article suggests are to patch ESXi 5.0 to Patch 5. However, the patch needed reboot which means, it would take approximately same time as upgrading ESXi to 5.5. The better option was to upgrade ESXi 5.0 to 5.5 and not allow virtual machines running in ESXi 5.5 to move to ESXi 5.0 servers, i.e. set DRS to manual. The only problem with this was manual vMotion was required to evacuate virtual machines to place the ESXi server on maintenance.

Alternatively, it was possible to rename the DVUplinks to be less than 31 characters to benefit from fully automated DRS. But our standard was to leave the name of DVUplinks as default.

After a discussion, we came up with a workaround:

  1. Set DRS to manual
  2. Patching a ESXi server, set DRS to fully automated
  3. Place the ESXi server on maintenance mode
  4. Set DRS back to manual once maintenance mode task kicks in evacuation of virtual machines
  5. Upgrade ESXi server
  6. Repeat 2~5

In this way, I could evacuate virtual machines to other ESXi servers automatically and not worrying about virtual machines being vMotioned from 5.5 to 5.0.

Hope this helped and feel free to leave a comment.