Follow up to the DPM recovery point expiration issues
Previously, I blogged about issues I was having where old recovery points were not being expired/removed from my DPM servers. I had to open a ticket with Microsoft, and worked with them to determine the cause, and since then, they have released a fix.
The fix that Microsoft developed is here: http://www.microsoft.com/downloads/details.aspx?FamilyID=aee949aa-d3e7-4b0f-b718-00b7c20f1257&displayLang=en
A few people have asked for the PowerShell script “show-pruneshadowcopies.ps1” that Microsoft provided and I mentioned in my previous post (here). The script looks like this:
#displays all RP for data sources and shows which RP’s would be deleted by the regular pruneshadowcopies.ps1
# Outputs to a logfile: C:\Program Files\Microsoft DPM\DPM\bin\SHOW-PRUNESHADOWCOPIES.LOG#Author : Mike J
#Date : 02/24/2009
$version="V1.0"$date=get-date
$logfile="SHOW-PRUNESHADOWCOPIES.LOG.txt"function GetDistinctDays([Microsoft.Internal.EnterpriseStorage.Dls.UI.ObjectModel.OMCommon.ProtectionGroup] $group,
[Microsoft.Internal.EnterpriseStorage.Dls.UI.ObjectModel.OMCommon.Datasource] $ds)
{
if($group.ProtectionType -eq [Microsoft.Internal.EnterpriseStorage.Dls.UI.ObjectModel.OMCommon.ProtectionType]::DiskToTape)
{
return 0
}
$scheduleList = get-policyschedule -ProtectionGroup $group -ShortTerm
if($ds -is [Microsoft.Internal.EnterpriseStorage.Dls.UI.ObjectModel.FileSystem.FsDataSource])
{
$jobType = [Microsoft.Internal.EnterpriseStorage.Dls.Intent.JobTypeType]::ShadowCopy
}
else
{
$jobType = [Microsoft.Internal.EnterpriseStorage.Dls.Intent.JobTypeType]::FullReplicationForApplication
if($ds.ProtectionType -eq [Microsoft.Internal.EnterpriseStorage.Dls.Intent.ReplicaProtectionType]::ProtectFromDPM)
{
return 2
}
}
write-host "Look for jobType $jobType"foreach($schedule in $scheduleList)
{
write-host("schedule jobType {0}" -f $schedule.JobType)
if($schedule.JobType -eq $jobType)
{
return [Math]::Ceiling(($schedule.WeekDays.Length * $ds.RecoveryRangeinDays) / 7)
}
}return 0
}function IsShadowCopyExternal($id)
{
$result = $false;$ctx = New-Object -Typename Microsoft.Internal.EnterpriseStorage.Dls.DB.SqlContext
$ctx.Open()$cmd = $ctx.CreateCommand()
$cmd.CommandText = "select COUNT(*) from tbl_RM_ShadowCopy where shadowcopyid = ‘$id’"
write-host $cmd.CommandText
$countObj = $cmd.ExecuteScalar()
write-host $countObj
if ($countObj -eq 0)
{
$result = $true
}
$cmd.Dispose()
$ctx.Close()return $result
}function IsShadowCopyInUse($id)
{
$result = $true;$ctx = New-Object -Typename Microsoft.Internal.EnterpriseStorage.Dls.DB.SqlContext
$ctx.Open()$cmd = $ctx.CreateCommand()
$cmd.CommandText = "select ArchiveTaskId, RecoveryJobId from tbl_RM_ShadowCopy where ShadowCopyId = ‘$id’"
write-host $cmd.CommandText
$reader = $cmd.ExecuteReader()
while($reader.Read())
{
if ($reader.IsDBNull(0) -and $reader.IsDBNull(1))
{
$result = $false
}
}
$cmd.Dispose()
$ctx.Close()return $result
}"**********************************" > $logfile
"Version $version" >> $logfile
get-date >> $logfile$dpmservername = &"hostname"
$dpmsrv = connect-dpmserver $dpmservername
if (!$dpmsrv)
{
write-host "Unable to connect to $dpmservername"
exit 1
}write-host $dpmservername
"Selected DPM server = $DPMservername" >> $logfile
$pgList = get-protectiongroup $dpmservername
if (!$pgList)
{
write-host "No PGs found"
disconnect-dpmserver $dpmservername
exit 2
}write-host("Number of ProtectionGroups = {0}" -f $pgList.Length)
$replicaList = @{}
$latestScDateList = @{}foreach($pg in $pgList)
{
$dslist = get-datasource $pg
if ($dslist.length -gt 0)
{
write-host("Number of datasources in this PG = {0}" -f $dslist.length)
("Number of datasources in this PG = {0}" -f $dslist.length) >> $logfile
}
Foreach ($ds in $dslist)
{
write-host("DS NAME= $ds")
("DS NAME= $ds") >>$logfile
}
foreach ($ds in $dslist)
{
$rplist = get-recoverypoint $ds | where { $_.DataLocation -eq ‘Disk’ }
write-host("Number of recovery points for $ds {0}" -f $rplist.length)
("Number of recovery points for $ds {0}" -f $rplist.length) >>$logfile
$countDistinctDays = GetDistinctDays $pg $ds
write-host("Number of days with fulls = $countDistinctDays")
("Number of days with fulls = $countDistinctDays") >>$logfile
if($countDistinctDays -eq 0)
{
write-host "D2T PG. No recovery points to delete"
"D2T PG. No recovery points to delete" >>$logfile
continue;
}
$replicaList[$ds.ReplicaPath] = $ds.RecoveryRangeinDays
$latestScDateList[$ds.ReplicaPath] = new-object DateTime 0,0
$lastDayOfRetentionRange = ([DateTime]::UtcNow).AddDays($ds.RecoveryRangeinDays * -1);
write-host("Distinct days to count = {0}. LastDayOfRetentionRange = {1} " -f $countDistinctDays, $lastDayOfRetentionRange)
("Distinct days to count = {0}. LastDayOfRetentionRange = {1} " -f $countDistinctDays, $lastDayOfRetentionRange) >>$logfile
$distinctDays = 0;
$lastDistinctDay = (get-Date).Date
$numberOfRecoveryPointsDeleted = 0if ($rplist)
{
foreach ($rp in ($rplist | sort-object -property UtcRepresentedPointInTime -descending))
{
if ($rp)
{
if ($rp.UtcRepresentedPointInTime.Date -lt $lastDistinctDay)
{
$distinctDays += 1
$lastDistinctDay = $rp.UtcRepresentedPointInTime.Date
}
write-host(" $ds")
(" $ds") >>$logfile
write-host(" Recovery Point #$distinctdays RPtime={0}" -f $rp.UtcRepresentedPointInTime)
(" Recovery Point #$distinctdays RPtime={0}" -f $rp.UtcRepresentedPointInTime) >>$logfile
if (($distinctDays -gt $countDistinctDays) -and ($rp.UtcRepresentedPointInTime -lt $lastDayOfRetentionRange))
{
write-host ("Recovery Point would be deleted ! – RPtime={0}" -f $rp.UtcRepresentedPointInTime) -foregroundcolor red
("Recovery Point would be deleted ! – RPtime={0} <<<<<<<" -f $rp.UtcRepresentedPointInTime) >>$logfile
#remove-recoverypoint $rp -ForceDeletion -confirm:$true | out-null
$numberOfRecoveryPointsDeleted += 1
}
else
{
write-host " Recovery point not expired yet"
" Recovery point not yet expired" >>$logfile
}
}
else
{
write-host "Got a NULL rp"
"Got a NULL rp" >>$logfile
}
}write-host "Number of RPs that would be deleted = $numberOfRecoveryPointsDeleted"
"Number of RPs that would be deleted = $numberOfRecoveryPointsDeleted" >>$logfile
}
}
}disconnect-dpmserver $dpmservername
write-host "Exiting from script"exit