Skip to content

Commit

Permalink
Merge pull request #1898 from microsoft/bilong-vsstesterrefactor
Browse files Browse the repository at this point in the history
VSSTester: Allow specifying volumes to back up
  • Loading branch information
bill-long authored Dec 6, 2023
2 parents 2f5ef4b + 6529140 commit 4101f9b
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 194 deletions.
245 changes: 98 additions & 147 deletions Databases/VSSTester/DiskShadow/Invoke-CreateDiskShadowFile.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,30 @@
# Licensed under the MIT License.

function Invoke-CreateDiskShadowFile {
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWMICmdlet', '', Justification = 'Required to get drives on old systems')]
[OutputType([string[]])]
param(
[Parameter(Mandatory = $true)]
[string]
$OutputPath,

[Parameter(Mandatory = $true)]
[string]
$ServerName,

[Parameter(Mandatory = $true)]
[Parameter(Mandatory = $true, ParameterSetName = "BackupByDatabase")]
[object[]]
$Databases,

[Parameter(Mandatory = $true)]
[Parameter(Mandatory = $true, ParameterSetName = "BackupByDatabase")]
[object]
$DatabaseToBackup,

[Parameter(Mandatory = $true)]
[string]
$DatabaseDriveLetter,
[Parameter(Mandatory = $true, ParameterSetName = "BackupByVolume")]
[object[]]
$VolumesToBackup,

[Parameter(Mandatory = $true)]
[string]
$LogDriveLetter
[string[]]
$DriveLetters
)

function Out-DHSFile {
Expand Down Expand Up @@ -74,171 +72,124 @@ function Invoke-CreateDiskShadowFile {
Out-DHSFile "writer exclude {a65faa63-5ea8-4ebc-9dbd-a0c4db26912a}"
Out-DHSFile " "

# add databases to exclude
# ------------------------
foreach ($db in $Databases) {
if ($db.Identity -ne $DatabaseToBackup.Identity) {
if ($db.Server.Name -eq $ServerName) {
Out-DHSFile "writer exclude `"Microsoft Exchange Writer:\Microsoft Exchange Server\Microsoft Information Store\$serverName\$($db.Guid)`""
} else {
#if passive copy, add it with replica in the string
Out-DHSFile "writer exclude `"Microsoft Exchange Replica Writer:\Microsoft Exchange Server\Microsoft Information Store\Replica\$serverName\$($db.Guid)`""
if ($DatabaseToBackup) {
# add databases to exclude
# ------------------------
foreach ($db in $Databases) {
if ($db.Identity -ne $DatabaseToBackup.Identity) {
if ($db.Server.Name -eq $ServerName) {
Out-DHSFile "writer exclude `"Microsoft Exchange Writer:\Microsoft Exchange Server\Microsoft Information Store\$serverName\$($db.Guid)`""
} else {
#if passive copy, add it with replica in the string
Out-DHSFile "writer exclude `"Microsoft Exchange Replica Writer:\Microsoft Exchange Server\Microsoft Information Store\Replica\$serverName\$($db.Guid)`""
}
}
}
}

Out-DHSFile " "
Out-DHSFile "Begin backup"

# add the volumes for the included database
# -----------------------------------------
#gets a list of mount points on local server
$mpVolumes = Get-WmiObject -Query "select name, DeviceId from win32_volume where DriveType=3 AND DriveLetter=NULL"
$deviceIDs = @()

$dbMP = $false
$logMP = $false

#if no MountPoints ($mpVolumes) causes null-valued error, need to handle
if ($null -ne $mpVolumes) {
foreach ($mp in $mpVolumes) {
$mpName = (($mp.name).substring(0, $mp.name.length - 1))
#if following mount point path exists in database path use deviceID in DiskShadow config file
if ($DatabaseToBackup.EdbFilePath.PathName.StartsWith($mpName, [System.StringComparison]::OrdinalIgnoreCase)) {
Write-Host " Mount point: $($mp.name) in use for database path: "
#Write-host "Yes. I am a database in MountPoint"
Write-Host " The selected database path is: $($DatabaseToBackup.EdbFilePath.PathName)"
$dbEdbVol = $mp.DeviceId
Write-Host " adding deviceID to file: $dbEdbVol"

#add device ID to array
$deviceID1 = $mp.DeviceID
$dbMP = $true
if ($DatabaseToBackup) {
# add the volumes for the included database
# -----------------------------------------
#gets a list of mount points on local server
$mpVolumes = Get-CimInstance -Query "select name, DeviceId from win32_volume where DriveType=3 AND DriveLetter=NULL"
$deviceIDs = @()

$dbMP = $false
$logMP = $false

#if no MountPoints ($mpVolumes) causes null-valued error, need to handle
if ($null -ne $mpVolumes) {
foreach ($mp in $mpVolumes) {
$mpName = (($mp.name).substring(0, $mp.name.length - 1))
#if following mount point path exists in database path use deviceID in DiskShadow config file
if ($DatabaseToBackup.EdbFilePath.PathName.StartsWith($mpName, [System.StringComparison]::OrdinalIgnoreCase)) {
Write-Host " Mount point: $($mp.name) in use for database path: "
Write-Host " The selected database path is: $($DatabaseToBackup.EdbFilePath.PathName)"
$dbEdbVol = $mp.DeviceId
Write-Host " adding deviceID to file: $dbEdbVol"

#add device ID to array
$deviceID1 = $mp.DeviceID
$dbMP = $true
}

#if following mount point path exists in log path use deviceID in DiskShadow config file
if ($DatabaseToBackup.LogFolderPath.PathName.ToLower().Contains($mpName.ToLower())) {
Write-Host
Write-Host " Mount point: $($mp.name) in use for log path: "
Write-Host " The log folder path of selected database is: $($DatabaseToBackup.LogFolderPath.PathName)"
$dbLogVol = $mp.DeviceId
Write-Host " adding deviceID to file: $dbLogVol"
$deviceID2 = $mp.DeviceID
$logMP = $true
}
}
}

#if following mount point path exists in log path use deviceID in DiskShadow config file
if ($DatabaseToBackup.LogFolderPath.PathName.ToLower().Contains($mpName.ToLower())) {
Write-Host
Write-Host " Mount point: $($mp.name) in use for log path: "
#Write-host "Yes. My logs are in a MountPoint"
Write-Host " The log folder path of selected database is: $($DatabaseToBackup.LogFolderPath.PathName)"
$dbLogVol = $mp.DeviceId
Write-Host " adding deviceID to file: $dbLogVol"
$deviceID2 = $mp.DeviceID
$logMP = $true
}
if ($dbMP -eq $false) {
$dbEdbVol = ($DatabaseToBackup.EdbFilePath.PathName).substring(0, 2)
Write-Host " The selected database path is '$($DatabaseToBackup.EdbFilePath.PathName)' so adding volume $dbEdbVol to backup scope"
$deviceID1 = $dbEdbVol
}
$deviceIDs = $deviceID1, $deviceID2
}

if ($dbMP -eq $false) {
$dbEdbVol = ($DatabaseToBackup.EdbFilePath.PathName).substring(0, 2)
Write-Host " The selected database path is '$($DatabaseToBackup.EdbFilePath.PathName)' so adding volume $dbEdbVol to backup scope"
$deviceID1 = $dbEdbVol
}
if ($logMP -eq $false) {
$dbLogVol = ($DatabaseToBackup.LogFolderPath.PathName).substring(0, 2)
Write-Host " The selected database log folder path is '$($DatabaseToBackup.LogFolderPath.PathName)' so adding volume $dbLogVol to backup scope"
$deviceID2 = $dbLogVol
}

if ($logMP -eq $false) {
$dbLogVol = ($DatabaseToBackup.LogFolderPath.PathName).substring(0, 2)
Write-Host " The selected database log folder path is '$($DatabaseToBackup.LogFolderPath.PathName)' so adding volume $dbLogVol to backup scope"
$deviceID2 = $dbLogVol
$deviceIDs = @($deviceID1)
if ($deviceID2 -ne $deviceID1) {
$deviceIDs += $deviceID2
}
} else {
$validVolumes = Get-CimInstance -Query "select name, DeviceId from win32_volume where DriveType=3" |
Where-Object { $_.Name -match "^\w:" } | Select-Object Name, DeviceID
$deviceIDs = @()
foreach ($v in $VolumesToBackup) {
$volToBackup = $validVolumes | Where-Object { $_.Name -eq $v }
if ($null -eq $volToBackup) {
Write-Warning "Failed to find volume by name: $v. Available volumes:`n$([string]::Join("`n", $validVolumes))"
exit
}

$deviceIDs += $volToBackup.DeviceID
}
}

# Here is where we start adding the appropriate volumes or MountPoints to the DiskShadow config file
# We make sure that we add only one Logical volume when we detect the EDB and log files
# are on the same volume

Write-Host
$deviceIDs = $deviceID1, $deviceID2
$comp = [string]::Compare($deviceID1, $deviceID2, $True)
if ($comp -eq 0) {
$dID = $deviceIDs[0]
Write-Debug -Message ('$dID = ' + $dID.ToString())
Write-Debug "When the database and log files are on the same volume, we add the volume only once"
if ($dID.length -gt "2") {
$addVol = "add volume $dID alias vss_test_" + ($dID).ToString().substring(11, 8)
Write-Host $addVol
Out-DHSFile $addVol
} else {
$addVol = "add volume $dID alias vss_test_" + ($dID).ToString().substring(0, 1)
Write-Host $addVol
Out-DHSFile $addVol
}
} else {
Write-Host " "
foreach ($device in $deviceIDs) {
if ($device.length -gt "2") {
Write-Host " Adding the Mount Point for DSH file"
$addVol = "add volume $device alias vss_test_" + ($device).ToString().substring(11, 8)
Write-Host " $addVol"
Out-DHSFile $addVol
} else {
Write-Host " Adding the volume for DSH file"
$addVol = "add volume $device alias vss_test_" + ($device).ToString().substring(0, 1)
Write-Host " $addVol"
Out-DHSFile $addVol
}
}
for ($i = 0; $i -lt $deviceIDs.Count; $i++) {
$id = $deviceIDs[$i]
Write-Debug -Message ('$id = ' + $id.ToString())
$addVol = "add volume $id alias vss_test_$i"
Write-Host $addVol
Out-DHSFile $addVol
}

Out-DHSFile "create"
Out-DHSFile " "
Write-Host "$(Get-Date) Getting drive letters for exposing backup snapshot"

# check to see if the drives are the same for both database and logs
# if the same volume is used, only one drive letter is needed for exposure
# if two volumes are used, two drive letters are needed

$matchCondition = "^[a-z]:$"
Write-Debug $matchCondition

$dbSnapVol = $DatabaseDriveLetter
if ($comp -eq 0) {
$logSnapVol = $dbSnapVol
Write-Host " Since the same volume is used for this database's EDB and logs, we only need a single drive"
Write-Host " letter to expose the backup snapshot."
} else {
$logSnapVol = $LogDriveLetter
Write-Host " Since different volumes are used for this database's EDB and logs, we need two drive"
Write-Host " letters to expose the backup snapshot."
}

Write-Debug "dbSnapVol: $dbSnapVol | logSnapVol: $logSnapVol"

# expose the drives
# if volumes are the same only one entry is needed
if ($dbEdbVol -eq $dbLogVol) {
if ($dbEdbVol.length -gt "2") {
$dbVolStr = "expose %vss_test_" + ($dbEdbVol).substring(11, 8) + "% $($dbSnapVol):"
Out-DHSFile $dbVolStr
} else {
$dbVolStr = "expose %vss_test_" + ($dbEdbVol).substring(0, 1) + "% $($dbSnapVol):"
Out-DHSFile $dbVolStr
}
} else {
# volumes are different, getting both
# if MountPoint use first part of string, if not use first letter
if ($dbEdbVol.length -gt "2") {
$dbVolStr = "expose %vss_test_" + ($dbEdbVol).substring(11, 8) + "% $($dbSnapVol)"
Out-DHSFile $dbVolStr
} else {
$dbVolStr = "expose %vss_test_" + ($dbEdbVol).substring(0, 1) + "% $($dbSnapVol)"
Out-DHSFile $dbVolStr
}
if ($deviceIDs.Count -lt $DriveLetters.Count) {
Write-Warning "Determined that we need $($deviceIDs.Count) drive letters to expose the snapshots, but only $($DriveLetters.Count) were provided. Exiting."
exit
}

# if MountPoint use first part of string, if not use first letter
if ($dbLogVol.length -gt "2") {
$logVolStr = "expose %vss_test_" + ($dbLogVol).substring(11, 8) + "% $($logSnapVol):"
Out-DHSFile $logVolStr
} else {
$logVolStr = "expose %vss_test_" + ($dbLogVol).substring(0, 1) + "% $($logSnapVol):"
Out-DHSFile $logVolStr
}
for ($i = 0; $i -lt $deviceIDs.Count; $i++) {
$dbVolStr = "expose %vss_test_$($i)% $($DriveLetters[$i]):"
Out-DHSFile $dbVolStr
}

# ending data of file
Out-DHSFile "end backup"

if ($dbSnapVol -eq $logSnapVol) {
return @($dbSnapVol)
} else {
return @($dbSnapVol, $logSnapVol)
}
# return the drive letters we used
return $DriveLetters | Select-Object -First ($deviceIDs.Count)
}
8 changes: 2 additions & 6 deletions Databases/VSSTester/DiskShadow/Invoke-DiskShadow.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,10 @@ function Invoke-DiskShadow {
param(
[Parameter(Mandatory = $true)]
[string]
$OutputPath,

[Parameter(Mandatory = $true)]
[object]
$DatabaseToBackup
$OutputPath
)

Write-Host "$(Get-Date) Starting DiskShadow copy of Exchange database: $Database"
Write-Host "$(Get-Date) Starting DiskShadow copy."
Write-Host " Running the following command:"
Write-Host " `"C:\Windows\System32\DiskShadow.exe /s $OutputPath\DiskShadow.dsh /l $OutputPath\DiskShadow.log`""

Expand Down
8 changes: 4 additions & 4 deletions Databases/VSSTester/Logging/Invoke-DisableExtraTracing.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function Invoke-DisableExTRATracing {
[string]
$ServerName,

[Parameter(Mandatory = $true)]
[Parameter(Mandatory = $false)]
[object]
$DatabaseToBackup,

Expand All @@ -24,9 +24,8 @@ function Invoke-DisableExTRATracing {
$OutputPath
)
Write-Host "$(Get-Date) Disabling ExTRA Tracing..."
$dbMountedOn = $DatabaseToBackup.Server.Name
if ($dbMountedOn -eq "$ServerName") {
#stop active copy
$traceLocalServerOnly = $null -eq $DatabaseToBackup -or $DatabaseToBackup.Server.Name -eq $ServerName
if ($traceLocalServerOnly) {
Write-Host
Write-Host " Stopping Exchange Trace data collector on $ServerName..."
logman stop vssTester -s $ServerName
Expand All @@ -35,6 +34,7 @@ function Invoke-DisableExTRATracing {
Write-Host
} else {
#stop passive copy
$dbMountedOn = $DatabaseToBackup.Server.Name
Write-Host " Stopping Exchange Trace data collector on $ServerName..."
logman stop vssTester-Passive -s $ServerName
Write-Host " Deleting Exchange Trace data collector on $ServerName..."
Expand Down
8 changes: 4 additions & 4 deletions Databases/VSSTester/Logging/Invoke-EnableExtraTracing.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ function Invoke-EnableExTRATracing {
[string]
$ServerName,

[Parameter(Mandatory = $true)]
[Parameter(Mandatory = $false)]
[object]
$DatabaseToBackup,

Expand Down Expand Up @@ -53,10 +53,9 @@ function Invoke-EnableExTRATracing {
}
}

$dbMountedOn = $DatabaseToBackup.Server.Name
$traceLocalServerOnly = $null -eq $DatabaseToBackup -or $DatabaseToBackup.Server.Name -eq $ServerName

#active server, only get tracing from active node
if ($dbMountedOn -eq $ServerName) {
if ($traceLocalServerOnly) {
Write-Host "Creating Exchange Trace data collector set..."
Invoke-ExtraTracingCreate -ComputerName $ServerName -LogmanName "VSSTester" -OutputPath $OutputPath
Write-Host "Starting Exchange Trace data collector..."
Expand All @@ -70,6 +69,7 @@ function Invoke-EnableExTRATracing {
Write-Host
} else {
#passive server, get tracing from both active and passive nodes
$dbMountedOn = $DatabaseToBackup.Server.Name
Write-Host "Copying the ExTRA config file 'EnabledTraces.config' file to $dbMountedOn..."
#copy EnabledTraces.config from current passive copy to active copy server
Copy-Item "c:\EnabledTraces.Config" "\\$dbMountedOn\c$\EnabledTraces.config" -Force
Expand Down
Loading

0 comments on commit 4101f9b

Please sign in to comment.