Skip to content
Permalink
Browse files

Various - AvailabilityGroup backup history added (#6251)

* extending dll

* making sbi ag aware

* working ag history

* fixing up help and comments

* how could I forget raw

* removing more debug code

* making PS3 compatible
  • Loading branch information
Stuart-Moore authored and potatoqualitee committed Jan 3, 2020
1 parent de948e2 commit b64035ae52bb8cedad3d94f9e6ae30c9c9434703
BIN +0 Bytes (100%) bin/dbatools.dll
Binary file not shown.

Some generated files are not rendered by default. Learn more.

BIN +0 Bytes (100%) bin/net452/dbatools.dll
Binary file not shown.

Some generated files are not rendered by default. Learn more.

BIN +0 Bytes (100%) bin/netcoreapp2.1/dbatools.dll
Binary file not shown.

Some generated files are not rendered by default. Learn more.

@@ -24,6 +24,11 @@ public class BackupHistory
/// </summary>
public string SqlInstance;

/// <summary>
/// The full Instance name as seen from outside
/// </summary>
public string AvailabilityGroupName;

/// <summary>
/// The Database that was backed up
/// </summary>
BIN +144 KB dbatools.dll
Binary file not shown.
@@ -8,7 +8,11 @@ function Get-DbaDbBackupHistory {
You can even get detailed information (including file path) for latest full, differential and log files.
Backups taken with the CopyOnly option will NOT be returned, unless the IncludeCopyOnly switch is present
Backups taken with the CopyOnly option will NOT be returned, unless the IncludeCopyOnly switch is present or the target includes an Availabity Group listener or a database in an Availability Group
If an Availability Group listener is specified as the target, then all nodes in the Group will be queried to return backup history
If a Sql Instance is specified and one of the target databases is in an Availability Group then the nodes hosting that AG will be queried as well.
Reference: http://www.sqlhub.com/2011/07/find-your-backup-history-in-sql-server.html
@@ -68,6 +72,9 @@ function Get-DbaDbBackupHistory {
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.PARAMETER AgCheck
Internal parameter used for getting history from AvailabilityGroups. If set, this will disable Availability Group support
.NOTES
Tags: DisasterRecovery, Backup
Author: Chrissy LeMaire (@cl) | Stuart Moore (@napalmgram)
@@ -140,6 +147,11 @@ function Get-DbaDbBackupHistory {
If db1 has multiple recovery forks, specifying the RecoveryFork GUID will restrict the search to that fork.
.EXAMPLE
PS C:\> Get-DbaDbBackupHistory -SqlInstance AgListener -Last
Will query all replicas in the Availability Group with AgListener and return the backup chain (Full, Diff and Log) to restore to the most rececnt point in time
#>
[CmdletBinding(DefaultParameterSetName = "Default")]
param (
@@ -155,20 +167,17 @@ function Get-DbaDbBackupHistory {
[DateTime]$Since = (Get-Date '01/01/1970'),
[ValidateScript( { ($_ -match '^(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}$') -or ('' -eq $_) })]
[string]$RecoveryFork,
[Parameter(ParameterSetName = "Last")]
[switch]$Last,
[Parameter(ParameterSetName = "Last")]
[switch]$LastFull,
[Parameter(ParameterSetName = "Last")]
[switch]$LastDiff,
[Parameter(ParameterSetName = "Last")]
[switch]$LastLog,
[string[]]$DeviceType,
[switch]$Raw,
[bigint]$LastLsn,
[switch]$IncludeMirror,
[ValidateSet("Full", "Log", "Differential", "File", "Differential File", "Partial Full", "Partial Differential")]
[string[]]$Type,
[switch]$AgCheck,
[switch]$EnableException
)

@@ -217,6 +226,46 @@ function Get-DbaDbBackupHistory {
} catch {
Stop-Function -Message "Error occurred while establishing connection to $instance" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
$AgResults = @()
$ProcessedAgDatabases = @()
if (($server.AvailabilityGroups.count -gt 0) -and ($agCheck -ne $True)) {
$agShortInstance = $instance.FullName.split('.')[0]
if ($agShortInstance -in ($server.AvailabilityGroups.AvailabilityGroupListeners).Name) {
# We have a listener passed in, just query the dbs specified or all in the AG
$null = $PSBoundParameters.Remove('SqlInstance')
$null = $PSBoundParameters.Remove('IncludeCopyOnly')
$null = $PsBoundParameters.Remove('AgCheck')
Write-Message -Level Verbose -Message "Fetching history from replicas on $($AvailabilityGroupBase.AvailabilityReplicas.name)"
$AvailabilityGroupBase = ($server.AvailabilityGroups | Where-Object { $_.AvailabilityGroupListeners.name -eq $agShortInstance })
$AgLoopResults = Get-DbaDbBackupHistory -SqlInstance $AvailabilityGroupBase.AvailabilityReplicas.name @PSBoundParameters -AgCheck -IncludeCopyOnly
$AvailabilityGroupName = $AvailabilityGroupBase.name
Foreach ($agr in $AgLoopResults) {
$agr.AvailabilityGroupName = $AvailabilityGroupName
}
if ($Last) {
Write-Message -Level Verbose -Message "Filtering Ag backups for Last"
$AgResults = $AgLoopResults | Select-DbaBackupInformation -ServerName $AvailabilityGroupName
} elseif ($LastFull) {
Foreach ($AgDb in ( $AgLoopResults.Database | Select-Object -Unique)) {
$AgResults += $AgLoopResults | Where-Object { $_.Database -eq $AgDb } | Sort-Object -Property FirstLsn | Select-Object -Last 1
}
} elseif ($LastDiff) {
Foreach ($AgDb in ( $AgLoopResults.Database | Select-Object -Unique)) {
$AgResults += $AgLoopResults | Where-Object { $_.Database -eq $AgDb } | Sort-Object -Property FirstLsn | Select-Object -Last 1
}
} elseif ($LastLog) {
Foreach ($AgDb in ( $AgLoopResults.Database | Select-Object -Unique)) {
$AgResults += $AgLoopResults | Where-Object { $_.Database -eq $AgDb } | Sort-Object -Property FirstLsn | Select-Object -Last 1
}
} else {
$AgResults += $AgLoopResult
}
# Results are already in the correct format so drop to output
$agresults
# We're done at this point so exit function
return
}
}

if ($server.VersionMajor -ge 12) {
$compressedFlag = $true
@@ -259,6 +308,28 @@ function Get-DbaDbBackupHistory {
if ($ExcludeDatabase) {
$databases = $databases | Where-Object Name -NotIn $ExcludeDatabase
}
if (($server.AvailabilityGroups.count -gt 0) -and ($agCheck -ne $True)) {
$adbs = $databases | Where-Object Name -In $server.AvailabilityGroups.AvailabilityDatabases.Name
$adbs = $adbs | Where-Object Name -NotIn $ProcessedAgDatabases
ForEach ($adb in $adbs) {
Write-Message -Level Verbose -Message "Fetching history from replicas for db $($adb.name)"
if ($adb.GetType().name -ne 'Database') {
$adb = Get-DbaDatabase -SqlInstance $server -Database $adb.name
}
$AvailabilityGroupBase = $adb.parent.AvailabilityGroups[$adb.AvailabilityGroupName]
$null = $PSBoundParameters.Remove('SqlInstance')
$null = $PSBoundParameters.Remove('Database')
$AgLoopResults = Get-DbaDbBackupHistory -SqlInstance $AvailabilityGroupBase.AvailabilityGroupListeners.name -database $adb.Name @PSBoundParameters
$AvailabilityGroupName = $AvailabilityGroupBase.name
Foreach ($agr in $AgLoopResults) {
$agr.AvailabilityGroupName = $AvailabilityGroupName
}
# Results already in the right format, drop straight to output
$AgLoopResults
# Remove database from collection as it's now done with
$databases = $databases | Where-Object Name -ne $adb.name
}
}
foreach ($d in $deviceTypeFilter) {
$deviceTypeFilterRight = "IN ('" + ($deviceTypeFilter -Join "','") + "')"
}
@@ -309,8 +380,8 @@ function Get-DbaDbBackupHistory {
}
#Get the full and build upwards
$allBackups = @()
$allBackups += $fullDb = Get-DbaDbBackupHistory -SqlInstance $server -Database $db.Name -LastFull -raw:$Raw -DeviceType $DeviceType -IncludeCopyOnly:$IncludeCopyOnly -Since:$since -RecoveryFork $RecoveryFork
$diffDb = Get-DbaDbBackupHistory -SqlInstance $server -Database $db.Name -LastDiff -raw:$Raw -DeviceType $DeviceType -IncludeCopyOnly:$IncludeCopyOnly -Since:$since -RecoveryFork $RecoveryFork
$allBackups += $fullDb = Get-DbaDbBackupHistory -SqlInstance $server -Database $db.Name -LastFull -raw:$Raw -DeviceType $DeviceType -IncludeCopyOnly:$IncludeCopyOnly -Since:$since -RecoveryFork $RecoveryFork -AgCheck:$Agcheck
$diffDb = Get-DbaDbBackupHistory -SqlInstance $server -Database $db.Name -LastDiff -raw:$Raw -DeviceType $DeviceType -IncludeCopyOnly:$IncludeCopyOnly -Since:$since -RecoveryFork $RecoveryFork -AgCheck:$AgCheck
if ($diffDb.LastLsn -gt $fullDb.LastLsn -and $diffDb.DatabaseBackupLSN -eq $fullDb.CheckPointLSN ) {
Write-Message -Level Verbose -Message "Valid Differential backup "
$allBackups += $diffDb
@@ -323,7 +394,12 @@ function Get-DbaDbBackupHistory {
continue
}
}
$allBackups += Get-DbaDbBackupHistory -SqlInstance $server -Database $db.Name -raw:$raw -DeviceType $DeviceType -LastLsn $tlogStartDsn -IncludeCopyOnly:$IncludeCopyOnly -Since:$since -RecoveryFork $RecoveryFork | Where-Object { $_.Type -eq 'Log' -and [bigint]$_.LastLsn -gt [bigint]$tlogStartDsn -and [bigint]$_.DatabaseBackupLSN -eq [bigint]$fullDb.CheckPointLSN -and $_.LastRecoveryForkGuid -eq $fullDb.LastRecoveryForkGuid }
if ($IncludeCopyOnly -eq $true) {
Write-Message -Level Verbose -Message 'Copy Only chekc'
$allBackups += Get-DbaDbBackupHistory -SqlInstance $server -Database $db.Name -raw:$raw -DeviceType $DeviceType -LastLsn $tlogStartDsn -IncludeCopyOnly:$IncludeCopyOnly -Since:$since -RecoveryFork $RecoveryFork -AgCheck:$Agcheck | Where-Object { $_.Type -eq 'Log' -and [bigint]$_.LastLsn -gt [bigint]$tlogStartDsn -and $_.LastRecoveryForkGuid -eq $fullDb.LastRecoveryForkGuid }
} else {
$allBackups += Get-DbaDbBackupHistory -SqlInstance $server -Database $db.Name -raw:$raw -DeviceType $DeviceType -LastLsn $tlogStartDsn -IncludeCopyOnly:$IncludeCopyOnly -Since:$since -RecoveryFork $RecoveryFork -AgCheck:$Agcheck | Where-Object { $_.Type -eq 'Log' -and [bigint]$_.LastLsn -gt [bigint]$tlogStartDsn -and [bigint]$_.DatabaseBackupLSN -eq [bigint]$fullDb.CheckPointLSN -and $_.LastRecoveryForkGuid -eq $fullDb.LastRecoveryForkGuid }
}
#This line does the output for -Last!!!
$allBackups | Sort-Object -Property LastLsn, Type
}
@@ -404,6 +480,7 @@ function Get-DbaDbBackupHistory {
$sql += "SELECT
a.BackupSetRank,
a.Server,
'' as AvailabilityGroupName,
a.[Database],
a.Username,
a.Start,
@@ -602,7 +679,7 @@ function Get-DbaDbBackupHistory {
}

Write-Message -Level Debug -Message "SQL Statement: `n$sql"
Write-Message -Level SomewhatVerbose -Message "Executing sql query."
Write-Message -Level SomewhatVerbose -Message "Executing sql query on $server."
$results = $server.ConnectionContext.ExecuteWithResults($sql).Tables.Rows | Select-Object * -ExcludeProperty BackupSetRank, RowError, RowState, Table, ItemArray, HasErrors

if ($raw) {
@@ -120,9 +120,15 @@ function Select-DbaBackupInformation {
# $InternalHistory = $InternalHistory | Where-Object {$_.Database -in $DatabaseName}
}

# Check for AGs
if (Test-Bound -ParameterName ServerName) {
Write-Message -Message "Filtering by ServerName" -Level Verbose
$InternalHistory = $InternalHistory | Where-Object { $_.InstanceName -in $servername }
if (($InternalHistory | Where-Object { $_.AvailabilityGroupName -ne '' }).count -ne 0) {
Write-Message -Level Verbose -Message 'Dealing with Availabilitygroups'
$InternalHistory = $InternalHistory | Where-Object { $_.AvailabilityGroupName -in $servername }
} else {
Write-Message -Message "Filtering by ServerName" -Level Verbose
$InternalHistory = $InternalHistory | Where-Object { $_.InstanceName -in $servername }
}
}

$Databases = ($InternalHistory | Select-Object -Property Database -unique).Database
@@ -4,11 +4,11 @@ Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan

Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
Context "Validate parameters" {
[object[]]$params = (Get-Command $CommandName).Parameters.Keys | Where-Object {$_ -notin ('whatif', 'confirm')}
[object[]]$knownParameters = 'SqlInstance', 'SqlCredential', 'Database', 'ExcludeDatabase', 'IncludeCopyOnly', 'Force', 'Since', 'RecoveryFork', 'Last', 'LastFull', 'LastDiff', 'LastLog', 'DeviceType', 'Raw', 'LastLsn', 'Type', 'EnableException', 'IncludeMirror'
[object[]]$params = (Get-Command $CommandName).Parameters.Keys | Where-Object { $_ -notin ('whatif', 'confirm') }
[object[]]$knownParameters = 'SqlInstance', 'SqlCredential', 'Database', 'ExcludeDatabase', 'IncludeCopyOnly', 'Force', 'Since', 'RecoveryFork', 'Last', 'LastFull', 'LastDiff', 'LastLog', 'DeviceType', 'Raw', 'LastLsn', 'Type', 'EnableException', 'IncludeMirror', 'AgCheck'
$knownParameters += [System.Management.Automation.PSCmdlet]::CommonParameters
It "Should only contain our specific parameters" {
(@(Compare-Object -ReferenceObject ($knownParameters | Where-Object {$_}) -DifferenceObject $params).Count ) | Should Be 0
(@(Compare-Object -ReferenceObject ($knownParameters | Where-Object { $_ }) -DifferenceObject $params).Count ) | Should Be 0
}
}
}
@@ -102,7 +102,7 @@ Describe "$CommandName Integration Tests" -Tag "IntegrationTests" {
$server = connect-dbainstance $script:instance1
$cast = $server.Query('select cast(1000000000000000 as numeric(20,0)) AS TotalSize')
$historyObject.TotalSize = $cast.TotalSize
($historyObject.TotalSize.Byte)| Should -Be 1000000000000000
($historyObject.TotalSize.Byte) | Should -Be 1000000000000000
}
}
}

0 comments on commit b64035a

Please sign in to comment.
You can’t perform that action at this time.