search
Categories
Sponsors
VirtualMetric Hyper-V Monitoring, Hyper-V Reporting
Archive
Blogroll

Badges
MCSE
Community

Cozumpark Bilisim Portali
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])

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])

After that script calculates space gap between those two volumes:

[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

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]

Finally we are migrating VM with following line:

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

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)}

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				
			}
		}
	}
}

Every time you run this script, it moves virtual machines unless space gap between CSVs is minimum.