Add advanced options to backup service (#405)

* Add advanced options for Backup

* Add backup encryption strings

* Add test for backup advanced option

* Add strings to SR

* Add verify backup restore option

* Addressed PR comments

* Add MIT license header
This commit is contained in:
Kate Shin
2017-07-12 13:09:07 -07:00
committed by GitHub
parent 64e671ca2a
commit 414949d129
11 changed files with 450 additions and 125 deletions

View File

@@ -13,6 +13,7 @@ using Microsoft.SqlServer.Management.Sdk.Sfc;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlTools.ServiceLayer.Admin;
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
using System.Globalization;
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
{
@@ -137,13 +138,14 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
/// </summary>
/// <param name="databaseName"></param>
/// <returns></returns>
public BackupConfigInfo GetBackupConfigInfo(string databaseName)
public BackupConfigInfo CreateBackupConfigInfo(string databaseName)
{
BackupConfigInfo databaseInfo = new BackupConfigInfo();
databaseInfo.RecoveryModel = this.GetRecoveryModel(databaseName);
databaseInfo.DefaultBackupFolder = this.GetDefaultBackupFolder();
databaseInfo.LatestBackups = this.GetLatestBackupLocations(databaseName);
return databaseInfo;
BackupConfigInfo configInfo = new BackupConfigInfo();
configInfo.RecoveryModel = GetRecoveryModel(databaseName);
configInfo.DefaultBackupFolder = GetDefaultBackupFolder();
configInfo.LatestBackups = GetLatestBackupLocations(databaseName);
configInfo.BackupEncryptors = GetBackupEncryptors();
return configInfo;
}
/// <summary>
@@ -226,17 +228,78 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
}
this.backup.CopyOnly = this.backupInfo.IsCopyOnly;
this.backup.FormatMedia = this.backupInfo.FormatMedia;
this.backup.Initialize = this.backupInfo.Initialize;
this.backup.SkipTapeHeader = this.backupInfo.SkipTapeHeader;
this.backup.Checksum = this.backupInfo.Checksum;
this.backup.ContinueAfterError = this.backupInfo.ContinueAfterError;
//TODO: This should be changed to get user inputs
this.backup.FormatMedia = false;
this.backup.Initialize = false;
this.backup.SkipTapeHeader = true;
this.backup.Checksum = false;
this.backup.ContinueAfterError = false;
this.backup.LogTruncation = BackupTruncateLogType.Truncate;
if (!string.IsNullOrEmpty(this.backupInfo.MediaName))
{
this.backup.MediaName = this.backupInfo.MediaName;
}
if (!string.IsNullOrEmpty(this.backupInfo.MediaDescription))
{
this.backup.MediaDescription = this.backupInfo.MediaDescription;
}
if (this.backupInfo.TailLogBackup
&& !this.backupRestoreUtil.IsHADRDatabase(this.backupInfo.DatabaseName)
&& !this.backupRestoreUtil.IsMirroringEnabled(this.backupInfo.DatabaseName))
{
this.backup.NoRecovery = true;
}
if (this.backupInfo.LogTruncation)
{
this.backup.LogTruncation = BackupTruncateLogType.Truncate;
}
else
{
this.backup.LogTruncation = BackupTruncateLogType.NoTruncate;
}
if (!string.IsNullOrEmpty(this.backupInfo.BackupSetDescription))
{
this.backup.BackupSetDescription = this.backupInfo.BackupSetDescription;
}
if (this.backupInfo.RetainDays >= 0)
{
this.backup.RetainDays = this.backupInfo.RetainDays;
}
else
{
this.backup.ExpirationDate = this.backupInfo.ExpirationDate;
}
this.backup.CompressionOption = (BackupCompressionOptions)this.backupInfo.CompressionOption;
if (!string.IsNullOrEmpty(this.backupInfo.EncryptorName))
{
this.backup.EncryptionOption = new BackupEncryptionOptions((BackupEncryptionAlgorithm)this.backupInfo.EncryptionAlgorithm,
(BackupEncryptorType)this.backupInfo.EncryptorType,
this.backupInfo.EncryptorName);
}
// Execute backup
this.backup.SqlBackup(this.dataContainer.Server);
// Verify backup if required
if (this.backupInfo.VerifyBackupRequired)
{
Restore restore = new Restore();
restore.Devices.AddRange(this.backup.Devices);
restore.Database = this.backup.Database;
string errorMessage = null;
restore.SqlVerifyLatest(this.dataContainer.Server, out errorMessage);
if (errorMessage != null)
{
throw new Exception(errorMessage);
}
}
}
/// <summary>
@@ -277,11 +340,34 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
}
/// <summary>
/// Return true if backup to URL is supported in the current SQL Server version
/// Returns the certificates and asymmetric keys from master for encryption
/// </summary>
private bool BackupToUrlSupported()
public List<BackupEncryptor> GetBackupEncryptors()
{
return BackupRestoreBase.IsBackupUrlDeviceSupported(this.dataContainer.Server.PingSqlServerVersion(this.dataContainer.ServerName));
List<BackupEncryptor> encryptors = new List<BackupEncryptor>();
if (this.dataContainer.Server.Databases.Contains("master"))
{
CertificateCollection certificates = this.dataContainer.Server.Databases["master"].Certificates;
DateTime currentUtcDateTime = DateTime.UtcNow;
foreach (Certificate item in certificates)
{
if ((item.Name.StartsWith("##", StringComparison.InvariantCulture) && item.Name.EndsWith("##", StringComparison.InvariantCulture)) ||
DateTime.Compare(item.ExpirationDate, currentUtcDateTime) < 0)
{
continue;
}
encryptors.Add(new BackupEncryptor((int)BackupEncryptorType.ServerCertificate, item.Name));
}
AsymmetricKeyCollection keys = this.dataContainer.Server.Databases["master"].AsymmetricKeys;
foreach (AsymmetricKey item in keys)
{
if (item.KeyEncryptionAlgorithm == AsymmetricKeyEncryptionAlgorithm.CryptographicProviderDefined)
{
encryptors.Add(new BackupEncryptor((int)BackupEncryptorType.ServerAsymmetricKey, item.Name));
}
}
}
return encryptors;
}
#endregion

View File

@@ -54,6 +54,33 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
StandBy
}
/// <summary>
/// Class for backup encryptor
/// </summary>
public class BackupEncryptor
{
/// <summary>
/// Ctor
/// </summary>
/// <param name="encryptorType"></param>
/// <param name="encryptorName"></param>
public BackupEncryptor(int encryptorType, string encryptorName)
{
this.EncryptorType = encryptorType;
this.EncryptorName = encryptorName;
}
/// <summary>
/// Encryptor type - certificate or asymetric key
/// </summary>
public int EncryptorType { get; set; }
/// <summary>
/// Encryptor name
/// </summary>
public string EncryptorName { get; set; }
}
/// <summary>
/// Restore item source
/// </summary>

View File

@@ -13,11 +13,34 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts
/// </summary>
public class BackupConfigInfo
{
/// <summary>
/// Gets or sets default database info
/// </summary>
public DatabaseInfo DatabaseInfo { get; set; }
/// <summary>
/// Gets or sets recovery model of a database
/// </summary>
public string RecoveryModel { get; set; }
/// <summary>
/// Gets or sets the latest backup set of a database
/// </summary>
public List<RestoreItemSource> LatestBackups { get; set; }
/// <summary>
/// Gets or sets the default backup folder
/// </summary>
public string DefaultBackupFolder { get; set; }
/// <summary>
/// Gets or sets backup encryptors
/// </summary>
public List<BackupEncryptor> BackupEncryptors { get; set; }
/// <summary>
/// Ctor
/// </summary>
public BackupConfigInfo()
{
}

View File

@@ -3,6 +3,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Collections.Generic;
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts
@@ -13,7 +14,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts
/// Name of the datbase to perfom backup
/// </summary>
public string DatabaseName { get; set; }
/// <summary>
/// Component to backup - Database or Files
/// </summary>
@@ -48,15 +49,104 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts
/// List of {key: backup path, value: device type}
/// </summary>
public Dictionary<string, int> BackupPathDevices { get; set; }
/// <summary>
/// List of selected backup paths
/// </summary>
public List<string> BackupPathList { get; set; }
/// <summary>
/// Indicates if the backup should be copy-only
/// </summary>
public bool IsCopyOnly { get; set; }
/// <summary>
/// Gets or sets a Boolean property value that determines whether a media is formatted as the first step of the backup operation.
/// </summary>
public bool FormatMedia { get; set; }
/// <summary>
/// Gets or sets a Boolean property value that determines whether the devices associated with a backup operation are initialized as part of the backup operation.
/// </summary>
public bool Initialize { get; set; }
/// <summary>
/// Gets or sets Boolean property that determines whether the tape header is read.
/// </summary>
public bool SkipTapeHeader { get; set; }
/// <summary>
/// Gets or sets the name used to identify a particular media set.
/// </summary>
public string MediaName { get; set; }
/// <summary>
/// Gets or sets a textual description of the medium that contains a backup set.
/// </summary>
public string MediaDescription { get; set; }
/// <summary>
/// Gets or sets a Boolean property value that determines whether a checksum value is calculated during backup or restore operations.
/// </summary>
public bool Checksum { get; set; }
/// <summary>
/// Gets or sets a Boolean property value that determines whether the backup or restore continues after a checksum error occurs.
/// </summary>
public bool ContinueAfterError { get; set; }
/// <summary>
/// Gets or sets a Boolean property value that determines whether to truncate the database log.
/// </summary>
public bool LogTruncation { get; set; }
/// <summary>
/// Gets or sets a Boolean property value that determines whether to backup the tail of the log
/// </summary>
public bool TailLogBackup { get; set; }
/// <summary>
/// Gets or sets a textual description for a particular backup set.
/// </summary>
public string BackupSetDescription { get; set; }
/// <summary>
/// Gets or sets the number of days that must elapse before a backup set can be overwritten.
/// </summary>
public int RetainDays { get; set; }
/// <summary>
/// Gets or sets the date and time when the backup set expires and the backup data is no longer considered relevant.
/// </summary>
public DateTime ExpirationDate { get; set; }
/// <summary>
/// Gets or sets the backup compression option.
/// This should be converted to BackupCompressionOptions when setting it to Backup object.
/// </summary>
public int CompressionOption { get; set; }
/// <summary>
/// Gets or sets a Boolean property that determines whether verify is required.
/// </summary>
public bool VerifyBackupRequired { get; set; }
/// <summary>
/// Specifies the algorithm type used for backup encryption.
/// This should be converted to BackupEncryptionAlgorithm when creating BackupEncryptionOptions object.
/// </summary>
public int EncryptionAlgorithm { get; set; }
/// <summary>
/// Specifies the encryptor type used to encrypt an encryption key.
/// This should be converted to BackupEncryptorType when creating BackupEncryptionOptions object.
/// </summary>
public int EncryptorType { get; set; }
/// <summary>
/// Gets or sets the name of the encryptor.
/// </summary>
public string EncryptorName { get; set; }
}
}

View File

@@ -281,7 +281,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
{
BackupOperation backupOperation = new BackupOperation();
backupOperation.Initialize(dataContainer, sqlConnection);
return backupOperation.GetBackupConfigInfo(databaseName);
return backupOperation.CreateBackupConfigInfo(databaseName);
}
internal BackupOperation SetBackupInput(CDataContainer dataContainer, SqlConnection sqlConnection, BackupInfo input)

View File

@@ -26,7 +26,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
/// </summary>
/// <param name="databaseName"></param>
/// <returns></returns>
BackupConfigInfo GetBackupConfigInfo(string databaseName);
BackupConfigInfo CreateBackupConfigInfo(string databaseName);
/// <summary>
/// Set backup input properties