Hi,
This is the script I'm currently using. (I'm not copying here the complete version that includes like parameters, logging etc)
I'm using $orphanSize += $fileResult.FileSize to find the total orphaned space ($totalorphanedspace) on specific vCenter. However, the total that I'm getting using $totalorphanedspace is not matching the total value that I'm getting by manually calculating (from the generated report). can you please help.
foreach ($vCenter in $vCenters) {
$Report= @()
$arrUsedDisks = Get-View -ViewType VirtualMachine | % {$_.Layout} | % {$_.Disk} | % {$_.DiskFile}
Get-View -ViewType Datastore -Property Name,Browser,Host | %{
$ds = $_
$dsBrowser = Get-View $ds.browser
$rootPath = "[" + $ds.Name + "]"
$searchSpec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec -property @{
matchPattern="*.vmdk"
details= New-Object VMware.Vim.FileQueryFlags -property @{
filesize=$true
modification=$true
}
}
$searchResult = $dsBrowser.SearchDatastoreSubFolders($rootPath, $searchSpec)
foreach ($folder in $searchResult) {
foreach ($fileResult in $folder.File) {
#if ($fileResult.Path -notmatch "-ctk.vmdk|-delta.vmdk|-00000[0-10].vmdk|-rdmp.vmdk|-rdm.vmdk"-and ($fileResult.Modification -lt $DaysOld)) {
if ($fileResult.Path -match "-flat.vmdk"-and ($fileResult.Modification -lt $DaysOld)) {
if (-not ($arrUsedDisks -contains ($folder.FolderPath + $fileResult.Path))){
$row = "" | Select DataStore, Path, ProbablyOrphanedFile, SizeGB, LastModifiedOn, Host
$row.DataStore = $ds.Name
$row.Path = $folder.FolderPath
$row.ProbablyOrphanedFile = $fileResult.Path
$row.SizeGB = [math]::Round($fileResult.FileSize/1GB,1)
$row.LastModifiedOn = $fileResult.Modification
$row.Host = (Get-View $ds.Host[0].Key).Name
$orphanSize += $fileResult.FileSize
$report += $row
}
}
}
}
}
}
$ovmdkscount = $report.Count
$totalorphanedspace = ([Math]::Round($orphanSize/1GB,1))
$report | ConvertTo-Html –title "$vCenter - Orphaned VMDK Report" –body "<H2>$vCenter - Orphaned VMDK Report</H2>" -head $Header | Out-File $BasePath\$Date\$vCenter-OrphanedVMDKs-$Date.htm
$text = "$vCenter has got $ovmdkscount Orphaned VMDKs - Total Orphaned Space that could be reclaimed is $totalorphanedspace GB"
$text | Out-File $BasePath\$Date\ReportedvCentersStatus.txt -Append -Force
Here is what I was saying about the $orphanSize = 0
foreach ($vCenter in $vCenters) {
$orphanSize = 0 # <<< since you want to tally orphan size per vcenter, for each vcenter you need to set it back to zero
$Report= @()
$arrUsedDisks = Get-View -ViewType VirtualMachine | % {$_.Layout} | % {$_.Disk} | % {$_.DiskFile}
Get-View -ViewType Datastore -Property Name,Browser,Host | %{
$ds = $_
$dsBrowser = Get-View $ds.browser
$rootPath = "[" + $ds.Name + "]"
$searchSpec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec -property @{matchPattern="*.vmdk";details=New-Object VMware.Vim.FileQueryFlags -property @{filesize=$true;modification=$true} }
$searchResult = $dsBrowser.SearchDatastoreSubFolders($rootPath, $searchSpec)
foreach ($folder in $searchResult) {
foreach ($fileResult in $folder.File) {
if ($fileResult.Path -match "-flat.vmdk"-and ($fileResult.Modification -lt $DaysOld)) {
if (-not ($arrUsedDisks -contains ($folder.FolderPath + $fileResult.Path))){
$orphanSize += $fileResult.FileSize
$row = "" | Select DataStore, Path, ProbablyOrphanedFile, SizeGB, LastModifiedOn, Host
$row.DataStore = $ds.Name
$row.Path = $folder.FolderPath
$row.ProbablyOrphanedFile = $fileResult.Path
$row.SizeGB = [math]::Round($fileResult.FileSize/1GB,1)
$row.LastModifiedOn = $fileResult.Modification
$row.Host = (Get-View $ds.Host[0].Key).Name
$report += $row
}
}
}
}
}
#also the following 5 lines were originally not part of your foreach vcenter loop
$ovmdkscount = $report.Count
$totalorphanedspace = ([Math]::Round($orphanSize/1GB,1))
$report | ConvertTo-Html –title "$vCenter - Orphaned VMDK Report" –body "<H2>$vCenter - Orphaned VMDK Report</H2>" -head $Header | Out-File $BasePath\$Date\$vCenter-OrphanedVMDKs-$Date.htm
$text = "$vCenter has got $ovmdkscount Orphaned VMDKs - Total Orphaned Space that could be reclaimed is $totalorphanedspace GB"
$text | Out-File $BasePath\$Date\ReportedvCentersStatus.txt -Append -Force
}
Not sure if that will solve your problem or not but that was a problem.
Alternatively you could add this to each of your rows:
$row.SizeBytes = $fileResult.FileSize
and then calculate the total size at the end:
$otherTotalOrphanedSpace = $report | Measure-Object -sum -property SizeBytes | select -expand Sum | foreach { [Math]::Round($_ / 1GB, 1) }
$orphanSize = 0
Does not exist anywhere in the code you pasted. So as-is it would just keep on growing.
Have you considered keeping a size-bytes in the report, and then using measure-object -sum to calculate total?
Sorry, I didn't understand.
Do you mind modifying the code as per your comments and copy the code here back please?
Thanks.
Here is what I was saying about the $orphanSize = 0
foreach ($vCenter in $vCenters) {
$orphanSize = 0 # <<< since you want to tally orphan size per vcenter, for each vcenter you need to set it back to zero
$Report= @()
$arrUsedDisks = Get-View -ViewType VirtualMachine | % {$_.Layout} | % {$_.Disk} | % {$_.DiskFile}
Get-View -ViewType Datastore -Property Name,Browser,Host | %{
$ds = $_
$dsBrowser = Get-View $ds.browser
$rootPath = "[" + $ds.Name + "]"
$searchSpec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec -property @{matchPattern="*.vmdk";details=New-Object VMware.Vim.FileQueryFlags -property @{filesize=$true;modification=$true} }
$searchResult = $dsBrowser.SearchDatastoreSubFolders($rootPath, $searchSpec)
foreach ($folder in $searchResult) {
foreach ($fileResult in $folder.File) {
if ($fileResult.Path -match "-flat.vmdk"-and ($fileResult.Modification -lt $DaysOld)) {
if (-not ($arrUsedDisks -contains ($folder.FolderPath + $fileResult.Path))){
$orphanSize += $fileResult.FileSize
$row = "" | Select DataStore, Path, ProbablyOrphanedFile, SizeGB, LastModifiedOn, Host
$row.DataStore = $ds.Name
$row.Path = $folder.FolderPath
$row.ProbablyOrphanedFile = $fileResult.Path
$row.SizeGB = [math]::Round($fileResult.FileSize/1GB,1)
$row.LastModifiedOn = $fileResult.Modification
$row.Host = (Get-View $ds.Host[0].Key).Name
$report += $row
}
}
}
}
}
#also the following 5 lines were originally not part of your foreach vcenter loop
$ovmdkscount = $report.Count
$totalorphanedspace = ([Math]::Round($orphanSize/1GB,1))
$report | ConvertTo-Html –title "$vCenter - Orphaned VMDK Report" –body "<H2>$vCenter - Orphaned VMDK Report</H2>" -head $Header | Out-File $BasePath\$Date\$vCenter-OrphanedVMDKs-$Date.htm
$text = "$vCenter has got $ovmdkscount Orphaned VMDKs - Total Orphaned Space that could be reclaimed is $totalorphanedspace GB"
$text | Out-File $BasePath\$Date\ReportedvCentersStatus.txt -Append -Force
}
Not sure if that will solve your problem or not but that was a problem.
Alternatively you could add this to each of your rows:
$row.SizeBytes = $fileResult.FileSize
and then calculate the total size at the end:
$otherTotalOrphanedSpace = $report | Measure-Object -sum -property SizeBytes | select -expand Sum | foreach { [Math]::Round($_ / 1GB, 1) }
Thank you for the detailed response.
For now, if I want to try option 2:
Instead of adding this $row.SizeBytes = $fileResult.FileSize to the code, can I do by utilizing the existing $row.SizeGB = [math]::Round($fileResult.FileSize/1GB,1),
as I want the report to show each vmdk size in GB and adding one more column with $row.SizeBytes for each vmdk would not be beneficial in my case.
Or is there a way, I can use $row.SizeBytes = $fileResult.FileSize, but not show this value on report and just use for measuring the size as you mentioned.
$otherTotalOrphanedSpace = $report | Measure-Object -sum -property SizeGB | select -expand Sum
You can exclude the $row.SizeBytes from your html by using select-object as shown below in red:
$report | select * -exclude SizeBytes | ConvertTo-Html –title "$vCenter - Orphaned VMDK Report" –body "<H2>$vCenter - Orphaned VMDK Report</H2>" -head $Header | Out-File $BasePath\$Date\$vCenter-OrphanedVMDKs-$Date.htm
Keep in mind, comparing this value to the sum of the converted GB values may result in a rounding difference.
Thank you.
In case if any specific Datastore is inaccessible on some vCenter, how do we catch the exception and skip that Datastore. I'm seeing this error:
Exception calling "SearchDatastoreSubFolders" with "2" argument(s): "Datastore 'xxxxxxxxxx' is not accessible. "
At E:\Scripts\OrphanedVMDKs\OrphanedVMDKs.ps1:82 char:5
+ $searchResult = $dsBrowser.SearchDatastoreSubFolders($rootPath, $searchSpec)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : VimException
if you just want to keep it from bleeding on your screen when it hits a ds it can't access then replacing the line with this should do it:
$searchResult = try{$dsBrowser.SearchDatastoreSubFolders($rootPath, $searchSpec)}catch{}
Good luck!
Thanks.
To log the inaccessible Datastore name and specific exception (error) details. Can I do something like this instead:
$searchResult = try {
$dsBrowser.SearchDatastoreSubFolders($rootPath, $searchSpec)
}
catch {
$Error = $Error[0].Exception.Message
Log-Write "On $vCenter - Datastore $ds.Name is not accessible. Error: $Error - Skipping the Datastore."
}