integrate backup operation with new scriptable task (#440)

* integrate backup operation with new scriptable task
This commit is contained in:
Leila Lali
2017-08-21 15:04:48 -07:00
committed by GitHub
parent 1511f73672
commit d94dda4282
12 changed files with 79 additions and 173 deletions

View File

@@ -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
/// <summary>
/// This class implements backup operations
/// </summary>
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 = "";
/// <summary>
/// Constants
@@ -151,22 +148,10 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
return configInfo;
}
public string ScriptContent
{
get
{
return this.scriptContent;
}
set
{
this.scriptContent = value;
}
}
/// <summary>
/// The error occurred during backup operation
/// </summary>
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;
}
}
/// <summary>
/// Execute backup
/// </summary>
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;
}
}
/// <summary>
/// Cancel backup
/// </summary>
public void Cancel()
public override void Cancel()
{
if (this.backup != null)
{

View File

@@ -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
{
/// <summary>
/// Backup parameters passed for execution and scripting
/// </summary>
public class BackupParams
public class BackupParams : IScriptableRequestParams
{
/// <summary>
/// Connection uri
@@ -23,9 +25,9 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts
public BackupInfo BackupInfo { get; set; }
/// <summary>
/// True for generating script, false for execution
///
/// </summary>
public bool IsScripting { get; set; }
public TaskExecutionMode TaskExecutionMode { get; set; }
}
/// <summary>

View File

@@ -147,20 +147,6 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts
/// </summary>
public TaskExecutionMode TaskExecutionMode { get; set; }
/// <summary>
/// Same as Target Database name. Used by task manager to create task info
/// </summary>
public string DatabaseName
{
get
{
return TargetDatabaseName;
}
set
{
TargetDatabaseName = value;
}
}
}
}

View File

@@ -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<SqlTask>(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<SqlTask>(metadata);
}
else
{
@@ -400,88 +387,5 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
{
backupOperation.Execute(TaskExecutionMode.Script);
}
/// <summary>
/// Async task to execute backup
/// </summary>
/// <param name="sqlTask"></param>
/// <returns></returns>
internal async Task<TaskResult> 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;
}
/// <summary>
/// Async task to cancel backup
/// </summary>
/// <param name="sqlTask"></param>
/// <returns></returns>
internal async Task<TaskResult> 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;
}
}
}

View File

@@ -57,6 +57,8 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
string DefaultTargetDbName { get; }
string TargetDatabaseName { get; set; }
bool CanDropExistingConnections { get; }
}
/// <summary>
@@ -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;
}
/// <summary>
/// Returns true if can close eixisting connections for give database
/// </summary>
public bool CanDropExistingConnections
{
get
{
if (RestorePlan != null && RestorePlanner != null)
{
return RestorePlan.CanDropExistingConnections(RestorePlanner.DatabaseName);
}
return false;
}
}
/// <summary>
/// Cancels the restore operations
/// </summary>

View File

@@ -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) =>

View File

@@ -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;
}

View File

@@ -12,10 +12,5 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility
/// The Uri to find the connection to do the restore operations
/// </summary>
string OwnerUri { get; set; }
/// <summary>
/// Database name
/// </summary>
string DatabaseName { get; set; }
}
}

View File

@@ -676,7 +676,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
{
OwnerUri = liveConnection.ConnectionInfo.OwnerUri,
BackupInfo = backupInfo,
IsScripting = false
TaskExecutionMode = TaskExecutionMode.Execute
};
// Backup the database

View File

@@ -25,7 +25,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery
DisasterRecoveryService service = new DisasterRecoveryService();
var mockBackupOperation = new Mock<IBackupOperation>();
TaskMetadata taskMetaData = this.CreateTaskMetaData(mockBackupOperation.Object);
SqlTask sqlTask = manager.CreateTask(taskMetaData, service.PerformBackupTaskAsync, service.CancelBackupTaskAsync);
SqlTask sqlTask = manager.CreateTask<SqlTask>(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<SqlTask>(taskMetaData);
Assert.NotNull(sqlTask);
Task taskToVerify = sqlTask.RunAsync().ContinueWith(Task =>
{
@@ -74,8 +74,8 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery
var mockBackupOperation = new Mock<IBackupOperation>();
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<SqlTask>(taskMetaData);
SqlTask sqlTask2 = manager.CreateTask<SqlTask>(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<SqlTask>(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<SqlTask>(taskMetaData);
SqlTask sqlTask2 = manager.CreateTask<SqlTask>(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<SqlTask>(taskMetaData);
var mockBackupOperation = new Mock<IBackupOperation>();
TaskMetadata taskMetaData2 = this.CreateTaskMetaData(mockBackupOperation.Object);
SqlTask sqlTask2 = manager.CreateTask(taskMetaData, service.PerformBackupTaskAsync, service.CancelBackupTaskAsync);
SqlTask sqlTask2 = manager.CreateTask<SqlTask>(taskMetaData);
Assert.NotNull(sqlTask);
Assert.NotNull(sqlTask2);

View File

@@ -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; }
}
}

View File

@@ -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<string, RestorePlanDetailInfo> 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<string, RestorePlanDetailInfo> 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<string>());
optionValues.Options.Add("CanDropExistingConnections", true);
return optionValues;
}
@@ -314,6 +339,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery
restoreDataObject.SourceDbNames = optionValues.GetOptionValue<List<string>>("SourceDbNames");
restoreDataObject.DefaultTargetDbName = optionValues.GetOptionValue<string>("DefaultTargetDbName");
restoreDataObject.BackupTailLog = optionValues.GetOptionValue<bool>(RestoreOptionsHelper.BackupTailLog);
restoreDataObject.CanDropExistingConnections = optionValues.GetOptionValue<bool>("CanDropExistingConnections");
restoreDataObject.RestoreParams = optionValues as RestoreParams;
restoreDataObject.RestorePlan = null;
RestoreOptions restoreOptions = new RestoreOptions();