In this chapter, you will learn to:
At this point, we have shown that vSphere is the virtualization platform of choice for a myriad of reasons. This chapter focuses on automating some of the most advanced features vSphere offers. Be aware that many of these features require the Enterprise Plus licensing to be used.
Have you ever tried to use older hardware in the same cluster as newly purchased servers? Did you encounter a condition where virtual machines would not vMotion between the hardware generations? This is because CPU features between the newer and older generations are incompatible. VMware uses Enhanced vMotion Compatibility (EVC) to mask the features of newer-generation CPUs so that they appear to have the same capabilities as the older generation. This facilitates vMotion across CPUs of the same manufacturer, even when they are vastly different in capability. One thing to note, though, is that EVC doesn’t allow you to vMotion VMs from AMD to Intel, or vice versa.
In recent versions of PowerCLI, VMware has added the ability to configure EVC for a cluster using the standard Set-Cluster
cmdlet, as shown in Listing 5-1.
Listing 5-1: Setting the EVC mode using PowerCLI
Get-Cluster $clustername | Set-Cluster -EVCMode intel-nehalem
That code snippet sets the EVC mode intel-nehalem
; it masks CPU features for all Intel processors so that they are at the feature level of the Nehalem platform. For ESXi 5.5 the supported EVC modes are as follows:
intel-merom
intel-penryn
intel-nehalem
intel-westmere
intel-sandybridge
intel-ivybridge
amd-rev-e
amd-rev-f
amd-greyhound-no3dnow
amd-greyhound
amd-bulldozer
amd-piledriver
ESXi 6 adds support for these CPU architectures as well:
intel-haswell
amd-steamroller
For each of the manufacturers, the list builds on the feature set of the less capable architecture. For example, intel-westmere
has all of the features of intel-nehalem
, plus the features specific to Westmere. This means that your cluster can only support the EVC mode of the least capable CPU architecture. Unfortunately, there is no quick way to determine what that mode should be. You have to make do with trial and error using the GUI. Fortunately, PowerCLI has access to all of this information, so you can create a simple function to determine the maximum EVC setting for any cluster. Take a look at Listing 5-2.
Listing 5-2: The Get-MaxEvcMode
functionfunction Get-MaxEvcMode {
<# .SYNOPSIS
Get the highest EVC level for a cluster
.DESCRIPTION
Function to get the highest EVC level supported by all of
the VMHosts in an cluster. Useful to easily/quickly
determine the highest EVC level that one might set for
the cluster based on the VMHosts that are a part of the
cluster and their hardware
types.
.EXAMPLE
Get-MaxEvcMode -Cluster (Get-Cluster clusterName)
Get the EVC mode info for cluster "clusterName"
.EXAMPLE
Use the pipeline to get the EVC mode info for the given clusters.
Get-Cluster someCluster,someOtherCluster | Get-MaxEvcMode
.PARAMETER Cluster
The cluster whose maximum EVC mode to determine.
.INPUTS
[VMware.VimAutomation.ViCore.Impl.V1.Inventory.ClusterImpl]
.OUTPUTS
PSCustomObject
#>
[CmdletBinding()]
param(
[parameter(
Mandatory=$true,
ValueFromPipeline=$true
)]
[VMware.VimAutomation.ViCore.Impl.V1.Inventory.ClusterImpl]
$Cluster
)
process {
$hosts = $Cluster | Get-VMHost
# get the max supported for each host
$nodeMaxEvcMode = @{}
$hosts | Foreach-Object {
$nodeMaxEvcMode.Add(
$_.Name,
$_.ExtensionData.Summary.MaxEVCModeKey
)
}
# check to see if there are different CPU manufacturers
$lastManufacturer = ""
$incompatible = $false
$nodeMaxEvcMode.GetEnumerator() | Foreach-Object {
$currentManufacturer = ($_.Value -split "-")[0]
if ($lastManufacturer -eq "") {
$lastManufacturer = $currentManufacturer
}
if ($lastManufacturer -ne $currentManufacturer) {
Write-Warning "Hosts have Intel and AMD CPUs"
$incompatible = $true
}
}
# check to see if they are all the same
[Array]$evcGroups = $nodeMaxEvcMode.GetEnumerator() |
Group-Object -Property Value
if ($evcGroups.Count -eq 1) {
# they're the same, print out the first one
$obj = "" | Select-Object MaxEvcMode
$obj.MaxEvcMode = $evcGroups[0].Name
$obj
} elseif ($incompatible -eq $false) {
# they're not the same, let's figure out the least
# common denominator
# collect all available EVC modes
$evcModes = @{
'intel' = @();
'amd' = @();
}
$serviceInstance = Get-View ServiceInstance
$serviceInstance.Capability.SupportedEVCMode | Foreach-Object {
$procManufacturer = ($_.key -split "-")[0]
$procArchiteture = $_.key
$evcModes.$procManufacturer += $procArchiteture
}
# Will map to the array index with the maximum supported
# value. The initial value is set high enough that it will
# be overwritten by the actual host values during the check
$maxSupported = `
$serviceInstance.Capability.SupportedEVCMode.length
# check to see what is the lowest supported value
$nodeMaxEvcMode.GetEnumerator() | Foreach-Object {
$cpuManufacturer = ($_.Value -split "-")[0]
# get the index value of this element
$nodeEvcIndex = [Array]::IndexOf(
$evcModes.$cpuManufacturer,
$_.Value
)
# check to see if this EVC value is less than
# the currently reported minimum
if ($nodeEvcIndex -lt $maxSupported) {
$maxSupported = $nodeEvcIndex
}
}
# return the value as an anon object
$obj = "" | Select-Object MaxEvcMode
$obj.MaxEvcMode = `
$evcModes.$cpuManufacturer[$maxSupported]
$obj
}
}
}
You can use the function created in Listing 5-2 in conjunction with the standard Set-Cluster
cmdlet to quickly and easily set the EVC mode for any set of servers. Listing 5-3 shows how that’s done.
Listing 5-3: Setting the EVC mode for a cluster
Get-Cluster $clustername | Set-Cluster `
-EVCMode (Get-Cluster $clustername | `
Get-MaxEvcMode).MaxEvcMode
Likewise, you can disable EVC for a cluster by setting the EVC mode to $null
, as we have done in Listing 5-4.
Listing 5-4: Disabling EVC for a cluster
Get-Cluster $clustername | Set-Cluster -EVCMode $null
EVC is a handy feature to enable vMotion within a cluster of multigenerational servers, but it does have some nuances. For example, you can increase the level of EVC without powering off all VMs in the cluster, but you cannot decrease the level without powering down the VMs first. Similarly, if you vMotion from a cluster with a higher EVC setting to a lower setting, you will need to power down the VM first.
With this workflow in mind, you can seamlessly replace an entire cluster’s worth of servers without having to vMotion across clusters:
vSphere Flash Read Cache (vFRC) is a feature that was introduced by VMware with vSphere 5.5. This feature uses SSDs local to the physical host to provide an accelerated read-only cache for individual VMDKs of virtual machines hosted by that server. This has a tremendously positive effect on the read performance of the virtual machine by using local, low-latency, high-performance drives that don’t have to traverse the storage network. It also works with VMFS, NFS, and even raw device mapping (RDM) disk devices, providing up to 200 GB (by default, but this can be expanded to 400 GB) of cache per VMDK.
If you are familiar with the feature introduced in vSphere 5.1 that enables the host to swap to SSD, this is a replacement for and an extension of that feature. Both the ESXi host and the VMs can be configured to swap to the SSD instead of to a datastore. vFRC is different than this swap-to-SSD functionality in that it acts as a read cache specifically for the VMDKs that have been assigned. Be aware that when vMotioning a VM with vFRC enabled, you will need to specify what action to take with the cache: copy it to the new host or discard the cache.
Administering vFRC is relatively easy; however, it requires the web client and must be done on a per-VMDK basis. Doing so is quite tedious when you have a large number of hosts to enable and VMDKs to assign capacity for. To expedite the configuration of vFRC on the host, we have created a simple PowerShell function (Listing 5-5) that uses all of the available SSDs to create a single VFFS filesystem on the host.
Listing 5-5: The Enable-VMHostVFRC
function
function Enable-VMHostVFRC {
<# .SYNOPSIS
Enable vFlash Read Cache for a host.
.DESCRIPTION
Function to enable vFRC on a host leveraging the available
SSDs to create the VFFS file system. Will use all
available SSDs as determined by the system.
.EXAMPLE
Enable vFRC for a particular host
Enable-VMHostVFRC -Host (Get-VM someVM)
.EXAMPLE
Use the pipeline to enable vFRC for a cluster
Get-Cluster | Get-VMHost | Enable-VMHostVFRC
.PARAMETER VMHost
The host to enable vFRC on.
.INPUTS
[VMware.VimAutomation.ViCore.Impl.V1.Inventory.VMHostImpl]
.OUTPUTS
PSCustomObject
#>
[CmdletBinding()]
param(
[parameter(Mandatory=$true,ValueFromPipeline=$true)]
[VMware.VimAutomation.ViCore.Impl.V1.Inventory.VMHostImpl]
$VMHost
)
process {
# get views for the objects we will interact with
$hostView = $VMHost | Get-View -Property `
ConfigManager.StorageSystem, ConfigManager.VFlashManager
$hostStorage = Get-View -Property SystemFile `
$hostView.ConfigManager.StorageSystem
$hostVflash = Get-View `
$hostView.ConfigManager.VFlashManager
# get the device path for each of the available SSDs
$availSsds = $hostStorage.QueryAvailableSsds(
[NullString]::Value
) | Foreach-Object {
$_.devicePath
}
# report if no SSDs were found
if ($availSsds -eq $null) {
Write-Verbose "No available SSDs on host $($VMHost).name"
}
try {
# reconfigure to create the vFRC VFFS
$task = $hostVflash.ConfigureVFlashResourceEx(
$availSsds
)
# update the view to check the status
$hostVflash.UpdateViewData(
"VFlashConfigInfo.VFlashResourceConfigInfo"
)
# report the status
$hostVflash.VFlashConfigInfo.VFlashResourceConfigInfo.Vffs
}
catch {
Write-Error `
"Unable to assign available SSDs to new VFFS"
}
}
}
Once the vFRC VFFS filesystem has been configured, you can begin assigning VMDKs to use it. This can be done through the web client, but life is so much easier when you use PowerShell!
First, let’s determine the current settings for a VMDK. To do this, we have created a simple function (Listing 5-6). This can be used to quickly determine which VMDKs are using vFRC, what their allocation is, and what the cache settings are.
Listing 5-6: The Get-HarddiskVFRC
function
function Get-HarddiskVFRC {
<# .SYNOPSIS
Get the status of vFRC for a virutal machine hard disk.
.DESCRIPTION
Determines if vFRC is enabled for a VM hard disk. If
enabled, will determine the settings and return them.
.EXAMPLE
Get the vFRC status for a hard disk
Get-HarddiskVFRC -Disk (Get-VM someVM | Get-VMHarddisk)
.EXAMPLE
Use the pipeline to get the vFRC status for all hard disks
in a vApp
Get-vApp "My Application" | Get-VM | Get-VMHarddisk |
Get-HarddiskVFRC
.PARAMETER Disk
The disk to check vFRC status on.
.INPUTS
[VMware.VimAutomation.ViCore.Impl.V1.VirtualDevice.HardDisk]
.OUTPUTS
PSCustomObject
#>
[CmdletBinding()]
param(
[parameter(
Mandatory=$true,
ValueFromPipeline=$true
)]
[VMware.VimAutomation.VICore.Types.V1.VirtualDevice.HardDisk]
$Disk
)
process {
# get the owning VM based on the disk's parent id
$parentVm = Get-VM -Id $disk.Parentid
# create an anon object to store vFRC relevant data
$info = "" | Select-Object VM,DiskName,ReservationMB,CacheType,`
CacheMode,BlockSizeKB,VFlashModule
$info.VM = $parentVm.Name
$info.DiskName = $Disk.Name
# read the disk object's info and put it into our custom object
if ($disk.ExtensionData.VFlashCacheConfigInfo `
-ne $null) {
$info.ReservationMB = `
$disk.ExtensionData.VflashCacheConfigInfo.ReservationInMB
$info.CacheType = `
$disk.ExtensionData.VFlashCacheConfigInfo.CacheConsistencyType
$info.CacheMode = `
$disk.ExtensionData.VFlashCacheConfigInfo.CacheMode
$info.BlockSizeKB = `
$disk.ExtensionData.VFlashCacheConfigInfo.BlockSizeInKB
$info.VFlashModule = `
$disk.ExtensionData.VFlashCacheConfigInfo.VFlashModule
}
$info
}
}
Being able to see the current vFRC settings for a VMDK is useful, but what we really care about is being able to configure those settings. We created a function for that too! Take a look at Listing 5-7.
Listing 5-7: The Set-HarddiskVFRC
function
function Set-HarddiskVFRC {
<# .SYNOPSIS
Set the configuration of vFRC for a virtual machine hard disk.
.DESCRIPTION
Sets the vFRC configuration for a VM hard disk. Will use
sane defaults if none are provided, setting the reserve to
1GB with strong cache type and 4KB block size.
.EXAMPLE
Set vFRC to the defaults
Set-HarddiskVFRC -Disk (Get-VM someVM | Get-VMHarddisk)
.EXAMPLE
Use the pipeline to get the vFRC status for all hard disks
in a vApp
Get-vApp "My Application" | Get-VM | Get-VMHarddisk |
Get-HarddiskVFRC
.PARAMETER Disk
[VMware.VimAutomation.ViCore.Impl.V1.VirtualDevice.HardDisk]
The VM hard disk to modify
.PARAMETER Enable
[System.Boolean]
Enable vFRC for the VM hard disk
.PARAMETER Disable
[System.Boolean]
Disable vFRC for the VM hard disk
.PARAMETER CacheMode
[System.String]
The cache mode, "WriteBack" or "WriteThru"
.PARAMETER CacheType
[System.String]
The cache type, "strong" or "weak"
.PARAMETER BlockSizeKB
[System.Int]
The block size of the cache, 4-1024KB
.PARAMETER ReservationMB
[System.Int]
The reservation size for the disk, in MB. 4MB-409600MB
.INPUTS
[VMware.VimAutomation.ViCore.Impl.V1.VirtualDevice.HardDisk]
.OUTPUTS
PSCustomObject
#>
[CmdletBinding(DefaultParameterSetName="EnableVfrc")]
param (
# the disk to enable/disable vFRC on
[parameter(
Mandatory=$true,
ValueFromPipeline=$true
)]
[VMware.VimAutomation.VICore.Types.V1.VirtualDevice.HardDisk]
$Disk
,
# enable cache for this disk
[parameter(
Mandatory=$true,
ParameterSetName="EnableVfrc"
)]
[Switch]$Enable
,
# disable cache for this disk
[parameter(
Mandatory=$true,
ParameterSetName="DisableVfrc"
)]
[Switch]$Disable
,
[parameter(
Mandatory=$false,
ParameterSetName="EnableVfrc"
)]
[ValidateSet("WriteBack", "WriteThru")]
[String]$CacheMode
,
[parameter(
Mandatory=$false,
ParameterSetName="EnableVfrc"
)]
[ValidateSet("strong", "weak")]
[string]$CacheType
,
[parameter(
Mandatory=$false,
ParameterSetName="EnableVfrc"
)]
[ValidateSet(4,8,16,32,64,128,256,512,1024)]
[Int]$BlocksizeKB
,
[parameter(
Mandatory=$false,
ParameterSetName="EnableVfrc"
)]
[ValidateRange(4, 409600)]
[Int]$ReservationMB
)
process {
# get the owning VM's view
$parentVm = Get-VM -Id $disk.Parentid
if ($parentVm.Version -ne "v10") {
Write-Warning "VM must be version 10"
} else {
$parentView = $parentVm | Get-View -Property Name
# set the default values for the vFRC
$VflashConfig = New-Object
VMware.Vim.VirtualDiskVFlashCacheConfigInfo
# If we are disabling, set the reservation to 0
if ($Disable) {
$VflashConfig.ReservationInMB = 0
}
if ($Enable) {
# if the user specified write thru, or no
# value was provided, or no value is currently
# set, default to write_thru
if ($CacheMode -eq "WriteThru" -or
(
$CacheMode -eq $null -and
$disk.ExtensionData.VFlashCache`
ConfigInfo.CacheMode -eq $null
)
) {
$VflashConfig.CacheMode = [VMware.Vim.`
VirtualDiskVFlashCacheConfigInfoCacheMode]::write_thru
} elseif ($CacheMode -eq "WriteBack") {
# only if the user specifies do we use
# write_back since it is dangerous
$VflashConfig.CacheMode = [VMware.Vim.`
VirtualDiskVFlashCacheConfigInfoCacheMode]::write_back
}
# will be a value specified, or null, which
# will automatically use the default, or
# keep the current setting
$VflashConfig.BlockSizeInKB = $BlocksizeKB
# if the user didn't specify, and the value
# isn't already set, use strong
if ($CacheType -eq $null -and $disk.`
ExtensionData.VFlashCacheConfigInfo.CacheConsistencyType `
-eq $null) {
$VflashConfig.CacheConsistencyType = `
[VMware.Vim.VirtualDiskVFlashCache`
ConfigInfoCacheConsistencyType]::strong
} elseif ($CacheType -eq $null -and
$disk.ExtensionData.VFlashCache`
ConfigInfo.CacheConsistencyType -ne $null) {
# if it's already set, keep it the same
$VflashConfig.CacheConsistencyType = $null
} else {
# use what was specified
$VflashConfig.CacheConsistencyType = `
[VMware.Vim.VirtualDiskVFlashCache`
ConfigInfoCacheConsistencyType]::$CacheType
}
# set a default of 1024MB, use what was
# provided, or leave it alone
if ($ReservationMB -eq 0 -and
$disk.ExtensionData.VFlashCacheConfigInfo.`
ReservationInMB -eq $null) {
$VflashConfig.ReservationInMB = 1024
} elseif ($ReservationMB -ne 0) {
$VflashConfig.ReservationInMB = `
$ReservationMB
} else {
$VflashConfig.ReservationInMB = `
$disk.ExtensionData.`
VFlashCacheConfigInfo.`
ReservationInMB
}
}
# specify the operation on the disk
$deviceTask = New-Object VMware.Vim.VirtualDevice`
ConfigSpec
$deviceTask.Operation = [VMware.Vim.VirtualDevice`
ConfigSpecOperation]::Edit
$deviceTask.Device = $disk.ExtensionData
# provide the new vFRC settings
$deviceTask.Device.VFlashCacheConfigInfo = `
$VflashConfig
# specify the operation on the VM
$vmTask = New-Object VMware.Vim.VirtualMachine`
ConfigSpec
$vmTask.DeviceChange = $deviceTask
# execute the spec against the VM
$taskId = $parentView.ReconfigVM_Task( $vmTask )
$task = Get-View $taskId
# wait for the task to finish
while (
"running", "queued" -contains $task.Info.State
) {
$task.UpdateViewData("Info")
Start-Sleep -Seconds 1
}
# return an error or the current status on success
if ($task.Info.State -eq "error") {
Write-Error $task.info.Error.LocalizedMessage
} else {
Get-HardDisk -id $Disk.Id -VM $parentVm |
Get-HarddiskVFRC
}
}
}
}
To take advantage of this function, simply pipe a virtual disk object into it and enable or disable the vFRC. Listing 5-8 shows how the command can be used to enable vFRC for all VMDKs of all virtual machines in a particular vApp:
Listing 5-8: Using the Set-HarddiskVFRC
function
Get-VApp $name | Get-VM | Get-HardDisk |
Set-HarddiskVFRC -Enable -ReservationMB 512
Name : Hard disk 1
Reservation : 512MB
CacheType : strong
CacheMode : write_thru
BlockSize : 8KB
VFlashModule : vfc
Putting these together, you can take a set of virtual machines, determine if they are using vFRC, and enable any without cache, as demonstrated in Listing 5-9.
Listing 5-9: Enabling vFRC for a subset of VMs
Get-vApp ReadSensitiveWorkload |
Get-VM |
Get-HardDisk | Where-Object {
($_.Name -ne "Hard disk 1") -and
(
(Get-HarddiskVFRC $_).ReservationMB -eq $null
)
} | Foreach-Object {
Set-HarddiskVFRC -Disk $_ -Enable -BlocksizeKB 4 -ReservationMB 128
}
VM : test2
DiskName : Hard disk 2
ReservationMB : 128
CacheType : strong
CacheMode : write_thru
BlockSizeKB : 4
VFlashModule : vfc
VM : test1
DiskName : Hard disk 2
ReservationMB : 128
CacheType : strong
CacheMode : write_thru
BlockSizeKB : 4
VFlashModule : vfc
vFRC is a powerful feature that provides a significant boost to performance for VMDKs that need extra read IOPS; however, there are some catches you should be aware of:
If you have virtual machines that can benefit from increased read IOPS and decreased read latency and you have hosts with SSDs installed, then leveraging vFRC is an inexpensive way to provide the needed boost. Adding drives to your shared storage solution can quickly become expensive, whereas with vFRC you can easily manage the cost and balance the capacity so that only those VMs that require the additional capability get it, thus ensuring maximum efficiency.
Distributed Resource Scheduler (DRS) is one of the defining features of vCenter and vSphere. It enables a virtualization administrator to add servers into a pool (also known as a cluster), after which the software balances the resource consumption across them. This is a simple process that allows greater utilization of hardware. However, it hasn’t been without warts and a couple of features that would make life a lot easier.
Fortunately, in vCenter 5.1 one of the most frequently requested features, DRS groups, was added. By grouping hosts and VMs together, administrators can communicate the concept of locality and applications to vCenter and apply rules at that granularity.
Let’s say that you have two racks of servers in the same cluster and you want to make sure that your domain controllers stay in separate racks. With groups, this is trivial; you create a group for each rack and the VMs, and then create two affinity rules for the VMs, and the desired configuration is quickly achieved. This configuration can be done using the web or desktop clients, but that process is quite click-intensive. We have created three functions that create a DRS host group (Listing 5-10), a VM group (Listing 5-11), and finally an affinity rule for the two groups (Listing 5-12).
Listing 5-10: The New-DrsHostGroup
function
function New-DrsHostGroup {
<# .SYNOPSIS
Create a DRS host group.
.DESCRIPTION
Creates a DRS host group in the specified cluster using one,
or more, VM hosts as members.
.EXAMPLE
Create a group with specific hosts
New-DrsHostGroup -Cluster clusterName -VMHost host1,host2 `
-GroupName MyGroup
.EXAMPLE
Use the pipeline to specify the cluster for the new DRS group
Get-Cluster clusterName | New-DrsHostGroup -VMHost host1,host2 `
-GroupName MyGroup
.PARAMETER Cluster
[System.String]
The name of the cluster to create the group in.
.PARAMETER VMHost
[System.String][]
An array of hosts to put into the newly created group.
.PARAMETER GroupName
[System.String]
A name for the new group.
.INPUTS
[VMware.VimAutomation.ViCore.Impl.V1.Inventory.ClusterImpl]
#>
[CmdletBinding()]
param(
[parameter(
Mandatory=$true,
ValueFromPipelineByPropertyName=$true
)]
[Alias('Name')]
[String]
$Cluster
,
[parameter(Mandatory=$true)]
[String[]]
$VMHost
,
[parameter(Mandatory=$true)]
[String]
$GroupName
)
$clusterObj = Get-Cluster -Name $Cluster
$clusterSpec = New-Object VMware.Vim.ClusterConfigSpecEx
$hostGroup = New-Object VMware.Vim.ClusterGroupSpec
# add, edit or remove the DRS group
$hostGroup.operation = "add"
# specify that this is a host group
$hostGroup.Info = New-Object VMware.Vim.ClusterHostGroup
# give it a name
$hostGroup.Info.Name = $GroupName
# add the host MoRefs
$VMHost | Foreach-Object {
$hostGroup.Info.Host += (Get-VMHost $_).Id
}
# add the group to the cluster settings specification
$clusterSpec.GroupSpec += $hostGroup
# execute the reconfigure method
$clusterObj.ExtensionData.ReconfigureComputeResource(
# provide the specification created above
$clusterSpec,
# set to true to update the cluster, set to false
# to unconfigure the cluster, except for what was set
# as a part of this specification
$true
)
}
This function is used to create DRS host groups. However, it can be modified very easily to edit or remove existing groups by changing line 31 from $hostGroup.operation = "add"
to $hostGroup.operation = "edit"
or $hostGroup.operation = "remove"
. This simple ability to specify what type of operation needs to be done is true of many of the modification specification tasks that are used by VMware’s SDK.
Note that the second parameter for the ReconfigureComputeResource
method invocation, a Boolean option in addition to configuration specification, is very important. This specifies that we are updating the cluster’s configuration. Without it, any settings not provided in our specification will be unset, which could make for a very bad day!
Listing 5-11: The New-DrsVmGroup
function
function New-DrsVmGroup {
<# .SYNOPSIS
Create a DRS virtual machine group.
.DESCRIPTION
Creates a DRS virtual machine group in the specified
cluster using one, or more, VMs as members.
.EXAMPLE
Create a group with specific VMs
New-DrsVmGroup -Cluster clusterName -VM vm1,vm2 `
-GroupName MyVMGroup
.EXAMPLE
Use the pipeline to specify the cluster for the new DRS group
Get-Cluster clusterName | New-DrsVmGroup -VM vm1,vm2 `
-GroupName MyVMGroup
.PARAMETER Cluster
[System.String]
The name of the cluster to create the group in.
.PARAMETER VMH
[System.String][]
An array of VMs to put into the newly created group.
.PARAMETER GroupName
[System.String]
A name for the new group.
.INPUTS
[VMware.VimAutomation.ViCore.Impl.V1.Inventory.ClusterImpl]
#>
[CmdletBinding()]
param(
[parameter(
Mandatory=$true,
ValueFromPipelineByPropertyName=$true
)]
[Alias('Name')]
[String]
$Cluster
,
[parameter(Mandatory=$true)]
[String[]]
$VM
,
[parameter(Mandatory=$true)]
[String]
$GroupName
)
$clusterObj = Get-Cluster -Name $Cluster
$clusterSpec = New-Object VMware.Vim.ClusterConfigSpecEx
$vmGroup = New-Object VMware.Vim.ClusterGroupSpec
# add, edit, or remove the VM group
$vmGroup.operation = "add"
# specify that this is a VM group
$vmGroup.Info = New-Object VMware.Vim.ClusterVmGroup
# give it a name
$vmGroup.Info.Name = $GroupName
# add the VMs to the group
$VM | Foreach-Object {
$vmGroup.Info.VM += (Get-VM $_).id
}
# add the VM group to the cluster specification
$clusterSpec.GroupSpec += $vmGroup
# execute the cluster reconfigure
$clusterObj.ExtensionData.ReconfigureComputeResource(
# provide the specification
$clusterSpec,
# update the cluster config
$true
)
}
This function is nearly identical to the function for creating a host group and has the same ability to be modified to edit or remove a VM group. The next function, in Listing 5-12, is used to set the group affinity by combining the virtual machine and host groups created using the previous functions with the logic to create rule sets.
Listing 5-12: The New-DrsGroupAffinity
function
function New-DrsGroupAffinity {
<# .SYNOPSIS
Create DRS affinity rule for a VM and host group.
.DESCRIPTION
Creates a DRS affinity group in the specified cluster using
the host and VM groups provided as members. If mandatory is
false, then the rule is a "should" rule, not a "must" rule.
.EXAMPLE
Create a mandatory group
Get-Cluster clusterName | New-DrsGroupAffinity `
-HostGroup MyGroup -VmGroup MyVmGroup `
-RuleName MyAffineGroup -Mandatory
.PARAMETER Cluster
[System.String]
The name of the cluster to create the group in.
.PARAMETER HostGroup
[System.String]
The name of the host group to add to the rule.
.PARAMETER VmGroup
[System.String]
The name of the VM group to add to the rule.
.PARAMETER RuleName
[System.String]
The name of the rule.
.PARAMETER Mandatory
[System.Boolean]
If true, rule is a "must", otherwise it is a "should".
.INPUTS
[VMware.VimAutomation.ViCore.Impl.V1.Inventory.ClusterImpl]
#>
[CmdletBinding()]
param(
[parameter(
Mandatory=$true,
ValueFromPipelineByPropertyName=$true
)]
[Alias('Name')]
[String]
$Cluster
,
[parameter(Mandatory=$true)]
[String[]]
$HostGroup
,
[parameter(Mandatory=$true)]
[String]
$VmGroup
,
[parameter(Mandatory=$true)]
[String]
$RuleName
,
[parameter(Mandatory=$false)]
[Switch]
$Mandatory = $false
)
$clusterObj = Get-Cluster $Cluster
$clusterSpec = New-Object VMware.Vim.ClusterConfigSpecEx
$rule = New-Object VMware.Vim.ClusterRuleSpec
# add, edit, remove the rule
$rule.operation = "add"
# the type of this object determines the type of rule being
# created:
# ClusterVmHostRuleInfo = VM(s) (anti)affinity to host(s)
# ClusterAffinityRuleSpec = VMs will be together
# ClusterAntiAffinityRuleSpec = VMs will be separated
#
$rule.Info = New-Object VMware.Vim.ClusterVmHostRuleInfo
# enable or disable the rule
$rule.Info.enabled = $true
# a name
$rule.Info.name = $RuleName
# this has different actions depending on the type of rule.
# - for ClusterVmHostRuleInfo:
# - setting mandatory to false is the equivalent of a
# "should" rule
# - setting mandatory to true is the equivalent of a
# "must" rule
# - for VM affinity and antiaffinity rules:
# - true will prevent the VMs from powering on if the
# rule will be violated
# - false will allow the VMs to be powered on and DRS
# will attempt to keep the rule in compliance
$rule.Info.mandatory = $Mandatory
# the name of the VM group
$rule.Info.vmGroupName = $VmGroup
# if we want to keep the VMs on the specified hosts, use an
# affine group
$rule.Info.affineHostGroupName = $HostGroup
# alternately, to keep the VMs off the specified hosts, use
# and antiaffine group
# rule.Info.antiAffineHostGroupName = $HostGroup
# add our rules to the cluster specification
$clusterSpec.RulesSpec += $rule
# execute the cluster reconfigure
$clusterObj.ExtensionData.ReconfigureComputeResource(
# provide the specification
$clusterSpec,
# update the cluster config
$true
)
}
Pay close attention to the comments in the function created using Listing 5-12. This example function provides the basis to configure many different kinds of DRS rules in your cluster. For example, if you want VMs to be kept off of a particular group of hosts, you would use an antiAffineHostGroupName
rule in your function. This same pattern is followed when specifying DRS rules for individual VMs, rather than DRS groups.
Now that we have our three base functions, we can put them together to create DRS groups and a rule that will ensure that our hosts and VMs are kept together (see Listing 5-13).
Listing 5-13: Using DRS groups to enforce affinity
Get-Cluster $clustername | `
New-DrsHostGroup -GroupName $hostGroup `
-VMHost $host1,$host2
Get-Cluster $clustername | `
New-DrsVmGroup -GroupName $vmGroup -VM $vm1,$vm2
Get-Cluster $clustername | `
New-DrsGroupAffinity -HostGroup $hostGroup `
-VmGroup $vmGroup -RuleName $ruleName -Mandatory:$false
Using these functions and their brethren capable of editing and removing the respective objects, it becomes easy to maintain affinity and anti-affinity rules for your VM and host groups. This is particularly useful where you are using a vSphere Metro Storage Cluster (vMSC) configuration. It is unwise to have VM storage traffic traversing the intersite links; those need to be using bandwidth for storage replication. Creating a scheduled task to maintain the correct locality affinity can significantly save on bandwidth between sites; it removes complexity during the provisioning process. Neither junior administrators nor an automated deployment system need to know or understand the underlying physical architecture and limitations. Instead your automation ensures that the VM layout is always optimal.
VMware Fault Tolerance (FT) was first introduced with vSphere 4.0. FT is an extension of VMware High Availability (HA) that enables guaranteed zero downtime. At a high level, it works by creating a second VM on a compatible vSphere host and replicates every external input to the CPU from the primary VM to the secondary VM. This results in the processors executing the same internal code on a per-instruction basis. Then, if the primary VM dies for any reason, the secondary VM continues executing and picks up where the primary left off. The operating system has no knowledge that it just blinked and awakened on a different physical host. More importantly, your users won’t know either. So, how do you enable this uptime pixie dust? In PowerCLI, it’s one line (see Listing 5-14).
Listing 5-14: Enabling FT
(Get-VM "HMIC").ExtensionData.CreateSecondaryVM_Task($null)
Optionally, you could even designate which VMHost the secondary VM is created on by supplying the VMHost’s managed object reference, or MoRef (see Listing 5-15).
Listing 5-15: Specifying where the secondary VM should be created
$VMHost = Get-VMHost -Name 'vSphere01*'
$VM = Get-VM -Name 'HVMIC'
$VM.ExtensionData.CreateSecondaryVM_Task($VMHost.Id)
As we just showed, it’s simple to enable FT. It’s just as simple to disable, as Listing 5-16 shows.
Listing 5-16: Disabling FT
$VM = Get-VM -Name 'HVMIC' | Where-Object {
$_.ExtensionData.Config.FtInfo.Role -eq 1}
$VM.ExtensionData.TurnOffFaultToleranceForVM()
You will find that this highly protected state carries with it some restrictions. For instance, you cannot modify any configuration or settings for the VM while it’s protected with FT. You will need to follow these steps:
Fortunately, it’s relatively easy to wrap the whole change in a PowerCLI script. In Listing 5-17, we demonstrate changing the RAM allocation on an FT-protected VM.
Listing 5-17: Modifying FT-protected VMs
# Get the VM
$VM = Get-VM -Name 'HVMIC' | Where-Object {
$_.ExtensionData.Config.FtInfo.Role -eq 1}
# Disable FT
$VM.ExtensionData.TurnOffFaultToleranceForVM()
# Add memory
Set-VM -VM $VM -MemoryMB 4096 -Confirm:$false
# Enable FT
$VM.ExtensionData.CreateSecondaryVM_Task($null)
We don’t feel FT replaces the need for application-level redundancy, and it does not replace backups. However, if your application users demand extreme availability and the virtual machine matches the constraints of FT, it is an excellent option with relatively little administrative overhead. Should you choose to enable FT in your virtual environment, PowerCLI is the glue that allows you to manage FT-protected VMs at scale.
Distributed Power Management (DPM) is an extension of the Distributed Resource Scheduler (DRS). DPM focuses on maximizing the power efficiency of your virtual environment by powering physical hosts down when not needed and turning them back on when the additional capacity is required. DPM is enabled on a per-cluster basis and achieves these power savings by carefully balancing the load across a given cluster. Once enabled, DPM then carefully monitors the resource utilization of the cluster—and particularly of each host. When it determines that consolidating VMs onto fewer hosts will result in a host being unused, it will execute those vMotion operations and then power off the unused host. When an increase in load is detected DPM will power on hosts to ensure sufficient capacity to meet the demands of the environment. All in all, DPM is a fantastic technology and should be enabled on all environments.
Unfortunately as of this writing, PowerCLI doesn’t offer any cmdlets for managing DPM. We wrote some you can use until VMware offers official support. So, how can you find out if you’re running DPM? Listing 5-18 contains a function that gives you the answer.
Listing 5-18: The Get-DPM
function
function Get-DPM {
<# .SYNOPSIS
Get the current DPM status for a cluster.
.EXAMPLE
Get the DPM status for a cluster
Get-Cluster clusterName | Get-DPM
.PARAMETER Cluster
[VMware.VimAutomation.ViCore.Impl.V1.Inventory.ClusterImpl]
The cluster to check.
.INPUTS
[VMware.VimAutomation.ViCore.Impl.V1.Inventory.ClusterImpl]
.OUTPUTS
PSCustomObj
#>
[CmdletBinding()]
param(
[parameter(
Mandatory=$true,
ValueFromPipelineByPropertyName=$true
)]
[Alias('Name')]
[String]
$Cluster
)
process {
# get the view's extended configuration data
$clusterObj = Get-Cluster $Cluster
$clusterView = $ClusterObj | Get-View -Property Name,ConfigurationEx
$dpmInfo = $clusterView.ConfigurationEx.DpmConfigInfo
# an anon object to hold only the data we care about
$info = "" | Select-Object Name,Status,Threshold,Behavior
# Set the properties in our anon object based on the
# different sources.
$info.Name = $clusterView.Name
# use a friendly name instead of a boolean
if ($dpmInfo.Enabled -eq $false) {
$info.Status = "Disabled"
} else {
$info.Status = "Enabled"
}
$info.Behavior = $dpmInfo.DefaultDpmBehavior
$info.Threshold = $dpmInfo.HostPowerActionRate
# return the object
$info
}
}
Using the Get-DPM
function, you can quickly get the status of DPM running across the environment in one line:
Get-Cluster | Get-DPM
Once you’ve identified the clusters that are not running DPM, you can use the Set-DPM
function shown in Listing 5-19 to configure DPM.
Listing 5-19: The Set-DPM
function
function Set-DPM {
<# .SYNOPSIS
Configures DPM for a cluster.
.EXAMPLE
Enable DPM for a cluster
Get-Cluster clusterName | Set-DPM -Enable
.EXAMPLE
Enable DPM for a cluster and set it to manual
Get-Cluster clusterName | Set-DPM -Enable -Behavior Manual
.PARAMETER Cluster
[System.String]
The cluster to check.
.PARAMETER Enable
[System.Boolean]
Enable DPM.
.PARAMETER Disable
[System.Boolean]
Disable DPM.
.PARAMETER Threshold
[System.Integer]
A value between 1 and 5 indicating the aggressiveness of DPM
actions. 1 is most aggressive, 5 is least.
.PARAMETER Behavior
[System.String]
Manual or Automated, indicating whether DPM will automatically
apply recommendations or wait for the user to apply.
.INPUTS
[VMware.VimAutomation.ViCore.Impl.V1.Inventory.ClusterImpl]
.OUTPUTS
PSCustomObj
#>
[CmdletBinding(SupportsShouldProcess=$true)]
param(
[parameter(
Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
ParameterSetName="EnableDpm"
)]
[parameter(
Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
ParameterSetName="DisableDpm"
)]
[Alias('Name')]
[String]
$Cluster
,
[parameter(
Mandatory=$true,
ParameterSetName="EnableDpm"
)]
[Switch]
$Enable
,
[parameter(
Mandatory=$true,
ParameterSetName="DisableDpm"
)]
[Switch]
$Disable
,
[parameter(
Mandatory=$false,
ParameterSetName="EnableDpm"
)]
[ValidateRange(1, 5)]
[Int]
$Threshold
,
[parameter(
Mandatory=$false,
ParameterSetName="EnableDpm"
)]
[ValidateSet("Automated", "Manual")]
[String]
$Behavior
)
process {
# an array to hold messages
$whatIf = @()
# get the relevant config data
$clusterObj = Get-Cluster $Cluster
$clusterView = $clusterObj | Get-View -Property Name
$dpmConfig = New-Object VMware.Vim.ClusterDpmConfigInfo
# set the operation based on the Enable/Disable property
# also, set the WhatIf operation
if ($Disable) {
$dpmConfig.Enabled = $false
$whatIf += "Disabling DPM"
}
if ($Enable) {
$dpmConfig.Enabled = $true
$whatIf += "Enabling DPM"
# set the remaining properties if provided
if ($Behavior) {
$dpmConfig.DefaultDpmBehavior = `
[VMware.Vim.DpmBehavior]::$Behavior
$whatIf += "Setting behavior to: $($Behavior)"
}
if ($Threshold) {
$dpmConfig.HostPowerActionRate = $Threshold
$whatIf += "Setting Threshold to: $($Threshold)"
}
}
# create a config spec and set our info
$clusterConfig = New-Object `
VMware.Vim.ClusterConfigSpecEx
$clusterConfig.DpmConfig = $dpmConfig
if ($PSCmdlet.ShouldProcess(
$clusterView.Name,
$whatIf -join ", "
)
) {
# execute the update
$taskId = $clusterView.`
ReconfigureComputeResource_Task(
$clusterConfig,
$true
)
$task = Get-View $taskId
# wait for the update to finish
while (
"running", "queued" -contains $task.Info.State
) {
$task.UpdateViewData("Info")
Start-Sleep -Seconds 1
}
# return an error or the current status
if ($task.Info.State -eq "error") {
Write-Error $task.info.Error.LocalizedMessage
} else {
$clusterObj | Get-DPM
}
}
}
}
Putting it all together, to enable DPM on any cluster where it is currently disabled, you would run the following:
Get-Cluster |
Get-DPM |
Where-Object { $_.Status -eq "Disabled" } |
Set-DPM -Enable
You can also perform basic administration. For example, you can set the host power action rate to 4 and the default DPM behavior to automatic with just one line of PowerCLI:
Get-Cluster |
Get-DPM |
Where-Object { $_.Status -eq "Enabled" } |
Set-DPM -Behavior Automated -Threshold 4 -Enable
Now that you can automate DPM, get out there and save some energy! In all seriousness, it’s understandable to be apprehensive about shutting down vSphere hosts. Our advice is to start conservatively and test DPM in your environment. Slowly, as you gain confidence and start to realize the power savings, you will naturally crank up the automation level. If you’re not currently using DPM, there is no reason not to begin testing. The power costs associated with a datacenter are one of the largest ongoing expenses associated with servers. Who knows? Cut the power bill and perhaps you can talk your boss into a trip to VMWorld!
Host profiles are used to ensure compliance across a series of hosts within vCenter Server. This is accomplished by identifying a reference host and creating a host profile. The host profile captures the configuration of that host and saves it into the vCenter Server database. At any time after that, you can test the configuration of a given vSphere host against that profile and report on compliance. If a host has fallen out of compliance, you simply reapply the profile. As of vSphere 6, host profiles currently cover the following configuration settings:
Each new version of vSphere increases the coverage of host profiles and eliminates many of the settings that previously would require further automation to complete configuration. Despite this, there are some notable items that cannot be configured or monitored via host profiles:
Getting started with host profiles is a snap. Simply configure one host to your organization’s standards and create a new host profile. In Listing 5-20, we created a host profile for a cluster named Prod01
based on a vSphere host named vSphere01
.
Listing 5-20: Creating a new host profile
New-VMHostProfile -Name Prod01 `
-ReferenceHost (Get-VMHost vSphere01*) `
-Description "Host profile for cluster Prod01"
Once the new profile is created, you can attach it to any vSphere host/cluster in vCenter Server. The code in Listing 5-21 associates the Prod01
profile to the Prod01
cluster, and then tests every host in the cluster for compliance.
Listing 5-21: Associating a host profile and checking for compliance
Apply-VMHostProfile -Entity (Get-Cluster prod01) `
-Profile (Get-VMHostProfile prod01) `
-AssociateOnly |
Get-VMHost |
Test-VMHostProfileCompliance
You could even take it a step further and apply your new profile, as shown in Listing 5-22.
Listing 5-22: Applying a host profile to any noncompliant host
Get-Cluster Prod01 |
Get-VMHost |
Test-VMHostProfileCompliance |
ForEach-Object {
$profile = Get-VMHostProfile $_.VMHostProfile
Set-VMHost -State 'Maintenance' -VMHost $_.VMhost |
Apply-VMHostProfile -Profile $Profile |
Set-VMHost -State 'Connected' |
Test-VMHostProfileCompliance
}
Of course, no environment is static. From time to time, you will need to make changes to hosts under the control of a host profile. As fantastic as vMotion is, it does take quite a bit of time to evacuate a host—and applying a profile requires that a host be in Maintenance mode. Therefore, when you’re making changes we highly recommend using the following workflow:
For example, the script in Listing 5-23 adds a new NFS datastore, updates the host profile, and then scans for compliance.
Listing 5-23: Making changes on a cluster using host profiles
$cluster = Get-Cluster prod01
# Add the datastore
$cluster | Get-VMHost |
New-Datastore -Name prod01_03 `
-Nfs `
-NfsHost 192.168.1.3 `
-Path /vol/prod01_03
# get the profile for the cluster
$VMhostProfile = $cluster | Get-VMHostProfile
# update the profile from the reference host
$profileSpec = New-Object VMware.Vim.HostProfileHostBasedConfigSpec
$profileSpec.host = `
(Get-View -Id $VMhostProfile.ReferenceHostId -Property Name).MoRef
$profileSpec.useHostProfileEngine = $true
$VMhostProfile.ExtensionData.UpdateHostProfile( $profileSpec )
# test compliance for all hosts
$cluster | Get-VMHost | Test-VMHostProfileCompliance
If you are fortunate enough to have the licensing, enjoy host profiles! They are a fantastic tool that truly simplifies the management of any size environment.
Active Directory (AD) integration has been refined over the last several versions of PowerCLI and now has been simplified down to just two commands: Get-VMHostAuthentication
(Listing 5-24) and Set-VMHostAuthentication
(Listing 5-25). Using AD to authenticate users for vSphere Hypervisor access is a simple, easy-to-maintain, and efficient method of ensuring that only authorized users are able to connect to the hosts using the CLI or vSphere Client. Using this method also provides a quick and easy way to ensure that when administrators join or leave your team, their access can quickly be updated from a single point.
Listing 5-24: Getting the current host authentication settings
Get-VMHost $hostname | Get-VMHostAuthentication
Domain DomainMembershipStatus TrustedDomains
------ ---------------------- --------------
Note that for the particular ESXi host in Listing 5-24 no Active Directory authentication has been defined; hence no domain membership is listed. To join the host to a domain, pipe the output of the Get-VMHostAuthentication
cmdlet into the Set-VMHostAuthentication
command.
Listing 5-25: Joining an ESXi host to a domain
$domainname = "powercli.lan"
$credential = Get-Credential
$hostname = "esxi-01.powercli.lan"
$splat = @{
'Domain' = $domainname;
'Credential' = $credential;
}
Get-VMHost $hostname |
Get-VMHostAuthentication |
Set-VMHostAuthentication @splat -JoinDomain
Now when you execute the Get-VMHostAuthentication
against the host, you can see that it is joined to the domain.
By default the administrators group for the ESXi host is set to ESX Admins
, but since this is a well-known group, it may be undesirable to leave it from a security perspective. You can change the administrators group for the host by adjusting the value of a particular advanced option: Config.HostAgent.plugins.hostsvc.esxAdminsGroup
. You can use the web or desktop clients to modify the setting or use a simple PowerShell function, as shown in Listing 5-26.
Listing 5-26: The Set-VMHostADAdminGroup
function
function Set-VMHostADAdminGroup {
<# .SYNOPSIS
Sets the Active Directory group to be used for ESXi host
administrators.
.EXAMPLE
Get-VMHost host1 | Set-VMHostADAdminGroup -Group "VM Admins"
.PARAMETER VMHost
[VMware.VimAutomation.ViCore.Impl.V1.Inventory.VMHostImpl]
The host to modify.
.PARAMETER GroupName
[System.String]
The name of the group to assign as the ESXi administrator.
.INPUTS
[VMware.VimAutomation.ViCore.Impl.V1.Inventory.VMHostImpl]
.OUTPUTS
PSCustomObj
#>
[CmdletBinding(SupportsShouldProcess=$true)]
param(
[parameter(
Mandatory=$true,
ValueFromPipeline=$true
)]
[VMware.VimAutomation.ViCore.Impl.V1.Inventory.VMHostImpl]
$VMHost
, [Parameter(Mandatory=$true)]
[String]$Group
)
process {
$VMHost | Foreach-Object {
if ($PSCmdlet.ShouldProcess(
$_.Name,
"Setting esxAdminsGroup to $($Group)"
)) {
# if the user approves, update the advanced
# setting for this host
$_ | Get-AdvancedSetting -Name `
Config.HostAgent.plugins.hostsvc.esxAdminsGroup |
Set-AdvancedSetting -Value $Group -Confirm:$false |
Out-Null
# create an anon object to return the current setting
$obj = "" | Select-Object "AdminGroup"
# get the current setting and populate the property
$obj.AdminGroup = ($VMHost | Get-AdvancedSetting -Name `
Config.HostAgent.plugins.hostsvc.esxAdminsGroup).Value
# return/display the current value
$obj
}
}
}
}
The Set-VMHostADAdminGroup
function simplifies setting the administrators group by allowing you to pipeline the host object, as shown in Listing 5-27.
Listing 5-27: Using the Set-VMHostADAdminGroup
cmdlet
$groupname = "vSphere Host Admins"
Get-VMHost $hostname | Set-VMHostADAdminGroup -Group $groupname
AdminGroup
----------
vSphere Host Admins
Many of these advanced features make the core of what differentiates VMware’s vSphere from the other hypervisors. Utilizing your infrastructure—and your hypervisor—to its fullest ensures that you are deriving the most value from your investment. Although the features are what motivate you to continue using ESXi as the hypervisor of choice, PowerCLI offers an incredibly robust and capable platform to provide the automation framework for all your tasks. By capitalizing on automation, you eliminate human error, misconfiguration, and the inevitable downtime that results.