Categories
Sponsors
Archive
Blogroll
Badges
Community
|
Posted in Windows Powershell, Windows Server | 2 Comments | 4,979 views | 20/07/2014 23:31
You may get following issues due to WMI problem:
Reboot is only way to fix it but at least you can prevent future issues:
1. Go to Start -> Run and type wbemtest.exe
2. Click Connect
3. In the namespace text box type “root” (without quotes).
4. Click Connect
5. Click Enum Instances
6. In the Class Info dialog box enter Superclass Name as “__ProviderHostQuotaConfiguration” (without quotes) and press OK.
(Note: the Superclass name includes a double underscore at the front.)
7. In the Query Result window, double-click “__ProviderHostQuotaConfiguration=@”
8. In the Object Editor window, double-click HandlesPerHost
9. In the Value dialog, type in 8192
10. Click Save Property
11. Click Save Object
12. Under properties find the property “MemoryPerHost” or any other ones you need to modify and double click it
13. Change the value from 512 MB which is 536870912 to 1GB which is 1073741824
14. Click Save Property
15. Click Save Object
16. Close Wbemtest
17. Restart the computer
Thanks to Shaon Shan for sharing this solution.
Posted in Virtual Machine Manager, Windows Powershell, Windows Server | 1 Comment | 3,351 views | 20/07/2014 09:31
Lets assume that you have a 3 years old storage and you are using it to place your virtual machines on Hyper-V Cluster. Then you bought a new storage box, you created LUNs on it and assign them as a CSV volumes on your existing Hyper-V Cluster. So next achievement would be migrating your virtual machines from old storage to new one.
That migration could be a headache if you have many VMs and many storage LUNs. So in that case, you can use following script to migrate Virtual Machines per CSV Volume. Just you need to specify which CSV volume you would like to drain. So let’s assume that volume is Volume1. I will specify that volume like following:
Start-CSVMigration -TargetVolume "Volume1" |
Start-CSVMigration -TargetVolume "Volume1"
Now you need to specify other CSV Luns as well to filter them. Otherwise, you can migrate a VM to old storage again. So we are filtering them like following:
Start-CSVMigration -TargetVolume "Volume1" -FilteredVolumes "Volume2","Volume3","Volume4" |
Start-CSVMigration -TargetVolume "Volume1" -FilteredVolumes "Volume2","Volume3","Volume4"
So how does it work?
We are creating an array called FilteredVolumes then adding our old storage luns to this array. You will see that I’m also adding $TargetVolume to same array. Because there is a chance to try to migrate a VM to same volume. So that will prevent that kind of issues.
So how can I get destination volume?
$FullQuery = '((Get-ClusterSharedVolume |' + $Query + ' Select -ExpandProperty SharedVolumeInfo | Select @{label="Name";expression={(($_.FriendlyVolumeName).Split("\"))[-1]}},@{label="FreeSpace";expression={($_ | Select -Expand Partition).FreeSpace}} | Sort FreeSpace -Descending)[0]).Name' |
$FullQuery = '((Get-ClusterSharedVolume |' + $Query + ' Select -ExpandProperty SharedVolumeInfo | Select @{label="Name";expression={(($_.FriendlyVolumeName).Split("\"))[-1]}},@{label="FreeSpace";expression={($_ | Select -Expand Partition).FreeSpace}} | Sort FreeSpace -Descending)[0]).Name'
As you can notice from the code below, i’m getting all CSV volumes, filtering old storage volumes, sorting new storage CSVs by their free space and selecting the storage which has biggest available disk space.
So I’ll place VM into that volume:
Move-VMStorage -ComputerName "$ClusterNode" -VMName "$VMName" -DestinationStoragePath "C:\ClusterStorage\$Volume\$VMName" |
Move-VMStorage -ComputerName "$ClusterNode" -VMName "$VMName" -DestinationStoragePath "C:\ClusterStorage\$Volume\$VMName"
You can also allow VMs with passthrough disks and vHBA. You just need to add -AllowPT switch.
Start-CSVMigration -TargetVolume "Volume1" -FilteredVolumes "Volume2","Volume3","Volume4" -AllowPT |
Start-CSVMigration -TargetVolume "Volume1" -FilteredVolumes "Volume2","Volume3","Volume4" -AllowPT
This is what I use to filter VMs with vHBA:
Where {($_ | Select -expand FibreChannelHostBusAdapters) -eq $Null} |
Where {($_ | Select -expand FibreChannelHostBusAdapters) -eq $Null}
If you get the idea, now I can share full script:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
| function Start-CSVMigration {
<#
.SYNOPSIS
Function to migrate all virtual machines placed on a CSV volume by using Hyper-V Live Storage Migration.
.DESCRIPTION
Lets assume that you have a 3 years old storage and you are using it to place your virtual machines on Hyper-V Cluster.
Then you bought a new storage box, you created LUNs on it and assign them as a CSV volumes on your existing Hyper-V Cluster.
So next achievement would be migrating your virtual machines from old storage to new one.
This script moves virtual machines into different CSV volumes by using Hyper-V Live Storage Migration.
.PARAMETER WhatIf
Display what would happen if you would run the function with given parameters.
.PARAMETER Confirm
Prompts for confirmation for each operation. Allow user to specify Yes/No to all option to stop prompting.
.EXAMPLE
Start-CSVMigration -TargetVolume "Volume1"
.EXAMPLE
Start-CSVMigration -TargetVolume "Volume1" -FilteredVolumes "Volume2","Volume3","Volume4"
.EXAMPLE
Start-CSVMigration -TargetVolume "Volume1" -FilteredVolumes "Volume2","Volume3","Volume4" -AllowPT
.INPUTS
None
.OUTPUTS
None
.NOTES
Author: Yusuf Ozturk
Website: http://www.yusufozturk.info
Email: ysfozy@gmail.com
Date created: 05-July-2014
Last modified: 20-July-2014
Version: 1.4
.LINK
http://www.yusufozturk.info
http://twitter.com/yusufozturk
#>
[CmdletBinding(SupportsShouldProcess = $true)]
param (
# Target Volume
[Parameter(
Mandatory = $true,
HelpMessage = 'The CSV Volume name that you want to drain, like: Volume1')]
$TargetVolume,
# Filtered Volumes
[Parameter(
Mandatory = $false,
HelpMessage = 'The CSV Volume names that you want to filter, like: Volume2')]
[array]$FilteredVolumes,
# Allow Passthrough Disks
[Parameter(
Mandatory = $false,
HelpMessage = 'Allow Passthrough Disks')]
[switch]$AllowPT = $false,
# Debug Mode
[Parameter(
Mandatory = $false,
HelpMessage = 'Debug Mode')]
[switch]$DebugMode = $false
)
# Enable Debug Mode
if ($DebugMode)
{
$DebugPreference = "Continue"
}
else
{
$ErrorActionPreference = "silentlycontinue"
}
# Filtered Volumes
$FilteredVolumes += $TargetVolume
# Clear Query
$Query = $Null;
# Create Query
foreach ($FilteredVolume in $FilteredVolumes)
{
$Query = $Query + ' Where {$_.SharedVolumeInfo.FriendlyVolumeName -notlike "*' + $FilteredVolume + '"} |'
}
# Full Query
$FullQuery = '((Get-ClusterSharedVolume |' + $Query + ' Select -ExpandProperty SharedVolumeInfo | Select @{label="Name";expression={(($_.FriendlyVolumeName).Split("\"))[-1]}},@{label="FreeSpace";expression={($_ | Select -Expand Partition).FreeSpace}} | Sort FreeSpace -Descending)[0]).Name'
# Get Cluster Nodes
$ClusterNodes = Get-Cluster | Get-ClusterNode
foreach ($ClusterNode in $ClusterNodes)
{
# Clear Variables
$VMs = $Null;
# Create VM Array
$VMArray = @()
if ($AllowPT)
{
# Get All Virtual Machines on Target Volume
$VMs = Get-VM -ComputerName "$ClusterNode" | Where ConfigurationLocation -like "C:\ClusterStorage\$TargetVolume\*"
foreach ($VM in $VMs)
{
# Get VM Information
$VMName = $VM.Name
# Add VM to VMArray
$VMArray += $VMName
}
}
else
{
# Get All Virtual Machines on Target Volume
$VMs = Get-VM -ComputerName "$ClusterNode" | Where ConfigurationLocation -like "C:\ClusterStorage\$TargetVolume\*" | Where {($_ | Select -expand FibreChannelHostBusAdapters) -eq $Null}
foreach ($VM in $VMs)
{
# Get VM Information
$VMName = $VM.Name
if ($VM.HardDrives.Path -like "Disk*")
{
# Skipping VM
}
else
{
# Add VM to VMArray
$VMArray += $VMName
}
}
}
foreach ($VMName in $VMArray)
{
Write-Host " "
Write-Host "Working on $VMName .."
Write-Host "Hyper-V Host: $ClusterNode"
# Get Volume Information
$Volume = Invoke-Expression $FullQuery
# Move Virtual Machine
Move-VMStorage -ComputerName "$ClusterNode" -VMName "$VMName" -DestinationStoragePath "C:\ClusterStorage\$Volume\$VMName"
Write-Host "Done."
}
}
} |
function Start-CSVMigration {
<#
.SYNOPSIS
Function to migrate all virtual machines placed on a CSV volume by using Hyper-V Live Storage Migration.
.DESCRIPTION
Lets assume that you have a 3 years old storage and you are using it to place your virtual machines on Hyper-V Cluster.
Then you bought a new storage box, you created LUNs on it and assign them as a CSV volumes on your existing Hyper-V Cluster.
So next achievement would be migrating your virtual machines from old storage to new one.
This script moves virtual machines into different CSV volumes by using Hyper-V Live Storage Migration.
.PARAMETER WhatIf
Display what would happen if you would run the function with given parameters.
.PARAMETER Confirm
Prompts for confirmation for each operation. Allow user to specify Yes/No to all option to stop prompting.
.EXAMPLE
Start-CSVMigration -TargetVolume "Volume1"
.EXAMPLE
Start-CSVMigration -TargetVolume "Volume1" -FilteredVolumes "Volume2","Volume3","Volume4"
.EXAMPLE
Start-CSVMigration -TargetVolume "Volume1" -FilteredVolumes "Volume2","Volume3","Volume4" -AllowPT
.INPUTS
None
.OUTPUTS
None
.NOTES
Author: Yusuf Ozturk
Website: http://www.yusufozturk.info
Email: ysfozy@gmail.com
Date created: 05-July-2014
Last modified: 20-July-2014
Version: 1.4
.LINK
http://www.yusufozturk.info
http://twitter.com/yusufozturk
#>
[CmdletBinding(SupportsShouldProcess = $true)]
param (
# Target Volume
[Parameter(
Mandatory = $true,
HelpMessage = 'The CSV Volume name that you want to drain, like: Volume1')]
$TargetVolume,
# Filtered Volumes
[Parameter(
Mandatory = $false,
HelpMessage = 'The CSV Volume names that you want to filter, like: Volume2')]
[array]$FilteredVolumes,
# Allow Passthrough Disks
[Parameter(
Mandatory = $false,
HelpMessage = 'Allow Passthrough Disks')]
[switch]$AllowPT = $false,
# Debug Mode
[Parameter(
Mandatory = $false,
HelpMessage = 'Debug Mode')]
[switch]$DebugMode = $false
)
# Enable Debug Mode
if ($DebugMode)
{
$DebugPreference = "Continue"
}
else
{
$ErrorActionPreference = "silentlycontinue"
}
# Filtered Volumes
$FilteredVolumes += $TargetVolume
# Clear Query
$Query = $Null;
# Create Query
foreach ($FilteredVolume in $FilteredVolumes)
{
$Query = $Query + ' Where {$_.SharedVolumeInfo.FriendlyVolumeName -notlike "*' + $FilteredVolume + '"} |'
}
# Full Query
$FullQuery = '((Get-ClusterSharedVolume |' + $Query + ' Select -ExpandProperty SharedVolumeInfo | Select @{label="Name";expression={(($_.FriendlyVolumeName).Split("\"))[-1]}},@{label="FreeSpace";expression={($_ | Select -Expand Partition).FreeSpace}} | Sort FreeSpace -Descending)[0]).Name'
# Get Cluster Nodes
$ClusterNodes = Get-Cluster | Get-ClusterNode
foreach ($ClusterNode in $ClusterNodes)
{
# Clear Variables
$VMs = $Null;
# Create VM Array
$VMArray = @()
if ($AllowPT)
{
# Get All Virtual Machines on Target Volume
$VMs = Get-VM -ComputerName "$ClusterNode" | Where ConfigurationLocation -like "C:\ClusterStorage\$TargetVolume\*"
foreach ($VM in $VMs)
{
# Get VM Information
$VMName = $VM.Name
# Add VM to VMArray
$VMArray += $VMName
}
}
else
{
# Get All Virtual Machines on Target Volume
$VMs = Get-VM -ComputerName "$ClusterNode" | Where ConfigurationLocation -like "C:\ClusterStorage\$TargetVolume\*" | Where {($_ | Select -expand FibreChannelHostBusAdapters) -eq $Null}
foreach ($VM in $VMs)
{
# Get VM Information
$VMName = $VM.Name
if ($VM.HardDrives.Path -like "Disk*")
{
# Skipping VM
}
else
{
# Add VM to VMArray
$VMArray += $VMName
}
}
}
foreach ($VMName in $VMArray)
{
Write-Host " "
Write-Host "Working on $VMName .."
Write-Host "Hyper-V Host: $ClusterNode"
# Get Volume Information
$Volume = Invoke-Expression $FullQuery
# Move Virtual Machine
Move-VMStorage -ComputerName "$ClusterNode" -VMName "$VMName" -DestinationStoragePath "C:\ClusterStorage\$Volume\$VMName"
Write-Host "Done."
}
}
}
It’s enough to run this script on one of the Cluster Node, so that will start migrating virtual machines. That will also give an output, so you will be able to see which VM you are migrating.
This is also an example how you can automate storage migrations:
1
2
3
4
5
6
7
8
9
10
11
| $Volumes = @()
$Volumes += "Volume1"
$Volumes += "Volume2"
$Volumes += "Volume3"
$Volumes += "Volume4"
$Volumes += "Volume5"
foreach ($Volume in $Volumes)
{
Start-CSVMigration -TargetVolume "$Volume" -FilteredVolumes "Volume1","Volume2","Volume3","Volume4","Volume5","Volume6","Volume7","Volume8","Volume9","Volume10","Volume11","Volume12","Volume13","Volume14","Volume15"
} |
$Volumes = @()
$Volumes += "Volume1"
$Volumes += "Volume2"
$Volumes += "Volume3"
$Volumes += "Volume4"
$Volumes += "Volume5"
foreach ($Volume in $Volumes)
{
Start-CSVMigration -TargetVolume "$Volume" -FilteredVolumes "Volume1","Volume2","Volume3","Volume4","Volume5","Volume6","Volume7","Volume8","Volume9","Volume10","Volume11","Volume12","Volume13","Volume14","Volume15"
}
I hope you will find it useful. See you!
Posted in Windows Powershell, Windows Server | 1 Comment | 4,962 views | 20/07/2014 09:06
This function works like VMware Storage DRS to do Storage Optimization on CSV volumes.
It moves virtual machines into different CSVs to optimize storage usage.
Updated version for Windows Server 2016 by Bas Roovers:
https://gist.github.com/basroovers/2c1cdf9298c2a26ad633e008d95abf46
So how does it work?
First we are getting best available CSV with following line:
$BestVolume = ((Get-ClusterSharedVolume | Select -ExpandProperty SharedVolumeInfo | Select @{label="Name";expression={(($_.FriendlyVolumeName).Split("\"))[-1]}},@{label="FreeSpace";expression={($_ | Select -Expand Partition).FreeSpace}} | Sort FreeSpace -Descending)[0]) |
$BestVolume = ((Get-ClusterSharedVolume | Select -ExpandProperty SharedVolumeInfo | Select @{label="Name";expression={(($_.FriendlyVolumeName).Split("\"))[-1]}},@{label="FreeSpace";expression={($_ | Select -Expand Partition).FreeSpace}} | Sort FreeSpace -Descending)[0])
Then we are getting worst available CSV with following line:
$WorstVolume = ((Get-ClusterSharedVolume | Select -ExpandProperty SharedVolumeInfo | Select @{label="Name";expression={(($_.FriendlyVolumeName).Split("\"))[-1]}},@{label="FreeSpace";expression={($_ | Select -Expand Partition).FreeSpace}} | Sort FreeSpace -Descending)[-1]) |
$WorstVolume = ((Get-ClusterSharedVolume | Select -ExpandProperty SharedVolumeInfo | Select @{label="Name";expression={(($_.FriendlyVolumeName).Split("\"))[-1]}},@{label="FreeSpace";expression={($_ | Select -Expand Partition).FreeSpace}} | Sort FreeSpace -Descending)[-1])
After that script calculates space gap between those two volumes:
[int64]$SpaceGap = [math]::round((([int64]$BestVolumeFreeSpace - [int64]$WorstVolumeFreeSpace) / 1GB), 0) |
[int64]$SpaceGap = [math]::round((([int64]$BestVolumeFreeSpace - [int64]$WorstVolumeFreeSpace) / 1GB), 0)
If space gap is bigger than 100 GB, so so now we know that, we should migrate virtual machines from $WorstVolume to $BestVolume. If space gap is less than 100 GB, we can assume that volumes are already optimized.
Then we are calculating virtual machines disk space usages on the $WorstVolume:
$VMReports += Get-VM -ComputerName $ClusterNode | Where ConfigurationLocation -like "C:\ClusterStorage\$WorstVolumeName\*" | Measure-VM |
$VMReports += Get-VM -ComputerName $ClusterNode | Where ConfigurationLocation -like "C:\ClusterStorage\$WorstVolumeName\*" | Measure-VM
So we should migrate a VM which has less disk space then average space gap between $BestVolume and $WorstVolume. Otherwise, a VM with a large disk space can fill all available space in $BestVolume.
$BestVM = ($VMReports | Where TotalDisk -lt $SpaceGapAverageMb | Sort TotalDisk -Descending)[0] |
$BestVM = ($VMReports | Where TotalDisk -lt $SpaceGapAverageMb | Sort TotalDisk -Descending)[0]
Finally we are migrating VM with following line:
Move-VMStorage -ComputerName "$BestVMHost" -VMName "$BestVMName" -DestinationStoragePath "C:\ClusterStorage\$BestVolumeName\$BestVMName" |
Move-VMStorage -ComputerName "$BestVMHost" -VMName "$BestVMName" -DestinationStoragePath "C:\ClusterStorage\$BestVolumeName\$BestVMName"
You can also allow VMs with passthrough disks and vHBA. You just need to add -AllowPT switch.
Start-VMStorageOptimization -AllowPT |
Start-VMStorageOptimization -AllowPT
This is what I use to filter VMs with passthrough disks and vHBA:
Where {(($_ | Select -Expand HardDrives).Path -notlike "Disk*") -and (($_ | Select -expand FibreChannelHostBusAdapters) -eq $Null)} |
Where {(($_ | Select -Expand HardDrives).Path -notlike "Disk*") -and (($_ | Select -expand FibreChannelHostBusAdapters) -eq $Null)}
If you get the idea, now I can share full script:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
| function Start-VMStorageOptimization {
<#
.SYNOPSIS
Function to optimize storage usage on CSV volumes by using Hyper-V Live Storage Migration.
.DESCRIPTION
If you use dynamic virtual disks with VMs, you may need to monitor free space on your CSV volumes.
This script moves virtual machines into different CSV volumes by using Hyper-V Live Storage Migration.
.PARAMETER WhatIf
Display what would happen if you would run the function with given parameters.
.PARAMETER Confirm
Prompts for confirmation for each operation. Allow user to specify Yes/No to all option to stop prompting.
.EXAMPLE
Start-VMStorageOptimization
.EXAMPLE
Start-VMStorageOptimization -AllowPT
.INPUTS
None
.OUTPUTS
None
.NOTES
Author: Yusuf Ozturk
Website: http://www.yusufozturk.info
Email: ysfozy@gmail.com
Date created: 08-June-2014
Last modified: 20-July-2014
Version: 1.4
.LINK
http://www.yusufozturk.info
http://twitter.com/yusufozturk
#>
[CmdletBinding(SupportsShouldProcess = $true)]
param (
# Allow Passthrough Disks
[Parameter(
Mandatory = $false,
HelpMessage = 'Allow Passthrough Disks')]
[switch]$AllowPT = $false,
# Debug Mode
[Parameter(
Mandatory = $false,
HelpMessage = 'Debug Mode')]
[switch]$DebugMode = $false
)
# Enable Debug Mode
if ($DebugMode)
{
$DebugPreference = "Continue"
}
else
{
$ErrorActionPreference = "silentlycontinue"
}
# Informational Output
Write-Host " "
Write-Host "------------------------------------------" -ForegroundColor Green
Write-Host " Hyper-V Storage Optimization Script v1.4 " -ForegroundColor Green
Write-Host "------------------------------------------" -ForegroundColor Green
Write-Host " "
# Set Storage Status
$StorageStatus = $True
while ($StorageStatus -eq $True)
{
# Get Worst CSV Volume
$WorstVolume = ((Get-ClusterSharedVolume | Select -ExpandProperty SharedVolumeInfo | Select @{label="Name";expression={(($_.FriendlyVolumeName).Split("\"))[-1]}},@{label="FreeSpace";expression={($_ | Select -Expand Partition).FreeSpace}} | Sort FreeSpace -Descending)[-1])
# Worst CSV Volume Information
$WorstVolumeName = $WorstVolume.Name
$WorstVolumeFreeSpace = $WorstVolume.FreeSpace
# Get Best CSV Volume
$BestVolume = ((Get-ClusterSharedVolume | Select -ExpandProperty SharedVolumeInfo | Select @{label="Name";expression={(($_.FriendlyVolumeName).Split("\"))[-1]}},@{label="FreeSpace";expression={($_ | Select -Expand Partition).FreeSpace}} | Sort FreeSpace -Descending)[0])
# Best CSV Volume Information
$BestVolumeName = $BestVolume.Name
$BestVolumeFreeSpace = $BestVolume.FreeSpace
# Calculate Space Gap
[int64]$SpaceGap = [math]::round((([int64]$BestVolumeFreeSpace - [int64]$WorstVolumeFreeSpace) / 1GB), 0)
if ($SpaceGap -lt "100")
{
# Informational Output
Write-Host "Storage is already optimized." -ForegroundColor Green
Write-Host " "
# Set Storage Status
$StorageStatus = $False
}
else
{
# VM Reports
$VMReports = $Null;
# Get Cluster Nodes
$ClusterNodes = Get-Cluster | Get-ClusterNode
# Informational Output
Write-Host "Measuring VMs to find the most suitable virtual machine for the migration.. Please wait.." -ForegroundColor Cyan
Write-Host " "
foreach ($ClusterNode in $ClusterNodes)
{
if ($AllowPT)
{
# Enable Resource Metering
$EnableMetering = Get-VM -ComputerName $ClusterNode | Where ConfigurationLocation -like "C:\ClusterStorage\$WorstVolumeName\*" | Enable-VMResourceMetering
# Get Resource Usage
$VMReports += Get-VM -ComputerName $ClusterNode | Where ConfigurationLocation -like "C:\ClusterStorage\$WorstVolumeName\*" | Measure-VM
}
else
{
# Enable Resource Metering
$EnableMetering = Get-VM -ComputerName $ClusterNode | Where ConfigurationLocation -like "C:\ClusterStorage\$WorstVolumeName\*" | Where {(($_ | Select -Expand HardDrives).Path -notlike "Disk*") -and (($_ | Select -expand FibreChannelHostBusAdapters) -eq $Null)} | Enable-VMResourceMetering
# Get Resource Usage
$VMReports += Get-VM -ComputerName $ClusterNode | Where ConfigurationLocation -like "C:\ClusterStorage\$WorstVolumeName\*" | Where {(($_ | Select -Expand HardDrives).Path -notlike "Disk*") -and (($_ | Select -expand FibreChannelHostBusAdapters) -eq $Null)} | Measure-VM
}
}
# Calculate Space Gap Average
[int64]$SpaceGapAverage = [math]::round(([int64]$SpaceGap / 2), 0)
[int64]$SpaceGapAverageMb = [int64]$SpaceGapAverage * 1024
# Find Most Suitable VM
$BestVM = ($VMReports | Where TotalDisk -lt $SpaceGapAverageMb | Sort TotalDisk -Descending)[0]
if ($BestVM)
{
# Best VM Information
$BestVMName = $BestVM.VMName
$BestVMHost = $BestVM.ComputerName
# Informational Output
Write-Host "Moving $BestVMName from $WorstVolumeName to $BestVolumeName by using Hyper-V Storage Live Migration.." -ForegroundColor Yellow
# Move Virtual Machine
Move-VMStorage -ComputerName "$BestVMHost" -VMName "$BestVMName" -DestinationStoragePath "C:\ClusterStorage\$BestVolumeName\$BestVMName"
# Informational Output
Write-Host "Done." -ForegroundColor Green
Write-Host " "
}
else
{
# Informational Output
Write-Host "No suitable VM is found.. $WorstVolumeName contains VMs with large spaces." -ForegroundColor Red
Write-Host " "
# Set Storage Status
$StorageStatus = $False
}
}
}
} |
function Start-VMStorageOptimization {
<#
.SYNOPSIS
Function to optimize storage usage on CSV volumes by using Hyper-V Live Storage Migration.
.DESCRIPTION
If you use dynamic virtual disks with VMs, you may need to monitor free space on your CSV volumes.
This script moves virtual machines into different CSV volumes by using Hyper-V Live Storage Migration.
.PARAMETER WhatIf
Display what would happen if you would run the function with given parameters.
.PARAMETER Confirm
Prompts for confirmation for each operation. Allow user to specify Yes/No to all option to stop prompting.
.EXAMPLE
Start-VMStorageOptimization
.EXAMPLE
Start-VMStorageOptimization -AllowPT
.INPUTS
None
.OUTPUTS
None
.NOTES
Author: Yusuf Ozturk
Website: http://www.yusufozturk.info
Email: ysfozy@gmail.com
Date created: 08-June-2014
Last modified: 20-July-2014
Version: 1.4
.LINK
http://www.yusufozturk.info
http://twitter.com/yusufozturk
#>
[CmdletBinding(SupportsShouldProcess = $true)]
param (
# Allow Passthrough Disks
[Parameter(
Mandatory = $false,
HelpMessage = 'Allow Passthrough Disks')]
[switch]$AllowPT = $false,
# Debug Mode
[Parameter(
Mandatory = $false,
HelpMessage = 'Debug Mode')]
[switch]$DebugMode = $false
)
# Enable Debug Mode
if ($DebugMode)
{
$DebugPreference = "Continue"
}
else
{
$ErrorActionPreference = "silentlycontinue"
}
# Informational Output
Write-Host " "
Write-Host "------------------------------------------" -ForegroundColor Green
Write-Host " Hyper-V Storage Optimization Script v1.4 " -ForegroundColor Green
Write-Host "------------------------------------------" -ForegroundColor Green
Write-Host " "
# Set Storage Status
$StorageStatus = $True
while ($StorageStatus -eq $True)
{
# Get Worst CSV Volume
$WorstVolume = ((Get-ClusterSharedVolume | Select -ExpandProperty SharedVolumeInfo | Select @{label="Name";expression={(($_.FriendlyVolumeName).Split("\"))[-1]}},@{label="FreeSpace";expression={($_ | Select -Expand Partition).FreeSpace}} | Sort FreeSpace -Descending)[-1])
# Worst CSV Volume Information
$WorstVolumeName = $WorstVolume.Name
$WorstVolumeFreeSpace = $WorstVolume.FreeSpace
# Get Best CSV Volume
$BestVolume = ((Get-ClusterSharedVolume | Select -ExpandProperty SharedVolumeInfo | Select @{label="Name";expression={(($_.FriendlyVolumeName).Split("\"))[-1]}},@{label="FreeSpace";expression={($_ | Select -Expand Partition).FreeSpace}} | Sort FreeSpace -Descending)[0])
# Best CSV Volume Information
$BestVolumeName = $BestVolume.Name
$BestVolumeFreeSpace = $BestVolume.FreeSpace
# Calculate Space Gap
[int64]$SpaceGap = [math]::round((([int64]$BestVolumeFreeSpace - [int64]$WorstVolumeFreeSpace) / 1GB), 0)
if ($SpaceGap -lt "100")
{
# Informational Output
Write-Host "Storage is already optimized." -ForegroundColor Green
Write-Host " "
# Set Storage Status
$StorageStatus = $False
}
else
{
# VM Reports
$VMReports = $Null;
# Get Cluster Nodes
$ClusterNodes = Get-Cluster | Get-ClusterNode
# Informational Output
Write-Host "Measuring VMs to find the most suitable virtual machine for the migration.. Please wait.." -ForegroundColor Cyan
Write-Host " "
foreach ($ClusterNode in $ClusterNodes)
{
if ($AllowPT)
{
# Enable Resource Metering
$EnableMetering = Get-VM -ComputerName $ClusterNode | Where ConfigurationLocation -like "C:\ClusterStorage\$WorstVolumeName\*" | Enable-VMResourceMetering
# Get Resource Usage
$VMReports += Get-VM -ComputerName $ClusterNode | Where ConfigurationLocation -like "C:\ClusterStorage\$WorstVolumeName\*" | Measure-VM
}
else
{
# Enable Resource Metering
$EnableMetering = Get-VM -ComputerName $ClusterNode | Where ConfigurationLocation -like "C:\ClusterStorage\$WorstVolumeName\*" | Where {(($_ | Select -Expand HardDrives).Path -notlike "Disk*") -and (($_ | Select -expand FibreChannelHostBusAdapters) -eq $Null)} | Enable-VMResourceMetering
# Get Resource Usage
$VMReports += Get-VM -ComputerName $ClusterNode | Where ConfigurationLocation -like "C:\ClusterStorage\$WorstVolumeName\*" | Where {(($_ | Select -Expand HardDrives).Path -notlike "Disk*") -and (($_ | Select -expand FibreChannelHostBusAdapters) -eq $Null)} | Measure-VM
}
}
# Calculate Space Gap Average
[int64]$SpaceGapAverage = [math]::round(([int64]$SpaceGap / 2), 0)
[int64]$SpaceGapAverageMb = [int64]$SpaceGapAverage * 1024
# Find Most Suitable VM
$BestVM = ($VMReports | Where TotalDisk -lt $SpaceGapAverageMb | Sort TotalDisk -Descending)[0]
if ($BestVM)
{
# Best VM Information
$BestVMName = $BestVM.VMName
$BestVMHost = $BestVM.ComputerName
# Informational Output
Write-Host "Moving $BestVMName from $WorstVolumeName to $BestVolumeName by using Hyper-V Storage Live Migration.." -ForegroundColor Yellow
# Move Virtual Machine
Move-VMStorage -ComputerName "$BestVMHost" -VMName "$BestVMName" -DestinationStoragePath "C:\ClusterStorage\$BestVolumeName\$BestVMName"
# Informational Output
Write-Host "Done." -ForegroundColor Green
Write-Host " "
}
else
{
# Informational Output
Write-Host "No suitable VM is found.. $WorstVolumeName contains VMs with large spaces." -ForegroundColor Red
Write-Host " "
# Set Storage Status
$StorageStatus = $False
}
}
}
}
Every time you run this script, it moves virtual machines unless space gap between CSVs is minimum.
Posted in Windows Powershell, Windows Server | 1 Comment | 6,449 views | 19/07/2014 22:43
You can search VMs with specific VLAN id with following script:
1
2
3
4
5
| $ClusterNodes = Get-Cluster | Get-ClusterNode
foreach ($ClusterNode in $ClusterNodes)
{
Get-VM -ComputerName $ClusterNode | Where {$_.NetworkAdapters.VlanSetting.AccessVlanId -eq "173"}
} |
$ClusterNodes = Get-Cluster | Get-ClusterNode
foreach ($ClusterNode in $ClusterNodes)
{
Get-VM -ComputerName $ClusterNode | Where {$_.NetworkAdapters.VlanSetting.AccessVlanId -eq "173"}
}
That will give you VM list which has VLAN 173.
Posted in Windows Powershell, Windows Server | No Comment | 2,782 views | 19/07/2014 08:40
I made a module for HP OpenView on PowerShell. You can download from here:
HP OpenView PowerShell Module v1.4
Exported Functions:
Get-HPOVManagedNode
Set-HPOVManagedNode
Get-HPOVPDTList
Start-HPOVManagedNodePDT
Stop-HPOVManagedNodePDT |
Get-HPOVManagedNode
Set-HPOVManagedNode
Get-HPOVPDTList
Start-HPOVManagedNodePDT
Stop-HPOVManagedNodePDT
How to import Module?
First extract module directory into one of PS module directory path. You can see paths with $env:PSModulePath
Examples:
Getting managed node properties from HP OpenView:
Get-HPOVManagedNode -Name "node.domain.com" |
Get-HPOVManagedNode -Name "node.domain.com"
Setting managed node properties from HP OpenView:
Set-HPOVManagedNode -Name "node.domain.com" -Owner "Admin" -CommunicationPath "192.168.0.1" |
Set-HPOVManagedNode -Name "node.domain.com" -Owner "Admin" -CommunicationPath "192.168.0.1"
Getting managed nodes in PDT:
Starting PDT for managed node:
Start-HPOVManagedNodePDT -Name "node.domain.com" |
Start-HPOVManagedNodePDT -Name "node.domain.com"
Stopping PDT for managed node:
Stop-HPOVManagedNodePDT -Name "node.domain.com" |
Stop-HPOVManagedNodePDT -Name "node.domain.com"
Thats all for now. I may add additional functions on this module.
Posted in Windows Powershell, Windows Server | No Comment | 3,706 views | 10/07/2014 15:43
You may need to see passthrough disk information from Hyper-V VMs on several clusters.
So you can use following script:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| # Get Clusters
$Clusters = Get-Content Clusters.txt
foreach ($Cluster in $Clusters)
{
# Get Cluster Nodes
$ClusterNodes = Get-Cluster $Cluster | Get-ClusterNode
foreach ($ClusterNode in $ClusterNodes)
{
# Clear PT Values
$PTDisks = $Null;
# Get Passthrough Disks
$PTDisks = Get-VM -ComputerName $ClusterNode | Get-VMHardDiskDrive | Where Path -like Disk*
if ($PTDisks)
{
# Get VM Name
$VMName = $PTDisks[0].VMName
# Informational Output
Write-Host Working on $VMName..
foreach ($PTDisk in $PTDisks)
{
# Get Passthrough Disk Info
$PTInfo = $PTDisk.Path
# Informational Output
Write-Host $PTInfo
}
# Informational Output
Write-Host " "
Write-Host " "
}
}
} |
# Get Clusters
$Clusters = Get-Content Clusters.txt
foreach ($Cluster in $Clusters)
{
# Get Cluster Nodes
$ClusterNodes = Get-Cluster $Cluster | Get-ClusterNode
foreach ($ClusterNode in $ClusterNodes)
{
# Clear PT Values
$PTDisks = $Null;
# Get Passthrough Disks
$PTDisks = Get-VM -ComputerName $ClusterNode | Get-VMHardDiskDrive | Where Path -like Disk*
if ($PTDisks)
{
# Get VM Name
$VMName = $PTDisks[0].VMName
# Informational Output
Write-Host Working on $VMName..
foreach ($PTDisk in $PTDisks)
{
# Get Passthrough Disk Info
$PTInfo = $PTDisk.Path
# Informational Output
Write-Host $PTInfo
}
# Informational Output
Write-Host " "
Write-Host " "
}
}
}
That will give you disk information output. You should add all clusters names into Cluster.txt file.
Posted in Windows Powershell, Windows Server | No Comment | 2,750 views | 04/07/2014 18:00
Lets assume that you have a 3 years old storage and you are using it to place your virtual machines on Hyper-V Cluster. Then you bought a new storage box, you created LUNs on it and assign them as a CSV volumes on your existing Hyper-V Cluster. So next achievement would be migrating your virtual machines from old storage to new one.
That migration could be a headache if you have many VMs and many storage LUNs. So in that case, you can use following script to migrate Virtual Machines per CSV Volume. Just you need to specify which CSV volume you would like to drain. So let’s assume that volume is Volume1. I will specify that volume by following line:
1
2
| # Target Volume
$TargetVolume = "Volume1" |
# Target Volume
$TargetVolume = "Volume1"
Now you need to specify other CSV Luns as well to filter them. Otherwise, you can migrate a VM to old storage again. So we are filtering them by following lines:
1
2
3
4
5
6
7
8
| # Filtered Volumes
$FilteredVolumes = @()
$FilteredVolumes += $TargetVolume
$FilteredVolumes += "Volume2"
$FilteredVolumes += "Volume3"
$FilteredVolumes += "Volume4"
$FilteredVolumes += "Volume5"
$FilteredVolumes += "Volume6" |
# Filtered Volumes
$FilteredVolumes = @()
$FilteredVolumes += $TargetVolume
$FilteredVolumes += "Volume2"
$FilteredVolumes += "Volume3"
$FilteredVolumes += "Volume4"
$FilteredVolumes += "Volume5"
$FilteredVolumes += "Volume6"
We are creating an array called FilteredVolumes then adding our old storage luns to this array. You will see that I’m also adding $TargetVolume to same array. Because there is a chance to try to migrate a VM to same volume. So that will prevent that kind of issues.
So how can I get destination volume?
1
2
| # Full Query
$FullQuery = '((Get-ClusterSharedVolume |' + $Query + ' Select -ExpandProperty SharedVolumeInfo | Select @{label="Name";expression={(($_.FriendlyVolumeName).Split("\"))[-1]}},@{label="FreeSpace";expression={($_ | Select -Expand Partition).FreeSpace}} | Sort FreeSpace -Descending)[0]).Name' |
# Full Query
$FullQuery = '((Get-ClusterSharedVolume |' + $Query + ' Select -ExpandProperty SharedVolumeInfo | Select @{label="Name";expression={(($_.FriendlyVolumeName).Split("\"))[-1]}},@{label="FreeSpace";expression={($_ | Select -Expand Partition).FreeSpace}} | Sort FreeSpace -Descending)[0]).Name'
As you can notice from the code below, i’m getting all CSV volumes, filtering old storage volumes, sorting new storage CSVs by their free space and selecting the storage which has biggest available disk space.
So I’ll place VM into that volume:
Move-VMStorage -ComputerName "$ClusterNode" -VMName "$VMName" -DestinationStoragePath "C:\ClusterStorage\$Volume\$VMName" |
Move-VMStorage -ComputerName "$ClusterNode" -VMName "$VMName" -DestinationStoragePath "C:\ClusterStorage\$Volume\$VMName"
If you get the idea, now I can share full script:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
| # Target Volume
$TargetVolume = "Volume1"
# Filtered Volumes
$FilteredVolumes = @()
$FilteredVolumes += $TargetVolume
$FilteredVolumes += "Volume2"
$FilteredVolumes += "Volume3"
$FilteredVolumes += "Volume4"
$FilteredVolumes += "Volume5"
$FilteredVolumes += "Volume6"
# Clear Query
$Query = $Null;
# Create Query
foreach ($FilteredVolume in $FilteredVolumes)
{
$Query = $Query + ' Where {$_.SharedVolumeInfo.FriendlyVolumeName -notlike "*' + $FilteredVolume + '"} |'
}
# Full Query
$FullQuery = '((Get-ClusterSharedVolume |' + $Query + ' Select -ExpandProperty SharedVolumeInfo | Select @{label="Name";expression={(($_.FriendlyVolumeName).Split("\"))[-1]}},@{label="FreeSpace";expression={($_ | Select -Expand Partition).FreeSpace}} | Sort FreeSpace -Descending)[0]).Name'
# Get Cluster Nodes
$ClusterNodes = Get-Cluster | Get-ClusterNode
foreach ($ClusterNode in $ClusterNodes)
{
# Clear Variables
$VMs = $Null;
# Get All Virtual Machines on Target Volume
$VMs = Get-VM -ComputerName "$ClusterNode" | Where ConfigurationLocation -like "C:\ClusterStorage\$TargetVolume\*"
foreach ($VM in $VMs)
{
# Get VM Information
$VMName = $VM.Name
Write-Host " "
Write-Host "Working on $VMName .."
Write-Host "Hyper-V Host: $ClusterNode"
# Get Volume Information
$Volume = Invoke-Expression $FullQuery
# Move Virtual Machine
Move-VMStorage -ComputerName "$ClusterNode" -VMName "$VMName" -DestinationStoragePath "C:\ClusterStorage\$Volume\$VMName"
Write-Host "Done."
}
} |
# Target Volume
$TargetVolume = "Volume1"
# Filtered Volumes
$FilteredVolumes = @()
$FilteredVolumes += $TargetVolume
$FilteredVolumes += "Volume2"
$FilteredVolumes += "Volume3"
$FilteredVolumes += "Volume4"
$FilteredVolumes += "Volume5"
$FilteredVolumes += "Volume6"
# Clear Query
$Query = $Null;
# Create Query
foreach ($FilteredVolume in $FilteredVolumes)
{
$Query = $Query + ' Where {$_.SharedVolumeInfo.FriendlyVolumeName -notlike "*' + $FilteredVolume + '"} |'
}
# Full Query
$FullQuery = '((Get-ClusterSharedVolume |' + $Query + ' Select -ExpandProperty SharedVolumeInfo | Select @{label="Name";expression={(($_.FriendlyVolumeName).Split("\"))[-1]}},@{label="FreeSpace";expression={($_ | Select -Expand Partition).FreeSpace}} | Sort FreeSpace -Descending)[0]).Name'
# Get Cluster Nodes
$ClusterNodes = Get-Cluster | Get-ClusterNode
foreach ($ClusterNode in $ClusterNodes)
{
# Clear Variables
$VMs = $Null;
# Get All Virtual Machines on Target Volume
$VMs = Get-VM -ComputerName "$ClusterNode" | Where ConfigurationLocation -like "C:\ClusterStorage\$TargetVolume\*"
foreach ($VM in $VMs)
{
# Get VM Information
$VMName = $VM.Name
Write-Host " "
Write-Host "Working on $VMName .."
Write-Host "Hyper-V Host: $ClusterNode"
# Get Volume Information
$Volume = Invoke-Expression $FullQuery
# Move Virtual Machine
Move-VMStorage -ComputerName "$ClusterNode" -VMName "$VMName" -DestinationStoragePath "C:\ClusterStorage\$Volume\$VMName"
Write-Host "Done."
}
}
It’s enough to run this script on one of the Cluster Node, so that will start migrating virtual machines. That will also give an output, so you will be able to see which VM you are migrating. I hope you will find it useful. See you!
|