mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-27 01:25:42 -05:00
Integrate generate script with task service (#426)
* Support generate script for backup * change * update task service data contract for Generate Script * more changes * update test * add comments * Add missing files * update stub backup operation for testing * pr comments * remove empty space * Fix tests * Add unit/integration tests and isCancelable to TaskInfo * address pr comments * pr comments - fix tests * fix minor issue * fix minor issues * remove unused variable
This commit is contained in:
@@ -14,6 +14,8 @@ using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlTools.ServiceLayer.Admin;
|
||||
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using Microsoft.SqlTools.ServiceLayer.TaskServices;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
{
|
||||
@@ -26,6 +28,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
private ServerConnection serverConnection;
|
||||
private CommonUtilities backupRestoreUtil = null;
|
||||
private Backup backup = null;
|
||||
private string scriptContent = "";
|
||||
|
||||
/// <summary>
|
||||
/// Constants
|
||||
@@ -111,7 +114,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
{
|
||||
this.dataContainer = dataContainer;
|
||||
this.serverConnection = new ServerConnection(sqlConnection);
|
||||
this.backupRestoreUtil = new CommonUtilities(this.dataContainer, this.serverConnection);
|
||||
this.backupRestoreUtil = new CommonUtilities(this.dataContainer, this.serverConnection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -122,7 +125,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
{
|
||||
this.backupInfo = input;
|
||||
|
||||
// convert the types
|
||||
// convert the types
|
||||
this.backupComponent = (BackupComponent)input.BackupComponent;
|
||||
this.backupType = (BackupType)input.BackupType;
|
||||
this.backupDeviceType = (BackupDeviceType)input.BackupDeviceType;
|
||||
@@ -148,52 +151,62 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
return configInfo;
|
||||
}
|
||||
|
||||
public string ScriptContent
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.scriptContent;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.scriptContent = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute backup
|
||||
/// </summary>
|
||||
public void PerformBackup()
|
||||
public void Execute(TaskExecutionMode mode)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
SqlExecutionModes oldExecutionMode = this.dataContainer.Server.ConnectionContext.SqlExecutionModes;
|
||||
this.dataContainer.Server.ConnectionContext.SqlExecutionModes = (mode == TaskExecutionMode.Script) ? SqlExecutionModes.CaptureSql: SqlExecutionModes.ExecuteAndCaptureSql;
|
||||
this.dataContainer.Server.ConnectionContext.CapturedSql.Clear();
|
||||
this.backup = new Backup();
|
||||
this.backup.Database = this.backupInfo.DatabaseName;
|
||||
this.backup.Action = this.backupActionType;
|
||||
this.backup.Incremental = this.isBackupIncremental;
|
||||
this.SetBackupProps();
|
||||
|
||||
if (this.backup.Action == BackupActionType.Files)
|
||||
try
|
||||
{
|
||||
IDictionaryEnumerator filegroupEnumerator = this.backupInfo.SelectedFileGroup.GetEnumerator();
|
||||
filegroupEnumerator.Reset();
|
||||
while (filegroupEnumerator.MoveNext())
|
||||
if (this.backup.Action == BackupActionType.Files)
|
||||
{
|
||||
string currentKey = Convert.ToString(filegroupEnumerator.Key,
|
||||
System.Globalization.CultureInfo.InvariantCulture);
|
||||
string currentValue = Convert.ToString(filegroupEnumerator.Value,
|
||||
System.Globalization.CultureInfo.InvariantCulture);
|
||||
if (currentKey.IndexOf(",", StringComparison.Ordinal) < 0)
|
||||
IDictionaryEnumerator filegroupEnumerator = this.backupInfo.SelectedFileGroup.GetEnumerator();
|
||||
filegroupEnumerator.Reset();
|
||||
while (filegroupEnumerator.MoveNext())
|
||||
{
|
||||
// is a file group
|
||||
this.backup.DatabaseFileGroups.Add(currentValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
// is a file
|
||||
int idx = currentValue.IndexOf(".", StringComparison.Ordinal);
|
||||
currentValue = currentValue.Substring(idx + 1, currentValue.Length - idx - 1);
|
||||
this.backup.DatabaseFiles.Add(currentValue);
|
||||
string currentKey = Convert.ToString(filegroupEnumerator.Key,
|
||||
System.Globalization.CultureInfo.InvariantCulture);
|
||||
string currentValue = Convert.ToString(filegroupEnumerator.Value,
|
||||
System.Globalization.CultureInfo.InvariantCulture);
|
||||
if (currentKey.IndexOf(",", StringComparison.Ordinal) < 0)
|
||||
{
|
||||
// is a file group
|
||||
this.backup.DatabaseFileGroups.Add(currentValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
// is a file
|
||||
int idx = currentValue.IndexOf(".", StringComparison.Ordinal);
|
||||
currentValue = currentValue.Substring(idx + 1, currentValue.Length - idx - 1);
|
||||
this.backup.DatabaseFiles.Add(currentValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool isBackupToUrl = false;
|
||||
if (this.backupDeviceType == BackupDeviceType.Url)
|
||||
{
|
||||
isBackupToUrl = true;
|
||||
}
|
||||
this.backup.BackupSetName = this.backupInfo.BackupsetName;
|
||||
|
||||
this.backup.BackupSetName = this.backupInfo.BackupsetName;
|
||||
|
||||
if (false == isBackupToUrl)
|
||||
{
|
||||
for (int i = 0; i < this.backupInfo.BackupPathList.Count; i++)
|
||||
{
|
||||
string destName = Convert.ToString(this.backupInfo.BackupPathList[i], System.Globalization.CultureInfo.InvariantCulture);
|
||||
@@ -205,8 +218,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
GetDeviceType(Convert.ToString(destName,
|
||||
System.Globalization.CultureInfo.InvariantCulture));
|
||||
|
||||
if ((this.backupDeviceType == BackupDeviceType.Disk && backupDeviceType == constDeviceTypeFile)
|
||||
|| (this.backupDeviceType == BackupDeviceType.Tape && backupDeviceType == constDeviceTypeTape))
|
||||
if (this.backupDeviceType == BackupDeviceType.Disk && backupDeviceType == constDeviceTypeFile)
|
||||
{
|
||||
this.backup.Devices.AddDevice(destName, DeviceType.LogicalDevice);
|
||||
}
|
||||
@@ -217,95 +229,104 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
this.backup.Devices.AddDevice(destName, DeviceType.File);
|
||||
}
|
||||
break;
|
||||
case (int)DeviceType.Tape:
|
||||
if (this.backupDeviceType == BackupDeviceType.Tape)
|
||||
{
|
||||
this.backup.Devices.AddDevice(destName, DeviceType.Tape);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
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)
|
||||
if (!string.IsNullOrEmpty(this.backupInfo.MediaName))
|
||||
{
|
||||
throw new Exception(errorMessage);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
if (this.dataContainer.Server.ConnectionContext != null)
|
||||
{
|
||||
// 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 DisasterRecoveryException(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (String s in this.dataContainer.Server.ConnectionContext.CapturedSql.Text)
|
||||
{
|
||||
sb.Append(s);
|
||||
sb.Append(Environment.NewLine);
|
||||
}
|
||||
this.ScriptContent = sb.ToString();
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.dataContainer.Server.ConnectionContext.CapturedSql.Clear();
|
||||
this.dataContainer.Server.ConnectionContext.SqlExecutionModes = oldExecutionMode;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel backup
|
||||
/// </summary>
|
||||
public void CancelBackup()
|
||||
public void Cancel()
|
||||
{
|
||||
if (this.backup != null)
|
||||
{
|
||||
@@ -374,43 +395,37 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
|
||||
private void SetBackupProps()
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (this.backupType)
|
||||
{
|
||||
case BackupType.Full:
|
||||
if (this.backupComponent == BackupComponent.Database)
|
||||
{
|
||||
this.backupActionType = BackupActionType.Database;
|
||||
}
|
||||
else if ((this.backupComponent == BackupComponent.Files) && (null != this.backupInfo.SelectedFileGroup) && (this.backupInfo.SelectedFileGroup.Count > 0))
|
||||
{
|
||||
this.backupActionType = BackupActionType.Files;
|
||||
}
|
||||
this.isBackupIncremental = false;
|
||||
break;
|
||||
case BackupType.Differential:
|
||||
if ((this.backupComponent == BackupComponent.Files) && (0 != this.backupInfo.SelectedFiles.Length))
|
||||
{
|
||||
this.backupActionType = BackupActionType.Files;
|
||||
this.isBackupIncremental = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.backupActionType = BackupActionType.Database;
|
||||
this.isBackupIncremental = true;
|
||||
}
|
||||
break;
|
||||
case BackupType.TransactionLog:
|
||||
this.backupActionType = BackupActionType.Log;
|
||||
this.isBackupIncremental = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch
|
||||
switch (this.backupType)
|
||||
{
|
||||
case BackupType.Full:
|
||||
if (this.backupComponent == BackupComponent.Database)
|
||||
{
|
||||
this.backupActionType = BackupActionType.Database;
|
||||
}
|
||||
else if ((this.backupComponent == BackupComponent.Files) && (this.backupInfo.SelectedFileGroup != null) && (this.backupInfo.SelectedFileGroup.Count > 0))
|
||||
{
|
||||
this.backupActionType = BackupActionType.Files;
|
||||
}
|
||||
this.isBackupIncremental = false;
|
||||
break;
|
||||
case BackupType.Differential:
|
||||
if ((this.backupComponent == BackupComponent.Files) && (this.backupInfo.SelectedFiles != null) && (this.backupInfo.SelectedFiles.Length > 0))
|
||||
{
|
||||
this.backupActionType = BackupActionType.Files;
|
||||
this.isBackupIncremental = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.backupActionType = BackupActionType.Database;
|
||||
this.isBackupIncremental = true;
|
||||
}
|
||||
break;
|
||||
case BackupType.TransactionLog:
|
||||
this.backupActionType = BackupActionType.Log;
|
||||
this.isBackupIncremental = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,20 +448,21 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
{
|
||||
result = Convert.ToInt16(dataset.Tables[0].Rows[0]["BackupDeviceType"],
|
||||
System.Globalization.CultureInfo.InvariantCulture);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return constDeviceTypeMediaSet;
|
||||
result = constDeviceTypeMediaSet;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.serverConnection.SqlExecutionModes = executionMode;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlTools.ServiceLayer.Admin;
|
||||
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.TaskServices;
|
||||
using System.Data.SqlClient;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
@@ -12,7 +13,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
/// <summary>
|
||||
/// Interface for backup operations
|
||||
/// </summary>
|
||||
public interface IBackupOperation
|
||||
public interface IBackupOperation: IScriptableTaskOperation
|
||||
{
|
||||
/// <summary>
|
||||
/// Initialize
|
||||
@@ -33,15 +34,5 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
void SetBackupInput(BackupInfo input);
|
||||
|
||||
/// <summary>
|
||||
/// Execute backup
|
||||
/// </summary>
|
||||
void PerformBackup();
|
||||
|
||||
/// <summary>
|
||||
/// Cancel backup
|
||||
/// </summary>
|
||||
void CancelBackup();
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,17 @@ using Microsoft.SqlTools.ServiceLayer.Admin.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Response class which returns backup configuration information
|
||||
/// </summary>
|
||||
public class BackupConfigInfoResponse
|
||||
{
|
||||
public BackupConfigInfo BackupConfigInfo { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request class to get backup configuration information
|
||||
/// </summary>
|
||||
public class BackupConfigInfoRequest
|
||||
{
|
||||
public static readonly
|
||||
|
||||
@@ -7,13 +7,30 @@ using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Backup parameters passed for execution and scripting
|
||||
/// </summary>
|
||||
public class BackupParams
|
||||
{
|
||||
/// <summary>
|
||||
/// Connection uri
|
||||
/// </summary>
|
||||
public string OwnerUri { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Backup metrics selected from the UI
|
||||
/// </summary>
|
||||
public BackupInfo BackupInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True for generating script, false for execution
|
||||
/// </summary>
|
||||
public bool IsScripting { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Response class for backup execution
|
||||
/// </summary>
|
||||
public class BackupResponse
|
||||
{
|
||||
public bool Result { get; set; }
|
||||
@@ -21,6 +38,9 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts
|
||||
public int TaskId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request class for backup execution
|
||||
/// </summary>
|
||||
public class BackupRequest
|
||||
{
|
||||
public static readonly
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Exception raised from disaster recovery operations
|
||||
/// </summary>
|
||||
internal sealed class DisasterRecoveryException : Exception
|
||||
{
|
||||
internal DisasterRecoveryException() : base()
|
||||
{
|
||||
}
|
||||
|
||||
internal DisasterRecoveryException(string m) : base(m)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.TaskServices;
|
||||
using System.Threading;
|
||||
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
{
|
||||
@@ -23,6 +24,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
{
|
||||
private static readonly Lazy<DisasterRecoveryService> instance = new Lazy<DisasterRecoveryService>(() => new DisasterRecoveryService());
|
||||
private static ConnectionService connectionService = null;
|
||||
private SqlTaskManager sqlTaskManagerInstance = null;
|
||||
private RestoreDatabaseHelper restoreDatabaseService = new RestoreDatabaseHelper();
|
||||
|
||||
/// <summary>
|
||||
@@ -59,6 +61,22 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
}
|
||||
}
|
||||
|
||||
private SqlTaskManager SqlTaskManagerInstance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (sqlTaskManagerInstance == null)
|
||||
{
|
||||
sqlTaskManagerInstance = SqlTaskManager.Instance;
|
||||
}
|
||||
return sqlTaskManagerInstance;
|
||||
}
|
||||
set
|
||||
{
|
||||
sqlTaskManagerInstance = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the service instance
|
||||
/// </summary>
|
||||
@@ -66,6 +84,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
{
|
||||
// Get database info
|
||||
serviceHost.SetRequestHandler(BackupConfigInfoRequest.Type, HandleBackupConfigInfoRequest);
|
||||
|
||||
// Create backup
|
||||
serviceHost.SetRequestHandler(BackupRequest.Type, HandleBackupRequest);
|
||||
|
||||
@@ -85,7 +104,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
/// <param name="optionsParams"></param>
|
||||
/// <param name="requestContext"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task HandleBackupConfigInfoRequest(
|
||||
internal async Task HandleBackupConfigInfoRequest(
|
||||
DefaultDatabaseInfoParams optionsParams,
|
||||
RequestContext<BackupConfigInfoResponse> requestContext)
|
||||
{
|
||||
@@ -103,7 +122,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
SqlConnection sqlConn = GetSqlConnection(connInfo);
|
||||
if ((sqlConn != null) && !connInfo.IsSqlDW && !connInfo.IsAzure)
|
||||
{
|
||||
BackupConfigInfo backupConfigInfo = DisasterRecoveryService.Instance.GetBackupConfigInfo(helper.DataContainer, sqlConn, sqlConn.Database);
|
||||
BackupConfigInfo backupConfigInfo = this.GetBackupConfigInfo(helper.DataContainer, sqlConn, sqlConn.Database);
|
||||
backupConfigInfo.DatabaseInfo = AdminService.GetDatabaseInfo(connInfo);
|
||||
response.BackupConfigInfo = backupConfigInfo;
|
||||
}
|
||||
@@ -213,7 +232,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
metadata.Data = restoreDataObject;
|
||||
|
||||
// create restore task and perform
|
||||
SqlTask sqlTask = SqlTaskManager.Instance.CreateAndRun(metadata, this.restoreDatabaseService.RestoreTaskAsync, restoreDatabaseService.CancelTaskAsync);
|
||||
SqlTask sqlTask = SqlTaskManagerInstance.CreateAndRun(metadata, this.restoreDatabaseService.RestoreTaskAsync, restoreDatabaseService.CancelTaskAsync);
|
||||
response.TaskId = sqlTask.TaskId.ToString();
|
||||
}
|
||||
else
|
||||
@@ -244,40 +263,50 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
/// <summary>
|
||||
/// Handles a backup request
|
||||
/// </summary>
|
||||
internal static async Task HandleBackupRequest(
|
||||
internal async Task HandleBackupRequest(
|
||||
BackupParams backupParams,
|
||||
RequestContext<BackupResponse> requestContext)
|
||||
{
|
||||
{
|
||||
try
|
||||
{
|
||||
{
|
||||
BackupResponse response = new BackupResponse();
|
||||
ConnectionInfo connInfo;
|
||||
DisasterRecoveryService.ConnectionServiceInstance.TryFindConnection(
|
||||
backupParams.OwnerUri,
|
||||
out connInfo);
|
||||
bool supported = IsBackupRestoreOperationSupported(backupParams.OwnerUri, out connInfo);
|
||||
|
||||
if (connInfo != null)
|
||||
if (supported && connInfo != null)
|
||||
{
|
||||
DatabaseTaskHelper helper = AdminService.CreateDatabaseTaskHelper(connInfo, databaseExists: true);
|
||||
SqlConnection sqlConn = GetSqlConnection(connInfo);
|
||||
if ((sqlConn != null) && !connInfo.IsSqlDW && !connInfo.IsAzure)
|
||||
{
|
||||
BackupOperation backupOperation = DisasterRecoveryService.Instance.SetBackupInput(helper.DataContainer, sqlConn, backupParams.BackupInfo);
|
||||
|
||||
// create task metadata
|
||||
TaskMetadata metadata = new TaskMetadata();
|
||||
metadata.ServerName = connInfo.ConnectionDetails.ServerName;
|
||||
metadata.DatabaseName = connInfo.ConnectionDetails.DatabaseName;
|
||||
metadata.Name = SR.Backup_TaskName;
|
||||
metadata.IsCancelable = true;
|
||||
metadata.Data = backupOperation;
|
||||
|
||||
// create backup task and perform
|
||||
SqlTask sqlTask = SqlTaskManager.Instance.CreateTask(metadata, Instance.BackupTaskAsync);
|
||||
sqlTask.Run();
|
||||
BackupOperation backupOperation = CreateBackupOperation(helper.DataContainer, sqlConn, backupParams.BackupInfo);
|
||||
SqlTask sqlTask = null;
|
||||
|
||||
// create task metadata
|
||||
TaskMetadata metadata = new TaskMetadata();
|
||||
metadata.ServerName = connInfo.ConnectionDetails.ServerName;
|
||||
metadata.DatabaseName = connInfo.ConnectionDetails.DatabaseName;
|
||||
metadata.Data = backupOperation;
|
||||
metadata.IsCancelable = true;
|
||||
|
||||
if (backupParams.IsScripting)
|
||||
{
|
||||
metadata.Name = string.Format("{0} {1}", SR.BackupTaskName, SR.ScriptTaskName);
|
||||
metadata.TaskExecutionMode = TaskExecutionMode.Script;
|
||||
}
|
||||
else
|
||||
{
|
||||
metadata.Name = SR.BackupTaskName;
|
||||
metadata.TaskExecutionMode = TaskExecutionMode.ExecuteAndScript;
|
||||
}
|
||||
|
||||
sqlTask = SqlTaskManagerInstance.CreateAndRun(metadata, this.PerformBackupTaskAsync, this.CancelBackupTaskAsync);
|
||||
}
|
||||
|
||||
await requestContext.SendResult(new BackupResponse());
|
||||
else
|
||||
{
|
||||
response.Result = false;
|
||||
}
|
||||
|
||||
await requestContext.SendResult(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -342,128 +371,123 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
return false;
|
||||
}
|
||||
|
||||
internal BackupConfigInfo GetBackupConfigInfo(CDataContainer dataContainer, SqlConnection sqlConnection, string databaseName)
|
||||
private BackupOperation CreateBackupOperation(CDataContainer dataContainer, SqlConnection sqlConnection)
|
||||
{
|
||||
BackupOperation backupOperation = new BackupOperation();
|
||||
backupOperation.Initialize(dataContainer, sqlConnection);
|
||||
return backupOperation.CreateBackupConfigInfo(databaseName);
|
||||
return backupOperation;
|
||||
}
|
||||
|
||||
internal BackupOperation SetBackupInput(CDataContainer dataContainer, SqlConnection sqlConnection, BackupInfo input)
|
||||
internal BackupOperation CreateBackupOperation(CDataContainer dataContainer, SqlConnection sqlConnection, BackupInfo input)
|
||||
{
|
||||
BackupOperation backupOperation = new BackupOperation();
|
||||
backupOperation.Initialize(dataContainer, sqlConnection);
|
||||
BackupOperation backupOperation = CreateBackupOperation(dataContainer, sqlConnection);
|
||||
backupOperation.SetBackupInput(input);
|
||||
return backupOperation;
|
||||
}
|
||||
|
||||
internal BackupConfigInfo GetBackupConfigInfo(CDataContainer dataContainer, SqlConnection sqlConnection, string databaseName)
|
||||
{
|
||||
BackupOperation backupOperation = CreateBackupOperation(dataContainer, sqlConnection);
|
||||
return backupOperation.CreateBackupConfigInfo(databaseName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For testing purpose only
|
||||
/// </summary>
|
||||
internal void PerformBackup(BackupOperation backupOperation)
|
||||
{
|
||||
backupOperation.PerformBackup();
|
||||
backupOperation.Execute(TaskExecutionMode.ExecuteAndScript);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create a backup task for execution and cancellation
|
||||
/// For testing purpose only
|
||||
/// </summary>
|
||||
/// <param name="sqlTask"></param>
|
||||
/// <returns></returns>
|
||||
internal async Task<TaskResult> BackupTaskAsync(SqlTask sqlTask)
|
||||
internal void ScriptBackup(BackupOperation backupOperation)
|
||||
{
|
||||
sqlTask.AddMessage(SR.Task_InProgress, SqlTaskStatus.InProgress, true);
|
||||
IBackupOperation backupOperation = sqlTask.TaskMetadata.Data as IBackupOperation;
|
||||
TaskResult taskResult = null;
|
||||
|
||||
if (backupOperation != null)
|
||||
{
|
||||
AutoResetEvent backupCompletedEvent = new AutoResetEvent(initialState: false);
|
||||
Task<TaskResult> performTask = PerformTaskAsync(backupOperation);
|
||||
Task<TaskResult> cancelTask = CancelTaskAsync(backupOperation, sqlTask.TokenSource.Token, backupCompletedEvent);
|
||||
Task<TaskResult> completedTask = await Task.WhenAny(performTask, cancelTask);
|
||||
|
||||
// Release the cancelTask
|
||||
if (completedTask == performTask)
|
||||
{
|
||||
backupCompletedEvent.Set();
|
||||
}
|
||||
|
||||
sqlTask.AddMessage(completedTask.Result.TaskStatus == SqlTaskStatus.Failed ? completedTask.Result.ErrorMessage : SR.Task_Completed,
|
||||
completedTask.Result.TaskStatus);
|
||||
taskResult = completedTask.Result;
|
||||
}
|
||||
else
|
||||
{
|
||||
taskResult = new TaskResult();
|
||||
taskResult.TaskStatus = SqlTaskStatus.Failed;
|
||||
}
|
||||
|
||||
return taskResult;
|
||||
backupOperation.Execute(TaskExecutionMode.Script);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Async task to execute backup
|
||||
/// </summary>
|
||||
/// <param name="backupOperation"></param>
|
||||
/// <param name="sqlTask"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<TaskResult> PerformTaskAsync(IBackupOperation backupOperation)
|
||||
internal async Task<TaskResult> PerformBackupTaskAsync(SqlTask sqlTask)
|
||||
{
|
||||
IBackupOperation backupOperation = sqlTask.TaskMetadata.Data as IBackupOperation;
|
||||
TaskResult result = new TaskResult();
|
||||
|
||||
// Create a task to perform backup
|
||||
return await Task.Factory.StartNew(() =>
|
||||
await Task.Factory.StartNew(() =>
|
||||
{
|
||||
TaskResult result = new TaskResult();
|
||||
try
|
||||
if (backupOperation != null)
|
||||
{
|
||||
backupOperation.PerformBackup();
|
||||
result.TaskStatus = SqlTaskStatus.Succeeded;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.TaskStatus = SqlTaskStatus.Failed;
|
||||
result.ErrorMessage = ex.Message;
|
||||
if (ex.InnerException != null)
|
||||
try
|
||||
{
|
||||
result.ErrorMessage += System.Environment.NewLine + ex.InnerException.Message;
|
||||
sqlTask.AddMessage(SR.TaskInProgress, SqlTaskStatus.InProgress, true);
|
||||
|
||||
// Execute backup
|
||||
backupOperation.Execute(sqlTask.TaskMetadata.TaskExecutionMode);
|
||||
|
||||
// Set result
|
||||
result.TaskStatus = SqlTaskStatus.Succeeded;
|
||||
|
||||
// Send generated script to client
|
||||
if (!String.IsNullOrEmpty(backupOperation.ScriptContent))
|
||||
{
|
||||
sqlTask.AddScript(result.TaskStatus, backupOperation.ScriptContent);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.TaskStatus = SqlTaskStatus.Failed;
|
||||
result.ErrorMessage = string.Format(CultureInfo.InvariantCulture, "error:{0} inner:{1} stacktrace:{2}",
|
||||
ex.Message,
|
||||
ex.InnerException != null ? ex.InnerException.Message : "",
|
||||
ex.StackTrace);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
else
|
||||
{
|
||||
result.TaskStatus = SqlTaskStatus.Failed;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Async task to cancel backup
|
||||
/// </summary>
|
||||
/// <param name="backupOperation"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <param name="backupCompletedEvent"></param>
|
||||
/// <param name="sqlTask"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<TaskResult> CancelTaskAsync(IBackupOperation backupOperation, CancellationToken token, AutoResetEvent backupCompletedEvent)
|
||||
internal async Task<TaskResult> CancelBackupTaskAsync(SqlTask sqlTask)
|
||||
{
|
||||
// Create a task for backup cancellation request
|
||||
return await Task.Factory.StartNew(() =>
|
||||
{
|
||||
TaskResult result = new TaskResult();
|
||||
WaitHandle[] waitHandles = new WaitHandle[2]
|
||||
{
|
||||
backupCompletedEvent,
|
||||
token.WaitHandle
|
||||
};
|
||||
IBackupOperation backupOperation = sqlTask.TaskMetadata.Data as IBackupOperation;
|
||||
TaskResult result = new TaskResult();
|
||||
|
||||
WaitHandle.WaitAny(waitHandles);
|
||||
try
|
||||
await Task.Factory.StartNew(() =>
|
||||
{
|
||||
if (backupOperation != null)
|
||||
{
|
||||
backupOperation.CancelBackup();
|
||||
result.TaskStatus = SqlTaskStatus.Canceled;
|
||||
try
|
||||
{
|
||||
backupOperation.Cancel();
|
||||
result.TaskStatus = SqlTaskStatus.Canceled;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.TaskStatus = SqlTaskStatus.Failed;
|
||||
result.ErrorMessage = ex.Message;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
else
|
||||
{
|
||||
result.TaskStatus = SqlTaskStatus.Failed;
|
||||
result.ErrorMessage = ex.Message;
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
||||
/// <returns></returns>
|
||||
internal async Task<TaskResult> RestoreTaskAsync(SqlTask sqlTask)
|
||||
{
|
||||
sqlTask.AddMessage(SR.Task_InProgress, SqlTaskStatus.InProgress, true);
|
||||
sqlTask.AddMessage(SR.TaskInProgress, SqlTaskStatus.InProgress, true);
|
||||
RestoreDatabaseTaskDataObject restoreDataObject = sqlTask.TaskMetadata.Data as RestoreDatabaseTaskDataObject;
|
||||
TaskResult taskResult = null;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user