But I wanted a more sophistical approach where my function shutdown the VM properly and then change the controller and starts the VM, the tricky part here was to check that the VM actually had shutdown before I change controller 🙂
function Change-ScsiController{
<#
.SYNOPSIS
Stop a VM and replace the SCSI controller and start it again
.DESCRIPTION
Use this function to change scsi cntroller
.PARAMETER xyz
.NOTES
Author: Niklas Akerlund / RTS
Date: 2012-03-21
#>
param (
[Parameter(Position=0,Mandatory=$true,HelpMessage="A virtual machine",
ValueFromPipeline=$True)]
$VM
)
$VM = Get-VM $VM
Shutdown-VMGuest -VM $VM -Confirm:$false
while ($VM.PowerState -eq "PoweredOn")
{
$VM = Get-VM $VM
Start-Sleep -s 1
}
Get-ScsiController -VM $VM | where {$_.Type -eq "Paravirtual"} | Set-ScsiController -Type VirtualLsiLogicSAS
Start-VM -VM $VM
}
During the ICM course that I was co-teaching last week i found a bug (I think) in the vSphere Client.
When I have installed a windows 2003 virtual machine and also deployed the VM tools i can configure the behavior of the power controls when editing the properties.
The interesting part here is that when i open the virtual machine console, although as you can see that i have set it to “Shut Down guest”, when i press that button in the virtual machine console i get a question about if i am sure that i want to turn it off, this as you can see on my next screenshot. And some of you will say, oh your tools are not running, that is why you get this, but…
If i use the power controls in the vSphere Client i get the following confirmation, that is the correct one ( I have not restarted or done anything other than change to the Client instead of the virtual machine console)
The strange thing is that it is not consistent this error and some times in the virtual machine console the stop button works as expected.
After this week when i have been co-teaching a VMware vSphere ICM course i was going to show the students FT and I was seeking in the powerCLI reference for some cmdlets but could not find any.
I created my own function for enabling this, i got inspiration from Cody Bunch post but extended it with a function and also with my function you can disable/enable/remove and test restart/failover the secondary vm. I also check if the FT is enabled or not before making any changes.
Here is the PowerCLI function, i am using some Get-View to be able to use the FT functions.
Updated: Today (Monday 27/2) i added functionality with a possibility to also send VMHost as argument, but then i realized some errors occuring, my assumtions that the primary VM always was the first object when doing a Get-VM was false, i have corrected the function that it actually checks for the correct VM, Primary or Secondary. If you do not send VMHost parameter the FT process will choose a Host for you, this cannot be the same as the Primary VM resides on.
function Set-VMFaultTolerance{
<#
.SYNOPSIS
Enable FT for the VM sent as parameter
.DESCRIPTION
Use this function to enable or disable Fault Tolerance
.PARAMETER xyz
.NOTES
Author: Niklas Akerlund / RTS
Date: 2012-02-27
#>
param (
[Parameter(Position=0,Mandatory=$true,HelpMessage="A virtual machine",
ValueFromPipeline=$True)]
$VM,
$VMHost = $null,
[switch]$enableFT,
[switch]$disableSecondaryFT,
[switch]$removeFT,
[switch]$failOverFT,
[switch]$restartSecondaryFT
)
if ($VM.ExtensionData.Config.FtInfo -ne $null){
$VM = Get-VM $VM | where {$_.Extensiondata.Config.FtInfo.Role -eq 1}
} else {
$VM = Get-VM $VM
}
if ($VMHost -ne $null){
$VMHost = Get-VMHost $VMHost | Get-View
if ($VMHost -ne $null) {
$VMHostobj = New-Object VMware.Vim.ManagedObjectReference
$VMHostobj.type = "HostSystem"
$VMHostobj.value = $VMHost.MoRef.Value
$VMHost = $VMHostobj
}
}
if ($enableFT) {
if ($VM.ExtensionData.Config.FtInfo -eq $null){
$VMview = $VM | Get-View
$VMview.CreateSecondaryVM($VMHost)
} else{
$VMsec = Get-VM $VM.Name | where {$_.Extensiondata.Config.FtInfo.Role -eq 2}| Get-View
$VMobj = New-Object VMware.Vim.ManagedObjectReference
$VMobj.type = "VirtualMachine"
$VMobj.value = $VMsec.MoRef.Value
$VMview = $VM | Get-View
$VMview.EnableSecondaryVM($VMobj, $null)
}
}elseif ($disableSecondaryFT) {
if ($VM.ExtensionData.Config.FtInfo -ne $null){
$VMsec = Get-VM $VM.Name | where {$_.Extensiondata.Config.FtInfo.Role -eq 2 -and $_.PowerState -eq "PoweredOn"}| Get-View
if ($VMsec -ne $null){
$VMobj = New-Object VMware.Vim.ManagedObjectReference
$VMobj.type = "VirtualMachine"
$VMobj.value = $VMsec.MoRef.Value
$VMview = $VM | Get-View
$VMview.DisableSecondaryVM($VMobj)
}else {
Write-Host "The Secondary is already disabled"
}
}else {
Write-Host "This VM is not FT enabled"
}
}elseif ($failOverFT) {
if ($VM.ExtensionData.Config.FtInfo -ne $null){
$VMsec = Get-VM $VM.Name | where {$_.Extensiondata.Config.FtInfo.Role -eq 2 -and $_.PowerState -eq "PoweredOn"}| Get-View
if ($VMsec -ne $null){
$VMobj = New-Object VMware.Vim.ManagedObjectReference
$VMobj.type = "VirtualMachine"
$VMobj.value = $VMsec.MoRef.Value
$VMview = $VM | Get-View
$VMview.MakePrimaryVM($VMobj)
}else {
Write-Host "The Secondary is disabled"
}
}else {
Write-Host "This VM is not FT enabled"
}
}elseif ($restartSecondaryFT) {
if ($VM.ExtensionData.Config.FtInfo -ne $null){
$VMsec = Get-VM $VM.Name | where {$_.Extensiondata.Config.FtInfo.Role -eq 2 -and $_.PowerState -eq "PoweredOn"}| Get-View
if ($VMsec -ne $null){
$VMobj = New-Object VMware.Vim.ManagedObjectReference
$VMobj.type = "VirtualMachine"
$VMobj.value = $VMsec.MoRef.Value
$VMview = $VM | Get-View
$VMview.TerminateFaultTolerantVM($VMobj)
}else {
Write-Host "The Secondary is disabled"
}
}else {
Write-Host "This VM is not FT enabled"
}
}elseif ($removeFT){
if ($VM.ExtensionData.Config.FtInfo -ne $null){
$VMview = Get-VM $VM | where {$_.Extensiondata.Config.FtInfo.Role -eq 1}| Get-View
$VMview.TurnOffFaultToleranceForVM()
} else {
Write-Host "This VM is not FT enabled"
}
}
}
I got the question from a colleague if i could add a parameter in my function, no problem, just some dividing..
So you can either set percent CPU/Memory or number of hosts and from that get a percent on the policy. I have not taken care of the case if you have more than 100 hosts in your cluster (as this is not possible, at least not yet 😉 )
function Set-HAAdmissionControlPolicy{
<#
.SYNOPSIS
Set the Percentage HA Admission Control Policy
.DESCRIPTION
Percentage of cluster resources reserved as failover spare capacity
.PARAMETER Cluster
The Cluster object that is going to be configurered
.PARAMETER numberHosts
When this parameter is set the percentage is set based on number of hosts in cluster
.PARAMETER percentCPU
The percent reservation of CPU Cluster resources
.PARAMETER percentMem
The percent reservation of Memory Cluster resources
.EXAMPLE
PS C:\> Set-HAAdmissionControlPolicy -Cluster $CL -percentCPU 50 -percentMem 50
.EXAMPLE
PS C:\> Get-Cluster | Set-HAAdmissionControlPolicy -percentCPU 50 -percentMem 50
.EXAMPLE
PS C:\> Set-HAAdmissionControlPolicy -Cluster $CL -numberHosts 5
.NOTES
Author: Niklas Akerlund / RTS
Date: 2012-01-25
#>
param (
[Parameter(Position=0,Mandatory=$true,HelpMessage="This need to be a clusterobject",
ValueFromPipeline=$True)]
$Cluster,
[int]$numberHosts = 0,
[int]$percentCPU = 25,
[int]$percentMem = 25
)
if(Get-Cluster $Cluster){
$spec = New-Object VMware.Vim.ClusterConfigSpecEx
$spec.dasConfig = New-Object VMware.Vim.ClusterDasConfigInfo
$spec.dasConfig.admissionControlPolicy = New-Object VMware.Vim.ClusterFailoverResourcesAdmissionControlPolicy
if($numberHosts -eq 0){
$spec.dasConfig.admissionControlPolicy.cpuFailoverResourcesPercent = $percentCPU
$spec.dasConfig.admissionControlPolicy.memoryFailoverResourcesPercent = $percentMem
}else{
$spec.dasConfig.admissionControlPolicy.cpuFailoverResourcesPercent = 100/$numberHosts
$spec.dasConfig.admissionControlPolicy.memoryFailoverResourcesPercent = 100/$numberHosts
}
$Cluster = Get-View $Cluster
$Cluster.ReconfigureComputeResource_Task($spec, $true)
}
}
I am attending a VMware vSphere ICM 5 course this week, this because I am planning to become a VCI.
As I am a bit of PowerCLI fan i am trying to do all the labs in the course from the powerCLI console 🙂
In the HA laboration i found that the default implementation of the powerCLI does not allow you to set the HA Admission Control Policy Percentage, only the Host failure the cluster tolerates, here is a link to the powerCLI reference page for Set-Cluster. In the slides VMware recommends that you set percentage in a HA setup.
This led me to develop a powerCLI function that can configure this. The function has a default value for CPU and Memory that is 25 %, so if you only give the Cluster parameter the Capacity setting will be 25 %.
function Set-HAAdmissionControlPolicy{
<#
.SYNOPSIS
Set the Percentage HA Admission Control Policy
.DESCRIPTION
Percentage of cluster resources reserved as failover spare capacity
.PARAMETER Cluster
The Cluster object that is going to be configurered
.PARAMETER percentCPU
The percent reservation of CPU Cluster resources
.PARAMETER percentMem
The percent reservation of Memory Cluster resources
.EXAMPLE
PS C:\> Set-HAAdmissionControlPolicy -Cluster $CL -percentCPU 50 -percentMem 50
.EXAMPLE
PS C:\> Get-Cluster | Set-HAAdmissionControlPolicy -percentCPU 50 -percentMem 50
.NOTES
Author: Niklas Akerlund / RTS
Date: 2012-01-19
#>
param (
[Parameter(Position=0,Mandatory=$true,HelpMessage="This need to be a clusterobject",
ValueFromPipeline=$True)]
$Cluster,
[int]$percentCPU = 25,
[int]$percentMem = 25
)
if(Get-Cluster $Cluster){
$spec = New-Object VMware.Vim.ClusterConfigSpecEx
$spec.dasConfig = New-Object VMware.Vim.ClusterDasConfigInfo
$spec.dasConfig.admissionControlPolicy = New-Object VMware.Vim.ClusterFailoverResourcesAdmissionControlPolicy
$spec.dasConfig.admissionControlPolicy.cpuFailoverResourcesPercent = $percentCPU
$spec.dasConfig.admissionControlPolicy.memoryFailoverResourcesPercent = $percentMem
$Cluster = Get-View $Cluster
$Cluster.ReconfigureComputeResource_Task($spec, $true)
}
}
If you are running too many VM´s on your datastores in your vSphere environment you can have some problems, this if your SAN is not VAAI compliant and can handle SCSI locking etc.
Alan Renouf has made a blog post about how to get a report about how many VM´s you have on every datastore, I have extended to only report on running VM´s as these are the interesting number..
I was thinking of how to get an report after reading about the converter best practice from Vladan and he also has this blog post about how to change the HAL, but first i want to know what running guests are there that has the wrong HAL running. I have selected to get an report out of the running windows 2003 ( windows 2008 and later has an uniform HAL that is the same for one or more cpu´s).
I have made an little script somewhat alike to Alan Renouf´s to get the HAL of the VM guest running in your environment, the difference in my approach is that i use invoke-script and get the info from inside the VM, as i have a case where the VMs are isolated and i can not get the wmi info from the network, Arnim Van Lieshout has made one blog post about  running wmi queries this way.
# Check the HAL in the VM with invoke-VMScript
#
# Niklas Akerlund / RTS
$VMs = Get-VM | Where {$_.Guest.OSFullName -eq "Microsoft Windows Server 2003 Standard (32-bit)" -and $_.PowerState -eq "PoweredOn"}
$guestcred = get-Credential
$report = @()
foreach ($VM in $VMs){
$HAL = Invoke-VMScript "wmic path Win32_PnPEntity WHERE ""DeviceID='ROOT\\ACPI_HAL\\0000'"" GET NAME /VALUE" -VM $VM -GuestCredential $guestcred -scripttype "bat"
$HAL = $HAL.Split('=')[1]
$data = New-Object PSObject -property @{
VMName=$VM.Name
vCPUs=$VM.NumCPU
HAL=$HAL.Trim('')
}
$report +=$data
}
$report | Export-Csv -path c:\temp\hal4.csv -NoTypeInformation -UseCulture
Today i added some fields in my little reportscript for the VM and their NICs, the reason was because of a customer that had an issue with duplicate MAC´s on their network.
We had earlier this year moved some VMs from an old vCenter to a new, i have made a blog post about the migration and the script we ran there.
Now when they started to deploy new VMs on the old vCenter it gave out the same MAC addresses as the ones on the VM´s that we had moved.. not so good, there is a fix that can be implemented on the old vCenter so it will start using new MAC´s instead, if you have this issue you can read the following KB 1024025 and set a new ID on the old vCenter and restart the service 🙂
But to check the VMs on both vCenter servers i ran the following script to get the data, the customer wanted both the VM name and the hostname/fqdn from the vm, also for every nic if it was generated or assigned.
Matt Boren that has the site vnugglets.com helped me with an more efficient way of getting the data, my script took about 5-10 minutes and Matt´s took 30 seconds, his key to lower time is using the Get-View for everything (i was only using it to get the vm.guest.hostname)
I have created a simple report-script that gives a list of what kind of format and how many vmdk each VM has. The report tells me if the disks are Thin or Thick and what size they are i GB.
after some magic in Excel it looks like this 🙂
And the powerCLI script looks like this, it is quite simple but still gives me information that i need for all my VMs on all datastores and quickly i can tell which machines that uses a lot of disk on my precious SAN 😉
I was informed that my script in the last post was not enough because @pfuhli has a bit more complex environment and then the Move-VM cmdlet is not sufficient because it moves the whole vm to the new datastore, no matter if the vmdk´s where located on different before.
As in some cases you have an virtual platform with different datastores for different performance levels and one VM has it´s vmdk configured to get the best throughput. So i did this with help from a script that Luc Dekens did in a communities post, mine added some functionality as his only moved the config file.
please comment if you find something crazy, I have now started to get the hang of why I would use functions :-), yes i should add some error-checking, that will be in version 0.3
Probably it would take some time to get through 500 VM´s but instead of manual work it is worth it.
# Move VMs with sVMotion where vmdk is on different datastores and
# lastly move the config file to the same datastore as hard disk 1
#
# Niklas Ã…kerlund / RTS 20111127
# Part of code from Luc Dekens http://communities.vmware.com/message/1680735
# Here i extended Luc´s function for moving only config
function Move-VMs{
param($vm)
Write-Host $vm
$HDDs = Get-HardDisk -VM $vm
# a foreach loop to move vmdk
$HDDs | %{
# Get the datastore name of the old
$oldDS = $_.Filename.Split(']')[0].TrimStart('[')
# as @pfuhli said the new lun has a preceding letter that differs from the old.
$newDS = "N" + $oldDS
# Here i check which is the first hdd to later move the config there
if ($_.Name -eq "Hard disk 1"){
$dsNameHDD1 = $newDS
}
$newDS = Get-Datastore $newDS
Set-HardDisk -HardDisk $_ -Datastore $newDS -Confirm:$false
}
# This part is for moving the config file
$HDDs = Get-HardDisk -VM $vm
$spec = New-Object VMware.Vim.VirtualMachineRelocateSpec
$spec.datastore = (Get-Datastore -Name $dsNameHDD1).Extensiondata.MoRef
$HDDs | %{
$disk = New-Object VMware.Vim.VirtualMachineRelocateSpecDiskLocator
$disk.diskId = $_.Extensiondata.Key
$disk.datastore = $_.Extensiondata.Backing.Datastore
$spec.disk += $disk
}
$vm.Extensiondata.RelocateVM_Task($spec, "defaultPriority")
}
Get-VM | %{
Move-VMs $_
}
Before running it on all VM´s i would test it on a few and then when feeling comfortable, you can move all 😀