diff --git a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/BackupOperation/BackupOperation.cs b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/BackupOperation/BackupOperation.cs index ca3c5127..645d4be8 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/BackupOperation/BackupOperation.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/BackupOperation/BackupOperation.cs @@ -13,8 +13,6 @@ 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; -using System.Text; using Microsoft.SqlTools.ServiceLayer.TaskServices; namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery @@ -22,13 +20,12 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery /// /// This class implements backup operations /// - public class BackupOperation : IBackupOperation + public class BackupOperation : SmoScriptableTaskOperation, IBackupOperation { private CDataContainer dataContainer; private ServerConnection serverConnection; private CommonUtilities backupRestoreUtil = null; private Backup backup = null; - private string scriptContent = ""; /// /// Constants @@ -151,22 +148,10 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery return configInfo; } - public string ScriptContent - { - get - { - return this.scriptContent; - } - set - { - this.scriptContent = value; - } - } - /// /// The error occurred during backup operation /// - public string ErrorMessage + public override string ErrorMessage { get { @@ -174,17 +159,19 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery } } - public SqlTask SqlTask { get; set; } + public override Server Server + { + get + { + return this.dataContainer.Server; + } + } /// /// Execute backup /// - public void Execute(TaskExecutionMode mode) + public override void Execute() { - 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; @@ -322,24 +309,18 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery } } - foreach (String s in this.dataContainer.Server.ConnectionContext.CapturedSql.Text) - { - sb.Append(s); - sb.Append(Environment.NewLine); - } - this.ScriptContent = sb.ToString(); + } - finally + catch(Exception) { - this.dataContainer.Server.ConnectionContext.CapturedSql.Clear(); - this.dataContainer.Server.ConnectionContext.SqlExecutionModes = oldExecutionMode; + throw; } } /// /// Cancel backup /// - public void Cancel() + public override void Cancel() { if (this.backup != null) { diff --git a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Contracts/BackupRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Contracts/BackupRequest.cs index 6a6ab212..dd88524f 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Contracts/BackupRequest.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Contracts/BackupRequest.cs @@ -4,13 +4,15 @@ // using Microsoft.SqlTools.Hosting.Protocol.Contracts; +using Microsoft.SqlTools.ServiceLayer.TaskServices; +using Microsoft.SqlTools.ServiceLayer.Utility; namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts { /// /// Backup parameters passed for execution and scripting /// - public class BackupParams + public class BackupParams : IScriptableRequestParams { /// /// Connection uri @@ -23,9 +25,9 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts public BackupInfo BackupInfo { get; set; } /// - /// True for generating script, false for execution + /// /// - public bool IsScripting { get; set; } + public TaskExecutionMode TaskExecutionMode { get; set; } } /// diff --git a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Contracts/RestoreRequestParams.cs b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Contracts/RestoreRequestParams.cs index de32a85d..f66e6532 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Contracts/RestoreRequestParams.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/Contracts/RestoreRequestParams.cs @@ -147,20 +147,6 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts /// public TaskExecutionMode TaskExecutionMode { get; set; } - /// - /// Same as Target Database name. Used by task manager to create task info - /// - public string DatabaseName - { - get - { - return TargetDatabaseName; - } - set - { - TargetDatabaseName = value; - } - } } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/DisasterRecoveryService.cs b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/DisasterRecoveryService.cs index 9d5d9403..6c14f285 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/DisasterRecoveryService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/DisasterRecoveryService.cs @@ -225,6 +225,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery { // create task metadata TaskMetadata metadata = TaskMetadata.Create(restoreParams, SR.RestoreTaskName, restoreDataObject, ConnectionServiceInstance); + metadata.DatabaseName = restoreParams.TargetDatabaseName; // create restore task and perform SqlTask sqlTask = SqlTaskManagerInstance.CreateAndRun(metadata); @@ -277,23 +278,9 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery SqlTask sqlTask = null; // create task metadata - TaskMetadata metadata = new TaskMetadata(); - metadata.ServerName = connInfo.ConnectionDetails.ServerName; - metadata.DatabaseName = connInfo.ConnectionDetails.DatabaseName; - metadata.TaskOperation = backupOperation; - - 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); + TaskMetadata metadata = TaskMetadata.Create(backupParams, SR.BackupTaskName, backupOperation, ConnectionServiceInstance); + + sqlTask = SqlTaskManagerInstance.CreateAndRun(metadata); } else { @@ -400,88 +387,5 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery { backupOperation.Execute(TaskExecutionMode.Script); } - - /// - /// Async task to execute backup - /// - /// - /// - internal async Task PerformBackupTaskAsync(SqlTask sqlTask) - { - IBackupOperation backupOperation = sqlTask.TaskMetadata.TaskOperation as IBackupOperation; - TaskResult result = new TaskResult(); - - // Create a task to perform backup - await Task.Factory.StartNew(() => - { - if (backupOperation != null) - { - try - { - 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); - } - } - else - { - result.TaskStatus = SqlTaskStatus.Failed; - } - }); - - return result; - } - - /// - /// Async task to cancel backup - /// - /// - /// - internal async Task CancelBackupTaskAsync(SqlTask sqlTask) - { - IBackupOperation backupOperation = sqlTask.TaskMetadata.TaskOperation as IBackupOperation; - TaskResult result = new TaskResult(); - - await Task.Factory.StartNew(() => - { - if (backupOperation != null) - { - try - { - backupOperation.Cancel(); - result.TaskStatus = SqlTaskStatus.Canceled; - } - catch (Exception ex) - { - result.TaskStatus = SqlTaskStatus.Failed; - result.ErrorMessage = ex.Message; - } - } - else - { - result.TaskStatus = SqlTaskStatus.Failed; - } - }); - - return result; - } } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/RestoreOperation/RestoreDatabaseTaskDataObject.cs b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/RestoreOperation/RestoreDatabaseTaskDataObject.cs index a12b6d6a..f29cb898 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/RestoreOperation/RestoreDatabaseTaskDataObject.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/RestoreOperation/RestoreDatabaseTaskDataObject.cs @@ -57,6 +57,8 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation string DefaultTargetDbName { get; } string TargetDatabaseName { get; set; } + bool CanDropExistingConnections { get; } + } /// @@ -247,7 +249,6 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation lastBackup = isTheLastOneSelected ? string.Format(CultureInfo.CurrentCulture, SR.TheLastBackupTaken, (backupTimeStr)) : backupTimeStr; } - //TODO: find the selected one else if (GetFirstSelectedBackupSetIndex() == 0 && !this.RestorePlanner.RestoreToLastBackup) { lastBackup = this.CurrentRestorePointInTime.Value.ToLongDateString() + @@ -1242,6 +1243,21 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation return true; } + /// + /// Returns true if can close eixisting connections for give database + /// + public bool CanDropExistingConnections + { + get + { + if (RestorePlan != null && RestorePlanner != null) + { + return RestorePlan.CanDropExistingConnections(RestorePlanner.DatabaseName); + } + return false; + } + } + /// /// Cancels the restore operations /// diff --git a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/RestoreOperation/RestoreOptionFactory.cs b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/RestoreOperation/RestoreOptionFactory.cs index 2ac1ffb2..54951cac 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/RestoreOperation/RestoreOptionFactory.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/RestoreOperation/RestoreOptionFactory.cs @@ -482,8 +482,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation { return new OptionValidationResult() { - //TODO: make the method public in SMO bool canDropExistingConnections = restoreDataObject.RestorePlan.CanDropExistingConnections(this.Data.RestorePlanner.DatabaseName); - IsReadOnly = false + IsReadOnly = !restoreDataObject.CanDropExistingConnections }; }, SetValueFunction = (IRestoreDatabaseTaskDataObject restoreDataObject, object value) => diff --git a/src/Microsoft.SqlTools.ServiceLayer/TaskServices/TaskMetadata.cs b/src/Microsoft.SqlTools.ServiceLayer/TaskServices/TaskMetadata.cs index 26ebcb4b..6ec143a5 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/TaskServices/TaskMetadata.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/TaskServices/TaskMetadata.cs @@ -67,11 +67,7 @@ namespace Microsoft.SqlTools.ServiceLayer.TaskServices taskMetadata.ServerName = connInfo.ConnectionDetails.ServerName; } - if (!string.IsNullOrEmpty(requestParam.DatabaseName)) - { - taskMetadata.DatabaseName = requestParam.DatabaseName; - } - else if (connInfo != null) + if (connInfo != null) { taskMetadata.DatabaseName = connInfo.ConnectionDetails.DatabaseName; } diff --git a/src/Microsoft.SqlTools.ServiceLayer/Utility/IRequestParams.cs b/src/Microsoft.SqlTools.ServiceLayer/Utility/IRequestParams.cs index 183acb13..c36bc493 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Utility/IRequestParams.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Utility/IRequestParams.cs @@ -12,10 +12,5 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility /// The Uri to find the connection to do the restore operations /// string OwnerUri { get; set; } - - /// - /// Database name - /// - string DatabaseName { get; set; } } } diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DisasterRecovery/RestoreDatabaseServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DisasterRecovery/RestoreDatabaseServiceTests.cs index 50552d4e..6f6ed1a6 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DisasterRecovery/RestoreDatabaseServiceTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DisasterRecovery/RestoreDatabaseServiceTests.cs @@ -676,7 +676,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery { OwnerUri = liveConnection.ConnectionInfo.OwnerUri, BackupInfo = backupInfo, - IsScripting = false + TaskExecutionMode = TaskExecutionMode.Execute }; // Backup the database diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/DisasterRecovery/BackupTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/DisasterRecovery/BackupTests.cs index 636ed87a..af74c789 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/DisasterRecovery/BackupTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/DisasterRecovery/BackupTests.cs @@ -25,7 +25,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery DisasterRecoveryService service = new DisasterRecoveryService(); var mockBackupOperation = new Mock(); TaskMetadata taskMetaData = this.CreateTaskMetaData(mockBackupOperation.Object); - SqlTask sqlTask = manager.CreateTask(taskMetaData, service.PerformBackupTaskAsync, service.CancelBackupTaskAsync); + SqlTask sqlTask = manager.CreateTask(taskMetaData); Assert.NotNull(sqlTask); Task taskToVerify = sqlTask.RunAsync().ContinueWith(Task => { @@ -50,7 +50,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery TaskMetadata taskMetaData = this.CreateTaskMetaData(mockBackupOperation.Object); taskMetaData.TaskExecutionMode = TaskExecutionMode.Script; - SqlTask sqlTask = manager.CreateTask(taskMetaData, service.PerformBackupTaskAsync); + SqlTask sqlTask = manager.CreateTask(taskMetaData); Assert.NotNull(sqlTask); Task taskToVerify = sqlTask.RunAsync().ContinueWith(Task => { @@ -74,8 +74,8 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery var mockBackupOperation = new Mock(); TaskMetadata taskMetaData = this.CreateTaskMetaData(mockBackupOperation.Object); - SqlTask sqlTask = manager.CreateTask(taskMetaData, service.PerformBackupTaskAsync, service.CancelBackupTaskAsync); - SqlTask sqlTask2 = manager.CreateTask(taskMetaData, service.PerformBackupTaskAsync, service.CancelBackupTaskAsync); + SqlTask sqlTask = manager.CreateTask(taskMetaData); + SqlTask sqlTask2 = manager.CreateTask(taskMetaData); Assert.NotNull(sqlTask); Assert.NotNull(sqlTask2); @@ -105,7 +105,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery IBackupOperation backupOperation = new BackupOperationStub(); DisasterRecoveryService service = new DisasterRecoveryService(); TaskMetadata taskMetaData = this.CreateTaskMetaData(backupOperation); - SqlTask sqlTask = manager.CreateTask(taskMetaData, service.PerformBackupTaskAsync, service.CancelBackupTaskAsync); + SqlTask sqlTask = manager.CreateTask(taskMetaData); Assert.NotNull(sqlTask); Task taskToVerify = sqlTask.RunAsync().ContinueWith(Task => { @@ -135,8 +135,8 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery TaskMetadata taskMetaData = this.CreateTaskMetaData(backupOperation); TaskMetadata taskMetaData2 = this.CreateTaskMetaData(backupOperation2); - SqlTask sqlTask = manager.CreateTask(taskMetaData, service.PerformBackupTaskAsync, service.CancelBackupTaskAsync); - SqlTask sqlTask2 = manager.CreateTask(taskMetaData2, service.PerformBackupTaskAsync, service.CancelBackupTaskAsync); + SqlTask sqlTask = manager.CreateTask(taskMetaData); + SqlTask sqlTask2 = manager.CreateTask(taskMetaData2); Assert.NotNull(sqlTask); Assert.NotNull(sqlTask2); @@ -174,11 +174,11 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery DisasterRecoveryService service = new DisasterRecoveryService(); IBackupOperation backupOperation = new BackupOperationStub(); TaskMetadata taskMetaData = this.CreateTaskMetaData(backupOperation); - SqlTask sqlTask = manager.CreateTask(taskMetaData, service.PerformBackupTaskAsync, service.CancelBackupTaskAsync); + SqlTask sqlTask = manager.CreateTask(taskMetaData); var mockBackupOperation = new Mock(); TaskMetadata taskMetaData2 = this.CreateTaskMetaData(mockBackupOperation.Object); - SqlTask sqlTask2 = manager.CreateTask(taskMetaData, service.PerformBackupTaskAsync, service.CancelBackupTaskAsync); + SqlTask sqlTask2 = manager.CreateTask(taskMetaData); Assert.NotNull(sqlTask); Assert.NotNull(sqlTask2); diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/DisasterRecovery/RestoreDatabaseTaskDataObjectStub.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/DisasterRecovery/RestoreDatabaseTaskDataObjectStub.cs index ddc22c94..de906eb3 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/DisasterRecovery/RestoreDatabaseTaskDataObjectStub.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/DisasterRecovery/RestoreDatabaseTaskDataObjectStub.cs @@ -52,5 +52,6 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery public string DefaultTargetDbName { get; set; } public string TargetDatabaseName { get; set; } + public bool CanDropExistingConnections { get; set; } } } diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/DisasterRecovery/RestoreOptionsHelperTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/DisasterRecovery/RestoreOptionsHelperTests.cs index 277fa209..d035285c 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/DisasterRecovery/RestoreOptionsHelperTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/DisasterRecovery/RestoreOptionsHelperTests.cs @@ -135,6 +135,30 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery Assert.False(result[RestoreOptionsHelper.StandbyFile].IsReadOnly); } + [Fact] + public void CloseExistingConnectionsShouldNotBeReadOnlyGivenCanDropExistingConnectionsSetToTrue() + { + GeneralRequestDetails optionValues = CreateOptionsTestData(); + optionValues.Options["CanDropExistingConnections"] = true; + IRestoreDatabaseTaskDataObject restoreDatabaseTaskDataObject = CreateRestoreDatabaseTaskDataObject(optionValues); + + Dictionary result = RestoreOptionsHelper.CreateRestorePlanOptions(restoreDatabaseTaskDataObject); + Assert.NotNull(result); + Assert.False(result[RestoreOptionsHelper.CloseExistingConnections].IsReadOnly); + } + + [Fact] + public void CloseExistingConnectionsShouldBeReadOnlyGivenCanDropExistingConnectionsSetToFalse() + { + GeneralRequestDetails optionValues = CreateOptionsTestData(); + optionValues.Options["CanDropExistingConnections"] = false; + IRestoreDatabaseTaskDataObject restoreDatabaseTaskDataObject = CreateRestoreDatabaseTaskDataObject(optionValues); + + Dictionary result = RestoreOptionsHelper.CreateRestorePlanOptions(restoreDatabaseTaskDataObject); + Assert.NotNull(result); + Assert.True(result[RestoreOptionsHelper.CloseExistingConnections].IsReadOnly); + } + [Fact] public void KeepReplicationShouldNotBeReadOnlyGivenRecoveryStateWithNoRecovery() { @@ -288,6 +312,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery optionValues.Options.Add("DefaultSourceDbName", "DefaultSourceDbName"); optionValues.Options.Add("DefaultTargetDbName", "DefaultTargetDbName"); optionValues.Options.Add("SourceDbNames", new List()); + optionValues.Options.Add("CanDropExistingConnections", true); return optionValues; } @@ -314,6 +339,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery restoreDataObject.SourceDbNames = optionValues.GetOptionValue>("SourceDbNames"); restoreDataObject.DefaultTargetDbName = optionValues.GetOptionValue("DefaultTargetDbName"); restoreDataObject.BackupTailLog = optionValues.GetOptionValue(RestoreOptionsHelper.BackupTailLog); + restoreDataObject.CanDropExistingConnections = optionValues.GetOptionValue("CanDropExistingConnections"); restoreDataObject.RestoreParams = optionValues as RestoreParams; restoreDataObject.RestorePlan = null; RestoreOptions restoreOptions = new RestoreOptions();