This is a script I put together to manually run the unmap to all shared iSCSI datastores from all hosts in a vCenter. After doing this your SAN should report the correct block usage ie more space!
With VMFS 6 this is not really needed but there may be times when you need to know that unMap is working, need to kick it in to life, or you have VMFS 5 datastores and need to periodically clean them up.
This script will do the following
- Loop through each host
- start SSH if its not running
- SSH to the host and run ESXtop
- Get the number of unMaps sent to each disk
- loop through each disk and run the unMap command
- SSH to the host and run ESXtop
- get the number of unMaps sent to each disk
- disconnect from the host and stop the SSH service if need to
- subtract the before unMaps from the after unMaps and report the difference
You will then have to look at your storage to see if the volume/LUNs are reporting more free space then before.
# jasons unMapper script
# v 1.04
# 03/04/2020
$vCenter = "my-vCenter-server"
$TimeOutSec = 60 # timeout for the SSH unmap command. if set to low the unmap command will still be running when the report of unmaps sent to disks is run. set to long and the script will take ages to run
$RootCred = Get-Credential
$LogFileName = "Unmap-" + ( get-date -f yyyyMMdd ) + ".csv"
$Report = @()
# connecting to the vcenter
connect-viserver $vCenter
# getting a list of shared datastores
$DSs = get-datastore | where{$_.ExtensionData.Summary.MultipleHostAccess -eq $true} | where {$_.type -eq "VMFS"}
$vHosts = get-vmhost
# loop through all hosts
foreach ($vHost in $vHosts)
{
# start the SSH service if its not started, to allow SSH logon
$SSHService = Get-VMHostService -VMHost $vHost | Where {$_.Key -eq "TSM-SSH"}
if ($SSHService.Running -eq $flase)
{
$SSHService | Start-VMHostService -Confirm:$false
if ($?){$DoINeedToStopTheSSHService = "yes"}
}else{
# if the service was started, don't try to stop it
$DoINeedToStopTheSSHService = "no"
}
$BeforeScan = @()
$AfterScan = @()
# connect to the host using SSH and get the session object
"Connecting to host " + $vHost.name
$Session = New-SSHSession -ComputerName $vHost.name -Credential $RootCred -Force
# run esxtop and get every metric back
"Getting unMaps from " + $vHost.name
$CommandString = "esxtop -a -n 2"
$Res = Invoke-SSHCommand -SSHSession $Session -Command $CommandString
# get the header row and the last data row
$Headers = $res.output[0].Split(",")
$Data = $res.output[2].Split(",")
# loop through the headers and get the cell number of any header with delete in the name
$TotalUnMaps = 0
for($x = 0 ; $x -lt $Headers.count ; $x ++)
{
if ($Headers[$x] -match '\Delete"')
{
# create a tmp object for the host, the disk name and the number of unmap
$tmpReport = '' | select Host, Disk, UnMaps
$tmpReport.host = $vHost.name
$tmpReport.Disk = $Headers[$x]
$tmpReport.UnMaps = [int](($Data[$x].replace('"','')))
$TotalUnMaps = $TotalUnMaps + [int](($Data[$x].replace('"','')))
$BeforeScan += $tmpReport
}
}
# create a tmp object for the host, then the total unmaps
$tmpReport = '' | select Host, Disk, UnMaps
$tmpReport.host = $vHost.name
$tmpReport.Disk = 'All'
$tmpReport.UnMaps = $TotalUnMaps
$BeforeScan += $tmpReport
# loop though each DS and send the unmap command.
# the unmap command can take ages to run so the invoke SSH command will time out
foreach ($DS in $DSs)
{
"sending unMap command to " + $vHost.name + ", disk " + $DS.name
$CommandString = "esxcli storage vmfs unmap -l " + $DS.name
$Res = Invoke-SSHCommand -SSHSession $Session -Command $CommandString -TimeOut $TimeOutSec
}
# run esxtop and get every metric back. same as the pre scan. but this is creating a Afterscan array
"Getting unMaps from " + $vHost.name
$CommandString = "esxtop -a -n 2"
$Res = Invoke-SSHCommand -SSHSession $Session -Command $CommandString
$Headers = $res.output[0].Split(",")
$Data = $res.output[2].Split(",")
$TotalUnMaps = 0
for($x = 0 ; $x -lt $Headers.count ; $x ++)
{
if ($Headers[$x] -match '\Delete"')
{
$tmpReport = '' | select Host, Disk, UnMaps
$tmpReport.host = $vHost.name
$tmpReport.Disk = $Headers[$x]
$tmpReport.UnMaps = [int](($Data[$x].replace('"','')))
$TotalUnMaps = $TotalUnMaps + [int](($Data[$x].replace('"','')))
$AfterScan += $tmpReport
}
}
$tmpReport = '' | select Host, Disk, UnMaps
$tmpReport.host = $vHost.name
$tmpReport.Disk = "All"
$tmpReport.UnMaps = $TotalUnMaps
$AfterScan += $tmpReport
# loop through each DS, get the difference in unmaps from the before and after scan arrays
foreach ($DS in $DSs)
{
$DSnaa = $DS.ExtensionData.Info.Vmfs.Extent[0].DiskName
$tmpReport = '' | select Host, Disk, UnMaps, unMapsb, unMapsa
$tmpReport.host = $vHost.name
$tmpReport.disk = $DS.name
$tmpReport.unMapsb = ($BeforeScan | where{$_.disk -match $DSnaa}).UnMaps
$tmpReport.unMapsa = ($AfterScan | where{$_.disk -match $DSnaa}).UnMaps
$tmpReport.UnMaps = $tmpReport.unMapsa - $tmpReport.unMapsb
$Report += $tmpReport
}
# disconnect the SSH session
Remove-SSHSession -SSHSession $Session
# if the script started SSH then stop it.
if ($DoINeedToStopTheSSHService -eq "yes")
{
$SSHService = Get-VMHostService -VMHost $vHost | Where {$_.Key -eq "TSM-SSH"}
$SSHService | Stop-VMHostService -Confirm:$false
}
}
# save the report
$Report | export-csv $LogFileName -NoTypeInformation
There you go.
Run this and you will end up with a CSV of the number of unMAps sent to each disk from each host (and the totals). Also your SAN should now be showing more free space then before.