mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-16 18:47:57 -05:00
@@ -0,0 +1,43 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
//
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts
|
||||||
|
{
|
||||||
|
public class RestoreConfigInfoRequestParams
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The Uri to find the connection to do the restore operations
|
||||||
|
/// </summary>
|
||||||
|
public string OwnerUri { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RestoreConfigInfoResponse
|
||||||
|
{
|
||||||
|
public RestoreConfigInfoResponse()
|
||||||
|
{
|
||||||
|
ConfigInfo = new Dictionary<string, object>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Config Info
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, object> ConfigInfo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Errors occurred while creating the restore config info
|
||||||
|
/// </summary>
|
||||||
|
public string ErrorMessage { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RestoreConfigInfoRequest
|
||||||
|
{
|
||||||
|
public static readonly
|
||||||
|
RequestType<RestoreConfigInfoRequestParams, RestoreConfigInfoResponse> Type =
|
||||||
|
RequestType<RestoreConfigInfoRequestParams, RestoreConfigInfoResponse>.Create("disasterrecovery/restoreconfiginfo");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
//
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Class to include the plan detail
|
||||||
|
/// </summary>
|
||||||
|
public class RestorePlanDetailInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the option from RestoreOptionsHelper
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current value of the option
|
||||||
|
/// </summary>
|
||||||
|
public object CurrentValue { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the option is read only or can be changed in client
|
||||||
|
/// </summary>
|
||||||
|
public bool IsReadOnly { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the option should be visibile in client
|
||||||
|
/// </summary>
|
||||||
|
public bool IsVisiable { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The default value of the option
|
||||||
|
/// </summary>
|
||||||
|
public object DefaultValue { get; set; }
|
||||||
|
|
||||||
|
internal static RestorePlanDetailInfo Create(string name, object currentValue, bool isReadOnly = false, bool isVisible = true, object defaultValue = null)
|
||||||
|
{
|
||||||
|
return new RestorePlanDetailInfo
|
||||||
|
{
|
||||||
|
CurrentValue = currentValue,
|
||||||
|
IsReadOnly = isReadOnly,
|
||||||
|
Name = name,
|
||||||
|
IsVisiable = isVisible,
|
||||||
|
DefaultValue = defaultValue
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
//
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Database file info
|
||||||
|
/// </summary>
|
||||||
|
public class RestoreDatabaseFileInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// File type (Rows Data, Log ...)
|
||||||
|
/// </summary>
|
||||||
|
public string FileType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Logical Name
|
||||||
|
/// </summary>
|
||||||
|
public string LogicalFileName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Original location of the file to restore to
|
||||||
|
/// </summary>
|
||||||
|
public string OriginalFileName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The file to restore to
|
||||||
|
/// </summary>
|
||||||
|
public string RestoreAsFileName { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Restore Plan Response
|
||||||
|
/// </summary>
|
||||||
|
public class RestorePlanResponse
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Restore session id, can be used in restore request to use an existing restore plan
|
||||||
|
/// </summary>
|
||||||
|
public string SessionId { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The list of backup sets to restore
|
||||||
|
/// </summary>
|
||||||
|
public DatabaseFileInfo[] BackupSetsToRestore { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the restore operation is supported
|
||||||
|
/// </summary>
|
||||||
|
public bool CanRestore { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Errors occurred while creating restore plan
|
||||||
|
/// </summary>
|
||||||
|
public string ErrorMessage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The db files included in the backup file
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<RestoreDatabaseFileInfo> DbFiles { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Database names extracted from backup sets
|
||||||
|
/// </summary>
|
||||||
|
public string[] DatabaseNamesFromBackupSets { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For testing purpose to verify the target database
|
||||||
|
/// </summary>
|
||||||
|
internal string DatabaseName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Plan details
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, RestorePlanDetailInfo> PlanDetails { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RestorePlanRequest
|
||||||
|
{
|
||||||
|
public static readonly
|
||||||
|
RequestType<RestoreParams, RestorePlanResponse> Type =
|
||||||
|
RequestType<RestoreParams, RestorePlanResponse>.Create("disasterrecovery/restoreplan");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,113 +3,10 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts
|
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Restore request parameters
|
|
||||||
/// </summary>
|
|
||||||
public class RestoreParams : GeneralRequestDetails
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Restore session id. The parameter is optional and if passed, an existing plan will be used
|
|
||||||
/// </summary>
|
|
||||||
internal string SessionId
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return GetOptionValue<string>(RestoreOptionsHelper.SessionId);
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
SetOptionValue(RestoreOptionsHelper.SessionId, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The Uri to find the connection to do the restore operations
|
|
||||||
/// </summary>
|
|
||||||
public string OwnerUri { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Comma delimited list of backup files
|
|
||||||
/// </summary>
|
|
||||||
internal string BackupFilePaths
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return GetOptionValue<string>(RestoreOptionsHelper.BackupFilePaths);
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
SetOptionValue(RestoreOptionsHelper.BackupFilePaths, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Target Database name to restore to
|
|
||||||
/// </summary>
|
|
||||||
internal string TargetDatabaseName
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return GetOptionValue<string>(RestoreOptionsHelper.TargetDatabaseName);
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
SetOptionValue(RestoreOptionsHelper.TargetDatabaseName, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Source Database name to restore from
|
|
||||||
/// </summary>
|
|
||||||
internal string SourceDatabaseName
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return GetOptionValue<string>(RestoreOptionsHelper.SourceDatabaseName);
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
SetOptionValue(RestoreOptionsHelper.SourceDatabaseName, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If set to true, the db files will be relocated to default data location in the server
|
|
||||||
/// </summary>
|
|
||||||
internal bool RelocateDbFiles
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return GetOptionValue<bool>(RestoreOptionsHelper.RelocateDbFiles);
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
SetOptionValue(RestoreOptionsHelper.RelocateDbFiles, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ids of the backup set to restore
|
|
||||||
/// </summary>
|
|
||||||
internal string[] SelectedBackupSets
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return GetOptionValue<string[]>(RestoreOptionsHelper.SelectedBackupSets);
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
SetOptionValue(RestoreOptionsHelper.SelectedBackupSets, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Restore response
|
/// Restore response
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -132,78 +29,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts
|
|||||||
public string ErrorMessage { get; set; }
|
public string ErrorMessage { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Database file info
|
|
||||||
/// </summary>
|
|
||||||
public class RestoreDatabaseFileInfo
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// File type (Rows Data, Log ...)
|
|
||||||
/// </summary>
|
|
||||||
public string FileType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Logical Name
|
|
||||||
/// </summary>
|
|
||||||
public string LogicalFileName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Original location of the file to restore to
|
|
||||||
/// </summary>
|
|
||||||
public string OriginalFileName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The file to restore to
|
|
||||||
/// </summary>
|
|
||||||
public string RestoreAsFileName { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Restore Plan Response
|
|
||||||
/// </summary>
|
|
||||||
public class RestorePlanResponse
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Restore session id, can be used in restore request to use an existing restore plan
|
|
||||||
/// </summary>
|
|
||||||
public string SessionId { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The list of backup sets to restore
|
|
||||||
/// </summary>
|
|
||||||
public DatabaseFileInfo[] BackupSetsToRestore { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates whether the restore operation is supported
|
|
||||||
/// </summary>
|
|
||||||
public bool CanRestore { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Errors occurred while creating restore plan
|
|
||||||
/// </summary>
|
|
||||||
public string ErrorMessage { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The db files included in the backup file
|
|
||||||
/// </summary>
|
|
||||||
public IEnumerable<RestoreDatabaseFileInfo> DbFiles { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Database names extracted from backup sets
|
|
||||||
/// </summary>
|
|
||||||
public string[] DatabaseNamesFromBackupSets { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// For testing purpose to verify the target database
|
|
||||||
/// </summary>
|
|
||||||
internal string DatabaseName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Plan details
|
|
||||||
/// </summary>
|
|
||||||
public Dictionary<string, object> PlanDetails { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RestoreRequest
|
public class RestoreRequest
|
||||||
{
|
{
|
||||||
@@ -212,10 +38,5 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts
|
|||||||
RequestType<RestoreParams, RestoreResponse>.Create("disasterrecovery/restore");
|
RequestType<RestoreParams, RestoreResponse>.Create("disasterrecovery/restore");
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RestorePlanRequest
|
|
||||||
{
|
|
||||||
public static readonly
|
|
||||||
RequestType<RestoreParams, RestorePlanResponse> Type =
|
|
||||||
RequestType<RestoreParams, RestorePlanResponse>.Create("disasterrecovery/restoreplan");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,145 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
//
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Restore request parameters
|
||||||
|
/// </summary>
|
||||||
|
public class RestoreParams : GeneralRequestDetails
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Restore session id. The parameter is optional and if passed, an existing plan will be used
|
||||||
|
/// </summary>
|
||||||
|
internal string SessionId
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return GetOptionValue<string>(RestoreOptionsHelper.SessionId);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetOptionValue(RestoreOptionsHelper.SessionId, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Uri to find the connection to do the restore operations
|
||||||
|
/// </summary>
|
||||||
|
public string OwnerUri { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Comma delimited list of backup files
|
||||||
|
/// </summary>
|
||||||
|
internal string BackupFilePaths
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return GetOptionValue<string>(RestoreOptionsHelper.BackupFilePaths);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetOptionValue(RestoreOptionsHelper.BackupFilePaths, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Target Database name to restore to
|
||||||
|
/// </summary>
|
||||||
|
internal string TargetDatabaseName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return GetOptionValue<string>(RestoreOptionsHelper.TargetDatabaseName);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetOptionValue(RestoreOptionsHelper.TargetDatabaseName, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Source Database name to restore from
|
||||||
|
/// </summary>
|
||||||
|
internal string SourceDatabaseName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return GetOptionValue<string>(RestoreOptionsHelper.SourceDatabaseName);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetOptionValue(RestoreOptionsHelper.SourceDatabaseName, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If set to true, the db files will be relocated to default data location in the server
|
||||||
|
/// </summary>
|
||||||
|
internal bool RelocateDbFiles
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return GetOptionValue<bool>(RestoreOptionsHelper.RelocateDbFiles);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetOptionValue(RestoreOptionsHelper.RelocateDbFiles, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If set to true, the backup files will be used to create restore plan otehrwise the source database name will be used
|
||||||
|
/// </summary>
|
||||||
|
internal bool ReadHeaderFromMedia
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
//Default is true for now for backward compatibility
|
||||||
|
return Options.ContainsKey(RestoreOptionsHelper.ReadHeaderFromMedia) ? GetOptionValue<bool>(RestoreOptionsHelper.ReadHeaderFromMedia) : true;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetOptionValue(RestoreOptionsHelper.ReadHeaderFromMedia, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ids of the selected backup set to restore. If null, all backup sets will be selected. If empty list,
|
||||||
|
/// no backup sets will be selected
|
||||||
|
/// </summary>
|
||||||
|
internal IEnumerable<string> SelectedBackupSets
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var selectedBackupSets = GetOptionValue<object>(RestoreOptionsHelper.SelectedBackupSets);
|
||||||
|
if (selectedBackupSets != null)
|
||||||
|
{
|
||||||
|
JArray array = selectedBackupSets as JArray;
|
||||||
|
if(array != null)
|
||||||
|
{
|
||||||
|
return array.ToObject<IEnumerable<string>>();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IEnumerable<string> list = selectedBackupSets as IEnumerable<string>;
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetOptionValue(RestoreOptionsHelper.SelectedBackupSets, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -10,12 +10,9 @@ using Microsoft.SqlTools.ServiceLayer.Admin;
|
|||||||
using Microsoft.SqlTools.ServiceLayer.Admin.Contracts;
|
using Microsoft.SqlTools.ServiceLayer.Admin.Contracts;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||||
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
|
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
|
||||||
using Microsoft.SqlTools.ServiceLayer.TaskServices;
|
using Microsoft.SqlTools.ServiceLayer.TaskServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation;
|
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation;
|
||||||
using Microsoft.SqlServer.Management.Smo;
|
|
||||||
using Microsoft.SqlServer.Management.Common;
|
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||||
{
|
{
|
||||||
@@ -26,7 +23,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
{
|
{
|
||||||
private static readonly Lazy<DisasterRecoveryService> instance = new Lazy<DisasterRecoveryService>(() => new DisasterRecoveryService());
|
private static readonly Lazy<DisasterRecoveryService> instance = new Lazy<DisasterRecoveryService>(() => new DisasterRecoveryService());
|
||||||
private static ConnectionService connectionService = null;
|
private static ConnectionService connectionService = null;
|
||||||
private RestoreDatabaseHelper restoreDatabaseService = RestoreDatabaseHelper.Instance;
|
private RestoreDatabaseHelper restoreDatabaseService = new RestoreDatabaseHelper();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default, parameterless constructor.
|
/// Default, parameterless constructor.
|
||||||
@@ -74,8 +71,12 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
|
|
||||||
// Create respore task
|
// Create respore task
|
||||||
serviceHost.SetRequestHandler(RestoreRequest.Type, HandleRestoreRequest);
|
serviceHost.SetRequestHandler(RestoreRequest.Type, HandleRestoreRequest);
|
||||||
|
|
||||||
// Create respore plan
|
// Create respore plan
|
||||||
serviceHost.SetRequestHandler(RestorePlanRequest.Type, HandleRestorePlanRequest);
|
serviceHost.SetRequestHandler(RestorePlanRequest.Type, HandleRestorePlanRequest);
|
||||||
|
|
||||||
|
// Create respore config
|
||||||
|
serviceHost.SetRequestHandler(RestoreConfigInfoRequest.Type, HandleRestoreConfigInfoRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -128,7 +129,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
ConnectionInfo connInfo;
|
ConnectionInfo connInfo;
|
||||||
bool supported = IsBackupRestoreOperationSupported(restoreParams, out connInfo);
|
bool supported = IsBackupRestoreOperationSupported(restoreParams.OwnerUri, out connInfo);
|
||||||
|
|
||||||
if (supported && connInfo != null)
|
if (supported && connInfo != null)
|
||||||
{
|
{
|
||||||
@@ -150,6 +151,37 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles a restore config info request
|
||||||
|
/// </summary>
|
||||||
|
internal async Task HandleRestoreConfigInfoRequest(
|
||||||
|
RestoreConfigInfoRequestParams restoreConfigInfoParams,
|
||||||
|
RequestContext<RestoreConfigInfoResponse> requestContext)
|
||||||
|
{
|
||||||
|
RestoreConfigInfoResponse response = new RestoreConfigInfoResponse();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ConnectionInfo connInfo;
|
||||||
|
bool supported = IsBackupRestoreOperationSupported(restoreConfigInfoParams.OwnerUri, out connInfo);
|
||||||
|
|
||||||
|
if (supported && connInfo != null)
|
||||||
|
{
|
||||||
|
response = this.restoreDatabaseService.CreateConfigInfoResponse(restoreConfigInfoParams);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.ErrorMessage = SR.RestoreNotSupported;
|
||||||
|
}
|
||||||
|
await requestContext.SendResult(response);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
response.ErrorMessage = ex.Message;
|
||||||
|
await requestContext.SendResult(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles a restore request
|
/// Handles a restore request
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -162,7 +194,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
ConnectionInfo connInfo;
|
ConnectionInfo connInfo;
|
||||||
bool supported = IsBackupRestoreOperationSupported(restoreParams, out connInfo);
|
bool supported = IsBackupRestoreOperationSupported(restoreParams.OwnerUri, out connInfo);
|
||||||
|
|
||||||
if (supported && connInfo != null)
|
if (supported && connInfo != null)
|
||||||
{
|
{
|
||||||
@@ -175,7 +207,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
// create task metadata
|
// create task metadata
|
||||||
TaskMetadata metadata = new TaskMetadata();
|
TaskMetadata metadata = new TaskMetadata();
|
||||||
metadata.ServerName = connInfo.ConnectionDetails.ServerName;
|
metadata.ServerName = connInfo.ConnectionDetails.ServerName;
|
||||||
metadata.DatabaseName = connInfo.ConnectionDetails.DatabaseName;
|
metadata.DatabaseName = restoreParams.TargetDatabaseName;
|
||||||
metadata.Name = SR.RestoreTaskName;
|
metadata.Name = SR.RestoreTaskName;
|
||||||
metadata.IsCancelable = true;
|
metadata.IsCancelable = true;
|
||||||
metadata.Data = restoreDataObject;
|
metadata.Data = restoreDataObject;
|
||||||
@@ -279,14 +311,14 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsBackupRestoreOperationSupported(RestoreParams restoreParams, out ConnectionInfo connectionInfo)
|
private bool IsBackupRestoreOperationSupported(string ownerUri, out ConnectionInfo connectionInfo)
|
||||||
{
|
{
|
||||||
SqlConnection sqlConn = null;
|
SqlConnection sqlConn = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ConnectionInfo connInfo;
|
ConnectionInfo connInfo;
|
||||||
DisasterRecoveryService.ConnectionServiceInstance.TryFindConnection(
|
DisasterRecoveryService.ConnectionServiceInstance.TryFindConnection(
|
||||||
restoreParams.OwnerUri,
|
ownerUri,
|
||||||
out connInfo);
|
out connInfo);
|
||||||
|
|
||||||
if (connInfo != null)
|
if (connInfo != null)
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
|
|||||||
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
|
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
|
||||||
using Microsoft.SqlTools.ServiceLayer.TaskServices;
|
using Microsoft.SqlTools.ServiceLayer.TaskServices;
|
||||||
using Microsoft.SqlTools.Utility;
|
using Microsoft.SqlTools.Utility;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
||||||
{
|
{
|
||||||
@@ -25,21 +24,6 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
|||||||
public class RestoreDatabaseHelper
|
public class RestoreDatabaseHelper
|
||||||
{
|
{
|
||||||
public const string LastBackupTaken = "lastBackupTaken";
|
public const string LastBackupTaken = "lastBackupTaken";
|
||||||
private static RestoreDatabaseHelper instance = new RestoreDatabaseHelper();
|
|
||||||
private ConcurrentDictionary<string, RestoreDatabaseTaskDataObject> restoreSessions = new ConcurrentDictionary<string, RestoreDatabaseTaskDataObject>();
|
|
||||||
|
|
||||||
internal RestoreDatabaseHelper()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RestoreDatabaseHelper Instance
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a backup task for execution and cancellation
|
/// Create a backup task for execution and cancellation
|
||||||
@@ -142,6 +126,26 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
|||||||
return taskResult;
|
return taskResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates response which includes information about the server given to restore (default data location, db names with backupsets)
|
||||||
|
/// </summary>
|
||||||
|
public RestoreConfigInfoResponse CreateConfigInfoResponse(RestoreConfigInfoRequestParams restoreConfigInfoRequest)
|
||||||
|
{
|
||||||
|
RestoreConfigInfoResponse response = new RestoreConfigInfoResponse();
|
||||||
|
RestoreDatabaseTaskDataObject restoreTaskObject = CreateRestoreForNewSession(restoreConfigInfoRequest.OwnerUri);
|
||||||
|
if (restoreTaskObject != null)
|
||||||
|
{
|
||||||
|
// Default Data folder path in the target server
|
||||||
|
response.ConfigInfo.Add(RestoreOptionsHelper.DataFileFolder, restoreTaskObject.DefaultDataFileFolder);
|
||||||
|
// Default log folder path in the target server
|
||||||
|
response.ConfigInfo.Add(RestoreOptionsHelper.LogFileFolder, restoreTaskObject.DefaultLogFileFolder);
|
||||||
|
// The db names with backup set
|
||||||
|
response.ConfigInfo.Add(RestoreOptionsHelper.SourceDatabaseNamesWithBackupSets, restoreTaskObject.GetDatabaseNamesWithBackupSets());
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a restore plan, The result includes the information about the backup set,
|
/// Creates a restore plan, The result includes the information about the backup set,
|
||||||
/// the files and the database to restore to
|
/// the files and the database to restore to
|
||||||
@@ -153,7 +157,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
|||||||
RestorePlanResponse response = new RestorePlanResponse()
|
RestorePlanResponse response = new RestorePlanResponse()
|
||||||
{
|
{
|
||||||
DatabaseName = restoreDataObject.RestoreParams.TargetDatabaseName,
|
DatabaseName = restoreDataObject.RestoreParams.TargetDatabaseName,
|
||||||
PlanDetails = new System.Collections.Generic.Dictionary<string, object>()
|
PlanDetails = new System.Collections.Generic.Dictionary<string, RestorePlanDetailInfo>()
|
||||||
};
|
};
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -179,25 +183,14 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
|||||||
response.ErrorMessage = SR.RestoreNotSupported;
|
response.ErrorMessage = SR.RestoreNotSupported;
|
||||||
}
|
}
|
||||||
|
|
||||||
response.PlanDetails.Add(LastBackupTaken, restoreDataObject.GetLastBackupTaken());
|
response.PlanDetails.Add(LastBackupTaken, RestorePlanDetailInfo.Create(LastBackupTaken, restoreDataObject.GetLastBackupTaken()));
|
||||||
|
|
||||||
response.BackupSetsToRestore = restoreDataObject.GetSelectedBakupSets();
|
response.BackupSetsToRestore = restoreDataObject.GetSelectedBakupSets();
|
||||||
var dbNames = restoreDataObject.GetSourceDbNames();
|
var dbNames = restoreDataObject.GetSourceDbNames();
|
||||||
response.DatabaseNamesFromBackupSets = dbNames == null ? new string[] { } : dbNames.ToArray();
|
response.DatabaseNamesFromBackupSets = dbNames == null ? new string[] { } : dbNames.ToArray();
|
||||||
|
|
||||||
// Adding the default values for some of the options in the plan details
|
RestoreOptionsHelper.AddOptions(response, restoreDataObject);
|
||||||
bool isTailLogBackupPossible = restoreDataObject.IsTailLogBackupPossible(restoreDataObject.RestorePlanner.DatabaseName);
|
|
||||||
// Default backup tail-log. It's true when tail-log backup is possible for the source database
|
|
||||||
response.PlanDetails.Add(RestoreOptionsHelper.DefaultBackupTailLog, isTailLogBackupPossible);
|
|
||||||
// Default backup file for tail-log bacup when Tail-Log bachup is set to true
|
|
||||||
response.PlanDetails.Add(RestoreOptionsHelper.DefaultTailLogBackupFile,
|
|
||||||
restoreDataObject.Util.GetDefaultTailLogbackupFile(restoreDataObject.RestorePlan.DatabaseName));
|
|
||||||
// Default stand by file path for when RESTORE WITH STANDBY is selected
|
|
||||||
response.PlanDetails.Add(RestoreOptionsHelper.DefaultStandbyFile, restoreDataObject.Util.GetDefaultStandbyFile(restoreDataObject.RestorePlan.DatabaseName));
|
|
||||||
// Default Data folder path in the target server
|
|
||||||
response.PlanDetails.Add(RestoreOptionsHelper.DefaultDataFileFolder, restoreDataObject.DefaultDataFileFolder);
|
|
||||||
// Default log folder path in the target server
|
|
||||||
response.PlanDetails.Add(RestoreOptionsHelper.DefaultLogFileFolder, restoreDataObject.DefaultLogFileFolder);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -248,30 +241,18 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
|||||||
public RestoreDatabaseTaskDataObject CreateRestoreDatabaseTaskDataObject(RestoreParams restoreParams)
|
public RestoreDatabaseTaskDataObject CreateRestoreDatabaseTaskDataObject(RestoreParams restoreParams)
|
||||||
{
|
{
|
||||||
RestoreDatabaseTaskDataObject restoreTaskObject = null;
|
RestoreDatabaseTaskDataObject restoreTaskObject = null;
|
||||||
if (!string.IsNullOrWhiteSpace(restoreParams.SessionId))
|
restoreTaskObject = CreateRestoreForNewSession(restoreParams.OwnerUri, restoreParams.TargetDatabaseName);
|
||||||
{
|
string sessionId = string.IsNullOrWhiteSpace(restoreParams.SessionId) ? Guid.NewGuid().ToString() : restoreParams.SessionId;
|
||||||
this.restoreSessions.TryGetValue(restoreParams.SessionId, out restoreTaskObject);
|
restoreTaskObject.SessionId = sessionId;
|
||||||
}
|
restoreTaskObject.RestoreParams = restoreParams;
|
||||||
|
|
||||||
if (restoreTaskObject == null)
|
|
||||||
{
|
|
||||||
restoreTaskObject = CreateRestoreForNewSession(restoreParams);
|
|
||||||
string sessionId = string.IsNullOrWhiteSpace(restoreParams.SessionId) ? Guid.NewGuid().ToString() : restoreParams.SessionId;
|
|
||||||
this.restoreSessions.AddOrUpdate(sessionId, restoreTaskObject, (key, oldSession) => restoreTaskObject);
|
|
||||||
restoreTaskObject.SessionId = sessionId;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
restoreTaskObject.RestoreParams = restoreParams;
|
|
||||||
}
|
|
||||||
return restoreTaskObject;
|
return restoreTaskObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
private RestoreDatabaseTaskDataObject CreateRestoreForNewSession(RestoreParams restoreParams)
|
private RestoreDatabaseTaskDataObject CreateRestoreForNewSession(string ownerUri, string targetDatabaseName = null)
|
||||||
{
|
{
|
||||||
ConnectionInfo connInfo;
|
ConnectionInfo connInfo;
|
||||||
DisasterRecoveryService.ConnectionServiceInstance.TryFindConnection(
|
DisasterRecoveryService.ConnectionServiceInstance.TryFindConnection(
|
||||||
restoreParams.OwnerUri,
|
ownerUri,
|
||||||
out connInfo);
|
out connInfo);
|
||||||
|
|
||||||
if (connInfo != null)
|
if (connInfo != null)
|
||||||
@@ -295,8 +276,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
|||||||
}
|
}
|
||||||
Server server = new Server(new ServerConnection(connection));
|
Server server = new Server(new ServerConnection(connection));
|
||||||
|
|
||||||
RestoreDatabaseTaskDataObject restoreDataObject = new RestoreDatabaseTaskDataObject(server, restoreParams.TargetDatabaseName);
|
RestoreDatabaseTaskDataObject restoreDataObject = new RestoreDatabaseTaskDataObject(server, targetDatabaseName);
|
||||||
restoreDataObject.RestoreParams = restoreParams;
|
|
||||||
return restoreDataObject;
|
return restoreDataObject;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -313,7 +293,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
|||||||
{
|
{
|
||||||
restoreDataObject.AddFiles(restoreDataObject.RestoreParams.BackupFilePaths);
|
restoreDataObject.AddFiles(restoreDataObject.RestoreParams.BackupFilePaths);
|
||||||
}
|
}
|
||||||
restoreDataObject.RestorePlanner.ReadHeaderFromMedia = !string.IsNullOrEmpty(restoreDataObject.RestoreParams.BackupFilePaths);
|
restoreDataObject.RestorePlanner.ReadHeaderFromMedia = restoreDataObject.RestoreParams.ReadHeaderFromMedia;
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(restoreDataObject.RestoreParams.SourceDatabaseName))
|
if (string.IsNullOrWhiteSpace(restoreDataObject.RestoreParams.SourceDatabaseName))
|
||||||
{
|
{
|
||||||
@@ -325,30 +305,9 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
|||||||
}
|
}
|
||||||
restoreDataObject.TargetDatabase = restoreDataObject.RestoreParams.TargetDatabaseName;
|
restoreDataObject.TargetDatabase = restoreDataObject.RestoreParams.TargetDatabaseName;
|
||||||
|
|
||||||
restoreDataObject.RestoreOptions.KeepReplication = restoreDataObject.RestoreParams.GetOptionValue<bool>(RestoreOptionsHelper.KeepReplication);
|
RestoreOptionsHelper.UpdateOptionsInPlan(restoreDataObject);
|
||||||
restoreDataObject.RestoreOptions.ReplaceDatabase = restoreDataObject.RestoreParams.GetOptionValue<bool>(RestoreOptionsHelper.ReplaceDatabase);
|
|
||||||
restoreDataObject.RestoreOptions.SetRestrictedUser = restoreDataObject.RestoreParams.GetOptionValue<bool>(RestoreOptionsHelper.SetRestrictedUser);
|
|
||||||
string recoveryState = restoreDataObject.RestoreParams.GetOptionValue<string>(RestoreOptionsHelper.RecoveryState);
|
|
||||||
object databaseRecoveryState;
|
|
||||||
if (Enum.TryParse(typeof(DatabaseRecoveryState), recoveryState, out databaseRecoveryState))
|
|
||||||
{
|
|
||||||
restoreDataObject.RestoreOptions.RecoveryState = (DatabaseRecoveryState)databaseRecoveryState;
|
|
||||||
}
|
|
||||||
bool isTailLogBackupPossible = restoreDataObject.IsTailLogBackupPossible(restoreDataObject.RestorePlanner.DatabaseName);
|
|
||||||
if (isTailLogBackupPossible)
|
|
||||||
{
|
|
||||||
restoreDataObject.RestorePlanner.BackupTailLog = restoreDataObject.RestoreParams.GetOptionValue<bool>(RestoreOptionsHelper.BackupTailLog);
|
|
||||||
restoreDataObject.TailLogBackupFile = restoreDataObject.RestoreParams.GetOptionValue<string>(RestoreOptionsHelper.TailLogBackupFile);
|
|
||||||
restoreDataObject.TailLogWithNoRecovery = restoreDataObject.RestoreParams.GetOptionValue<bool>(RestoreOptionsHelper.TailLogWithNoRecovery);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
restoreDataObject.RestorePlanner.BackupTailLog = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
restoreDataObject.CloseExistingConnections = restoreDataObject.RestoreParams.GetOptionValue<bool>(RestoreOptionsHelper.CloseExistingConnections);
|
restoreDataObject.UpdateRestorePlan();
|
||||||
|
|
||||||
restoreDataObject.UpdateRestorePlan(restoreDataObject.RestoreParams.RelocateDbFiles);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -366,8 +325,6 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
|||||||
{
|
{
|
||||||
restoreDataObject.SqlTask = sqlTask;
|
restoreDataObject.SqlTask = sqlTask;
|
||||||
restoreDataObject.Execute();
|
restoreDataObject.Execute();
|
||||||
RestoreDatabaseTaskDataObject cachedRestoreDataObject;
|
|
||||||
this.restoreSessions.TryRemove(restoreDataObject.SessionId, out cachedRestoreDataObject);
|
|
||||||
}
|
}
|
||||||
catch(Exception ex)
|
catch(Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,16 +8,46 @@ using System.Collections.Generic;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Microsoft.SqlServer.Management.Common;
|
||||||
using Microsoft.SqlServer.Management.Smo;
|
using Microsoft.SqlServer.Management.Smo;
|
||||||
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
|
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
|
||||||
using Microsoft.SqlTools.ServiceLayer.TaskServices;
|
using Microsoft.SqlTools.ServiceLayer.TaskServices;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
||||||
{
|
{
|
||||||
|
public interface IRestoreDatabaseTaskDataObject
|
||||||
|
{
|
||||||
|
string DataFilesFolder { get; set; }
|
||||||
|
string DefaultDataFileFolder { get; }
|
||||||
|
bool RelocateAllFiles { get; set; }
|
||||||
|
string LogFilesFolder { get; set; }
|
||||||
|
string DefaultLogFileFolder { get; }
|
||||||
|
List<DbFile> DbFiles { get; }
|
||||||
|
|
||||||
|
RestoreOptions RestoreOptions { get; }
|
||||||
|
|
||||||
|
string GetDefaultStandbyFile(string databaseName);
|
||||||
|
bool IsTailLogBackupPossible(string databaseName);
|
||||||
|
bool IsTailLogBackupWithNoRecoveryPossible(string databaseName);
|
||||||
|
|
||||||
|
bool TailLogWithNoRecovery { get; set; }
|
||||||
|
|
||||||
|
string TailLogBackupFile { get; set; }
|
||||||
|
|
||||||
|
string GetDefaultTailLogbackupFile(string databaseName);
|
||||||
|
|
||||||
|
RestorePlan RestorePlan { get; }
|
||||||
|
|
||||||
|
bool CloseExistingConnections { get; set; }
|
||||||
|
|
||||||
|
RestoreParams RestoreParams { get; set; }
|
||||||
|
|
||||||
|
bool BackupTailLog { get; set; }
|
||||||
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Includes the plan with all the data required to do a restore operation on server
|
/// Includes the plan with all the data required to do a restore operation on server
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class RestoreDatabaseTaskDataObject
|
public class RestoreDatabaseTaskDataObject : IRestoreDatabaseTaskDataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private const char BackupMediaNameSeparator = ',';
|
private const char BackupMediaNameSeparator = ',';
|
||||||
@@ -48,6 +78,11 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
|||||||
this.restoreOptions.PercentCompleteNotification = 5;
|
this.restoreOptions.PercentCompleteNotification = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Boolean indicating whether the relocate all files checkbox was checked
|
||||||
|
/// </summary>
|
||||||
|
public bool RelocateAllFiles { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Restore session id
|
/// Restore session id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -89,6 +124,15 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
|||||||
return Util.GetSourceDbNames(this.restorePlanner.BackupMediaList, this.CredentialName);
|
return Util.GetSourceDbNames(this.restorePlanner.BackupMediaList, this.CredentialName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the db names that have backupsets
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public List<String> GetDatabaseNamesWithBackupSets()
|
||||||
|
{
|
||||||
|
return Util.GetSourceDbNames();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Current sqlserver instance
|
/// Current sqlserver instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -375,7 +419,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// <c>true</c> if [is tail log backup possible]; otherwise, <c>false</c>.
|
/// <c>true</c> if [is tail log backup possible]; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
internal bool IsTailLogBackupPossible(string databaseName)
|
public bool IsTailLogBackupPossible(string databaseName)
|
||||||
{
|
{
|
||||||
if (this.Server.Version.Major < 9 || String.IsNullOrEmpty(this.restorePlanner.DatabaseName))
|
if (this.Server.Version.Major < 9 || String.IsNullOrEmpty(this.restorePlanner.DatabaseName))
|
||||||
{
|
{
|
||||||
@@ -403,6 +447,45 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether [is tail log backup with NORECOVERY possible].
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// <c>true</c> if [is tail log backup with NORECOVERY possible]; otherwise, <c>false</c>.
|
||||||
|
/// </returns>
|
||||||
|
public bool IsTailLogBackupWithNoRecoveryPossible(string databaseName)
|
||||||
|
{
|
||||||
|
if (!IsTailLogBackupPossible(databaseName))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Database db = this.Server.Databases[databaseName];
|
||||||
|
if (db == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (Server.Version.Major > 10 && db.DatabaseEngineType == DatabaseEngineType.Standalone && !String.IsNullOrEmpty(db.AvailabilityGroupName))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (db.DatabaseEngineType == DatabaseEngineType.Standalone && db.IsMirroringEnabled)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetDefaultStandbyFile(string databaseName)
|
||||||
|
{
|
||||||
|
return Util.GetDefaultStandbyFile(databaseName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetDefaultTailLogbackupFile(string databaseName)
|
||||||
|
{
|
||||||
|
return Util.GetDefaultTailLogbackupFile(databaseName);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether [prompt before each backup].
|
/// Gets or sets a value indicating whether [prompt before each backup].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -500,7 +583,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
|||||||
{
|
{
|
||||||
if (this.restorePlan == null)
|
if (this.restorePlan == null)
|
||||||
{
|
{
|
||||||
this.UpdateRestorePlan(false);
|
this.UpdateRestorePlan();
|
||||||
}
|
}
|
||||||
return this.restorePlan;
|
return this.restorePlan;
|
||||||
}
|
}
|
||||||
@@ -681,7 +764,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates restore plan
|
/// Updates restore plan
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void UpdateRestorePlan(bool relocateAllFiles = false)
|
public void UpdateRestorePlan()
|
||||||
{
|
{
|
||||||
this.ActiveException = null; //Clear any existing exceptions as the plan is getting recreated.
|
this.ActiveException = null; //Clear any existing exceptions as the plan is getting recreated.
|
||||||
//Clear any existing exceptions as new plan is getting recreated.
|
//Clear any existing exceptions as new plan is getting recreated.
|
||||||
@@ -703,7 +786,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
|||||||
this.dbFiles = this.GetDbFiles();
|
this.dbFiles = this.GetDbFiles();
|
||||||
UpdateDBFilesPhysicalRelocate();
|
UpdateDBFilesPhysicalRelocate();
|
||||||
|
|
||||||
if (relocateAllFiles)
|
if (RelocateAllFiles)
|
||||||
{
|
{
|
||||||
UpdateDbFiles();
|
UpdateDbFiles();
|
||||||
}
|
}
|
||||||
@@ -992,47 +1075,4 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RestoreDatabaseRecoveryState
|
|
||||||
{
|
|
||||||
public RestoreDatabaseRecoveryState(DatabaseRecoveryState recoveryState)
|
|
||||||
{
|
|
||||||
this.RecoveryState = recoveryState;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DatabaseRecoveryState RecoveryState;
|
|
||||||
private static string RestoreWithRecovery = "RESTORE WITH RECOVERY";
|
|
||||||
private static string RestoreWithNoRecovery = "RESTORE WITH NORECOVERY";
|
|
||||||
private static string RestoreWithStandby = "RESTORE WITH STANDBY";
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
switch (this.RecoveryState)
|
|
||||||
{
|
|
||||||
case DatabaseRecoveryState.WithRecovery:
|
|
||||||
return RestoreDatabaseRecoveryState.RestoreWithRecovery;
|
|
||||||
case DatabaseRecoveryState.WithNoRecovery:
|
|
||||||
return RestoreDatabaseRecoveryState.RestoreWithNoRecovery;
|
|
||||||
case DatabaseRecoveryState.WithStandBy:
|
|
||||||
return RestoreDatabaseRecoveryState.RestoreWithStandby;
|
|
||||||
}
|
|
||||||
return RestoreDatabaseRecoveryState.RestoreWithRecovery;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
public string Info()
|
|
||||||
{
|
|
||||||
switch (this.RecoveryState)
|
|
||||||
{
|
|
||||||
case DatabaseRecoveryState.WithRecovery:
|
|
||||||
return SR.RestoreWithRecoveryInfo;
|
|
||||||
case DatabaseRecoveryState.WithNoRecovery:
|
|
||||||
return SR.RestoreWithNoRecoveryInfo;
|
|
||||||
case DatabaseRecoveryState.WithStandBy:
|
|
||||||
return SR.RestoreWithStandbyInfo;
|
|
||||||
}
|
|
||||||
return SR.RestoreWithRecoveryInfo;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,9 +70,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
|||||||
return dt;
|
return dt;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: the code is moved from ssms and used for restore differential backups
|
|
||||||
//Uncomment when restore operation for differential backups is supported
|
|
||||||
/*
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queries msdb for source database names
|
/// Queries msdb for source database names
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -87,7 +85,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
|||||||
req.OrderByList[0] = new OrderBy();
|
req.OrderByList[0] = new OrderBy();
|
||||||
req.OrderByList[0].Field = "DatabaseName";
|
req.OrderByList[0].Field = "DatabaseName";
|
||||||
req.OrderByList[0].Dir = OrderBy.Direction.Asc;
|
req.OrderByList[0].Dir = OrderBy.Direction.Asc;
|
||||||
DataTable dt = server.ExecutionManager.GetEnumeratorData(req);
|
DataTable dt = GetEnumeratorData(req);
|
||||||
string last = "";
|
string last = "";
|
||||||
foreach (DataRow row in dt.Rows)
|
foreach (DataRow row in dt.Rows)
|
||||||
{
|
{
|
||||||
@@ -97,7 +95,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
|||||||
bool found = false;
|
bool found = false;
|
||||||
foreach (string str in databaseNames)
|
foreach (string str in databaseNames)
|
||||||
{
|
{
|
||||||
if (StrEqual(str, dbName))
|
if (string.Compare(str, dbName, StringComparison.InvariantCultureIgnoreCase) == 0)
|
||||||
{
|
{
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
@@ -112,7 +110,16 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
|||||||
}
|
}
|
||||||
return databaseNames;
|
return databaseNames;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
/// <summary>
|
||||||
|
/// make enumerator data request
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="req"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal DataTable GetEnumeratorData(Request req)
|
||||||
|
{
|
||||||
|
return new Enumerator().Process(this.server.ConnectionContext.SqlConnectionObject, req);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads backup file header to get source database names
|
/// Reads backup file header to get source database names
|
||||||
|
|||||||
@@ -3,34 +3,76 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.SqlServer.Management.Smo;
|
||||||
using Microsoft.SqlTools.Hosting.Contracts;
|
using Microsoft.SqlTools.Hosting.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation;
|
||||||
|
using Microsoft.SqlTools.Utility;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||||
{
|
{
|
||||||
public class RestoreOptionsHelper
|
public class RestoreOptionsHelper
|
||||||
{
|
{
|
||||||
|
//The key names of restore info in the resquest of response
|
||||||
|
|
||||||
|
//Option name keepReplication
|
||||||
internal const string KeepReplication = "keepReplication";
|
internal const string KeepReplication = "keepReplication";
|
||||||
|
|
||||||
|
//Option name replaceDatabase
|
||||||
internal const string ReplaceDatabase = "replaceDatabase";
|
internal const string ReplaceDatabase = "replaceDatabase";
|
||||||
|
|
||||||
|
//Option name setRestrictedUser
|
||||||
internal const string SetRestrictedUser = "setRestrictedUser";
|
internal const string SetRestrictedUser = "setRestrictedUser";
|
||||||
|
|
||||||
|
//Option name recoveryState
|
||||||
internal const string RecoveryState = "recoveryState";
|
internal const string RecoveryState = "recoveryState";
|
||||||
|
|
||||||
|
//Option name backupTailLog
|
||||||
internal const string BackupTailLog = "backupTailLog";
|
internal const string BackupTailLog = "backupTailLog";
|
||||||
internal const string DefaultBackupTailLog = "defaultBackupTailLog";
|
|
||||||
|
//Option name tailLogBackupFile
|
||||||
internal const string TailLogBackupFile = "tailLogBackupFile";
|
internal const string TailLogBackupFile = "tailLogBackupFile";
|
||||||
internal const string DefaultTailLogBackupFile = "defaultTailLogBackupFile";
|
|
||||||
|
//Option name tailLogWithNoRecovery
|
||||||
internal const string TailLogWithNoRecovery = "tailLogWithNoRecovery";
|
internal const string TailLogWithNoRecovery = "tailLogWithNoRecovery";
|
||||||
|
|
||||||
|
//Option name closeExistingConnections
|
||||||
internal const string CloseExistingConnections = "closeExistingConnections";
|
internal const string CloseExistingConnections = "closeExistingConnections";
|
||||||
|
|
||||||
|
//Option name relocateDbFiles
|
||||||
internal const string RelocateDbFiles = "relocateDbFiles";
|
internal const string RelocateDbFiles = "relocateDbFiles";
|
||||||
|
|
||||||
|
//Option name dataFileFolder
|
||||||
internal const string DataFileFolder = "dataFileFolder";
|
internal const string DataFileFolder = "dataFileFolder";
|
||||||
internal const string DefaultDataFileFolder = "defaultDataFileFolder";
|
|
||||||
|
//Option name logFileFolder
|
||||||
internal const string LogFileFolder = "logFileFolder";
|
internal const string LogFileFolder = "logFileFolder";
|
||||||
internal const string DefaultLogFileFolder = "defaultLogFileFolder";
|
|
||||||
|
//The key name to use to set the session id in the request
|
||||||
internal const string SessionId = "sessionId";
|
internal const string SessionId = "sessionId";
|
||||||
|
|
||||||
|
//The key name to use to set the backup file paths in the request
|
||||||
internal const string BackupFilePaths = "backupFilePaths";
|
internal const string BackupFilePaths = "backupFilePaths";
|
||||||
|
|
||||||
|
//The key name to use to set the target database name in the request
|
||||||
internal const string TargetDatabaseName = "targetDatabaseName";
|
internal const string TargetDatabaseName = "targetDatabaseName";
|
||||||
|
|
||||||
|
//The key name to use to set the source database name in the request
|
||||||
internal const string SourceDatabaseName = "sourceDatabaseName";
|
internal const string SourceDatabaseName = "sourceDatabaseName";
|
||||||
|
|
||||||
|
//The key name to use to set the selected backup sets in the request
|
||||||
internal const string SelectedBackupSets = "selectedBackupSets";
|
internal const string SelectedBackupSets = "selectedBackupSets";
|
||||||
|
|
||||||
|
//The key name to use to set the standby file sets in the request
|
||||||
internal const string StandbyFile = "standbyFile";
|
internal const string StandbyFile = "standbyFile";
|
||||||
internal const string DefaultStandbyFile = "defaultStandbyFile";
|
|
||||||
|
//The key name to use to set source db names in restore response
|
||||||
|
internal const string SourceDatabaseNamesWithBackupSets = "sourceDatabaseNamesWithBackupSets";
|
||||||
|
|
||||||
|
//The key name to use to set in the requst. If set to true, the backup files will be used to restore otherwise the source database name
|
||||||
|
internal const string ReadHeaderFromMedia = "readHeaderFromMedia";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates the options metadata available for restore operations
|
/// Creates the options metadata available for restore operations
|
||||||
@@ -187,5 +229,201 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
|||||||
|
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static Dictionary<string, RestorePlanDetailInfo> CreateRestorePlanOptions(IRestoreDatabaseTaskDataObject restoreDataObject)
|
||||||
|
{
|
||||||
|
Validate.IsNotNull(nameof(restoreDataObject), restoreDataObject);
|
||||||
|
|
||||||
|
Dictionary<string, RestorePlanDetailInfo> options = new Dictionary<string, RestorePlanDetailInfo>();
|
||||||
|
string databaseName = restoreDataObject.RestorePlan == null ? string.Empty : restoreDataObject.RestorePlan.DatabaseName;
|
||||||
|
//Files
|
||||||
|
|
||||||
|
// Default Data folder path in the target server
|
||||||
|
options.Add(RestoreOptionsHelper.DataFileFolder, RestorePlanDetailInfo.Create(
|
||||||
|
name: RestoreOptionsHelper.DataFileFolder,
|
||||||
|
currentValue: restoreDataObject.DataFilesFolder,
|
||||||
|
defaultValue: restoreDataObject.DefaultDataFileFolder,
|
||||||
|
isReadOnly: !restoreDataObject.RelocateAllFiles,
|
||||||
|
isVisible: true
|
||||||
|
));
|
||||||
|
|
||||||
|
// Default log folder path in the target server
|
||||||
|
options.Add(RestoreOptionsHelper.LogFileFolder, RestorePlanDetailInfo.Create(
|
||||||
|
name: RestoreOptionsHelper.LogFileFolder,
|
||||||
|
currentValue: restoreDataObject.LogFilesFolder,
|
||||||
|
defaultValue: restoreDataObject.DefaultLogFileFolder,
|
||||||
|
isReadOnly: !restoreDataObject.RelocateAllFiles,
|
||||||
|
isVisible: true
|
||||||
|
));
|
||||||
|
|
||||||
|
// Relocate all files
|
||||||
|
options.Add(RestoreOptionsHelper.RelocateDbFiles, RestorePlanDetailInfo.Create(
|
||||||
|
name: RestoreOptionsHelper.RelocateDbFiles,
|
||||||
|
currentValue: restoreDataObject.RelocateAllFiles,
|
||||||
|
defaultValue: false,
|
||||||
|
isReadOnly: restoreDataObject.DbFiles.Count == 0,
|
||||||
|
isVisible: true
|
||||||
|
));
|
||||||
|
|
||||||
|
|
||||||
|
//Options
|
||||||
|
|
||||||
|
//With Replace
|
||||||
|
options.Add(RestoreOptionsHelper.ReplaceDatabase, RestorePlanDetailInfo.Create(
|
||||||
|
name: RestoreOptionsHelper.ReplaceDatabase,
|
||||||
|
currentValue: restoreDataObject.RestoreOptions.ReplaceDatabase,
|
||||||
|
defaultValue: false,
|
||||||
|
isReadOnly: false,
|
||||||
|
isVisible: true
|
||||||
|
));
|
||||||
|
|
||||||
|
//Keep replication
|
||||||
|
options.Add(RestoreOptionsHelper.KeepReplication, RestorePlanDetailInfo.Create(
|
||||||
|
name: RestoreOptionsHelper.KeepReplication,
|
||||||
|
currentValue: restoreDataObject.RestoreOptions.KeepReplication,
|
||||||
|
defaultValue: false,
|
||||||
|
isReadOnly: restoreDataObject.RestoreOptions.RecoveryState == DatabaseRecoveryState.WithNoRecovery,
|
||||||
|
isVisible: true
|
||||||
|
));
|
||||||
|
|
||||||
|
//Restricted user
|
||||||
|
options.Add(RestoreOptionsHelper.SetRestrictedUser, RestorePlanDetailInfo.Create(
|
||||||
|
name: RestoreOptionsHelper.SetRestrictedUser,
|
||||||
|
currentValue: restoreDataObject.RestoreOptions.SetRestrictedUser,
|
||||||
|
defaultValue: false,
|
||||||
|
isReadOnly: false,
|
||||||
|
isVisible: true
|
||||||
|
));
|
||||||
|
|
||||||
|
//State recovery
|
||||||
|
options.Add(RestoreOptionsHelper.RecoveryState, RestorePlanDetailInfo.Create(
|
||||||
|
name: RestoreOptionsHelper.RecoveryState,
|
||||||
|
currentValue: restoreDataObject.RestoreOptions.RecoveryState.ToString(),
|
||||||
|
defaultValue: DatabaseRecoveryState.WithRecovery.ToString(),
|
||||||
|
isReadOnly: false,
|
||||||
|
isVisible: true
|
||||||
|
));
|
||||||
|
|
||||||
|
// stand by file path for when RESTORE WITH STANDBY is selected
|
||||||
|
options.Add(RestoreOptionsHelper.StandbyFile, RestorePlanDetailInfo.Create(
|
||||||
|
name: RestoreOptionsHelper.StandbyFile,
|
||||||
|
currentValue: restoreDataObject.RestoreOptions.StandByFile,
|
||||||
|
defaultValue: restoreDataObject.GetDefaultStandbyFile(databaseName),
|
||||||
|
isReadOnly: restoreDataObject.RestoreOptions.RecoveryState != DatabaseRecoveryState.WithStandBy,
|
||||||
|
isVisible: true
|
||||||
|
));
|
||||||
|
|
||||||
|
// Tail-log backup
|
||||||
|
// TODO:These methods are internal in SMO. after making them public, they can be removed from RestoreDatabaseTaskDataObject
|
||||||
|
bool isTailLogBackupPossible = restoreDataObject.IsTailLogBackupPossible(databaseName);
|
||||||
|
bool isTailLogBackupWithNoRecoveryPossible = restoreDataObject.IsTailLogBackupWithNoRecoveryPossible(databaseName);
|
||||||
|
|
||||||
|
options.Add(RestoreOptionsHelper.BackupTailLog, RestorePlanDetailInfo.Create(
|
||||||
|
name: RestoreOptionsHelper.BackupTailLog,
|
||||||
|
currentValue: restoreDataObject.BackupTailLog,
|
||||||
|
defaultValue: isTailLogBackupPossible,
|
||||||
|
isReadOnly: !isTailLogBackupPossible,
|
||||||
|
isVisible: true
|
||||||
|
));
|
||||||
|
|
||||||
|
options.Add(RestoreOptionsHelper.TailLogBackupFile, RestorePlanDetailInfo.Create(
|
||||||
|
name: RestoreOptionsHelper.TailLogBackupFile,
|
||||||
|
currentValue: restoreDataObject.TailLogBackupFile,
|
||||||
|
defaultValue: restoreDataObject.GetDefaultTailLogbackupFile(databaseName),
|
||||||
|
isReadOnly: !isTailLogBackupPossible,
|
||||||
|
isVisible: true
|
||||||
|
));
|
||||||
|
|
||||||
|
options.Add(RestoreOptionsHelper.TailLogWithNoRecovery, RestorePlanDetailInfo.Create(
|
||||||
|
name: RestoreOptionsHelper.TailLogWithNoRecovery,
|
||||||
|
currentValue: restoreDataObject.TailLogWithNoRecovery,
|
||||||
|
defaultValue: isTailLogBackupWithNoRecoveryPossible,
|
||||||
|
isReadOnly: !isTailLogBackupWithNoRecoveryPossible,
|
||||||
|
isVisible: true
|
||||||
|
));
|
||||||
|
|
||||||
|
|
||||||
|
//TODO: make the method public in SMO bool canDropExistingConnections = restoreDataObject.RestorePlan.CanDropExistingConnections(this.Data.RestorePlanner.DatabaseName);
|
||||||
|
options.Add(RestoreOptionsHelper.CloseExistingConnections, RestorePlanDetailInfo.Create(
|
||||||
|
name: RestoreOptionsHelper.CloseExistingConnections,
|
||||||
|
currentValue: restoreDataObject.CloseExistingConnections,
|
||||||
|
defaultValue: false,
|
||||||
|
isReadOnly: false, //TODO: !canDropExistingConnections
|
||||||
|
isVisible: true
|
||||||
|
));
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Add options to restore plan response
|
||||||
|
/// </summary>
|
||||||
|
internal static void AddOptions(RestorePlanResponse response, RestoreDatabaseTaskDataObject restoreDataObject)
|
||||||
|
{
|
||||||
|
Validate.IsNotNull(nameof(response), response);
|
||||||
|
Validate.IsNotNull(nameof(restoreDataObject), restoreDataObject);
|
||||||
|
Validate.IsNotNull(nameof(restoreDataObject.RestorePlanner), restoreDataObject.RestorePlanner);
|
||||||
|
|
||||||
|
|
||||||
|
var options = RestoreOptionsHelper.CreateRestorePlanOptions(restoreDataObject);
|
||||||
|
|
||||||
|
foreach (var item in options)
|
||||||
|
{
|
||||||
|
response.PlanDetails.Add(item.Key, item.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static T GetOptionValue<T>(string optionkey, Dictionary<string, RestorePlanDetailInfo> optionsMetadata, IRestoreDatabaseTaskDataObject restoreDataObject)
|
||||||
|
{
|
||||||
|
RestorePlanDetailInfo optionMetadata = null;
|
||||||
|
if(optionsMetadata.TryGetValue(optionkey, out optionMetadata))
|
||||||
|
{
|
||||||
|
if (!optionMetadata.IsReadOnly)
|
||||||
|
{
|
||||||
|
return restoreDataObject.RestoreParams.GetOptionValue<T>(optionkey);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (T)Convert.ChangeType(optionMetadata.DefaultValue, typeof(T));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return default(T);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load options in restore plan
|
||||||
|
/// </summary>
|
||||||
|
internal static void UpdateOptionsInPlan(IRestoreDatabaseTaskDataObject restoreDataObject)
|
||||||
|
{
|
||||||
|
var options = RestoreOptionsHelper.CreateRestorePlanOptions(restoreDataObject);
|
||||||
|
|
||||||
|
//Files
|
||||||
|
restoreDataObject.LogFilesFolder = GetOptionValue<string>(RestoreOptionsHelper.LogFileFolder, options, restoreDataObject);
|
||||||
|
restoreDataObject.DataFilesFolder = GetOptionValue<string>(RestoreOptionsHelper.DataFileFolder, options, restoreDataObject);
|
||||||
|
restoreDataObject.RelocateAllFiles = GetOptionValue<bool>(RestoreOptionsHelper.RelocateDbFiles, options, restoreDataObject);
|
||||||
|
|
||||||
|
//Options
|
||||||
|
object databaseRecoveryState;
|
||||||
|
|
||||||
|
string recoveryState = GetOptionValue<string>(RestoreOptionsHelper.RecoveryState, options, restoreDataObject);
|
||||||
|
if (Enum.TryParse(typeof(DatabaseRecoveryState), recoveryState, out databaseRecoveryState))
|
||||||
|
{
|
||||||
|
restoreDataObject.RestoreOptions.RecoveryState = (DatabaseRecoveryState)databaseRecoveryState;
|
||||||
|
}
|
||||||
|
restoreDataObject.RestoreOptions.KeepReplication = GetOptionValue<bool>(RestoreOptionsHelper.KeepReplication, options, restoreDataObject);
|
||||||
|
restoreDataObject.RestoreOptions.ReplaceDatabase = GetOptionValue<bool>(RestoreOptionsHelper.ReplaceDatabase, options, restoreDataObject);
|
||||||
|
restoreDataObject.RestoreOptions.SetRestrictedUser = GetOptionValue<bool>(RestoreOptionsHelper.SetRestrictedUser, options, restoreDataObject);
|
||||||
|
restoreDataObject.RestoreOptions.StandByFile = GetOptionValue<string>(RestoreOptionsHelper.StandbyFile, options, restoreDataObject);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
restoreDataObject.BackupTailLog = GetOptionValue<bool>(RestoreOptionsHelper.BackupTailLog, options, restoreDataObject);
|
||||||
|
restoreDataObject.TailLogBackupFile = GetOptionValue<string>(RestoreOptionsHelper.TailLogBackupFile, options, restoreDataObject);
|
||||||
|
restoreDataObject.TailLogWithNoRecovery = GetOptionValue<bool>(RestoreOptionsHelper.TailLogWithNoRecovery, options, restoreDataObject);
|
||||||
|
|
||||||
|
restoreDataObject.CloseExistingConnections = GetOptionValue<bool>(RestoreOptionsHelper.CloseExistingConnections, options, restoreDataObject);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
|
|||||||
private DisasterRecoveryService service;
|
private DisasterRecoveryService service;
|
||||||
private string fullBackupFilePath;
|
private string fullBackupFilePath;
|
||||||
private string[] backupFilesToRecoverDatabase;
|
private string[] backupFilesToRecoverDatabase;
|
||||||
|
private string databaseNameToRestoreFrom;
|
||||||
|
|
||||||
//The table names used in the script to create backup files for a database
|
//The table names used in the script to create backup files for a database
|
||||||
//Each table is created after a backup script to verify recovering to different states
|
//Each table is created after a backup script to verify recovering to different states
|
||||||
@@ -84,6 +85,27 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
|
|||||||
await VerifyRestoreMultipleBackupSets(backupFiles, indexToDelete, expectedTable);
|
await VerifyRestoreMultipleBackupSets(backupFiles, indexToDelete, expectedTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void RestoreShouldRestoreFromAnotherDatabase()
|
||||||
|
{
|
||||||
|
await GetBackupFilesToRecoverDatabaseCreated();
|
||||||
|
|
||||||
|
var testDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, null, "RestoreTest");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Dictionary<string, object> options = new Dictionary<string, object>();
|
||||||
|
options.Add(RestoreOptionsHelper.ReplaceDatabase, true);
|
||||||
|
await VerifyRestore(null, databaseNameToRestoreFrom, true, true, testDb.DatabaseName, null, options, (database) =>
|
||||||
|
{
|
||||||
|
return database.Tables.Contains("tb1", "test");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
testDb.Cleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async void RestoreShouldRestoreTheBackupSetsThatAreSelected()
|
public async void RestoreShouldRestoreTheBackupSetsThatAreSelected()
|
||||||
{
|
{
|
||||||
@@ -114,7 +136,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
|
|||||||
{
|
{
|
||||||
string targetDbName = testDb.DatabaseName;
|
string targetDbName = testDb.DatabaseName;
|
||||||
bool canRestore = true;
|
bool canRestore = true;
|
||||||
var response = await VerifyRestore(backupFiles, canRestore, false, targetDbName, null, null);
|
var response = await VerifyRestore(backupFiles, null, canRestore, false, targetDbName, null, null);
|
||||||
Assert.True(response.BackupSetsToRestore.Count() >= 2);
|
Assert.True(response.BackupSetsToRestore.Count() >= 2);
|
||||||
var allIds = response.BackupSetsToRestore.Select(x => x.Id).ToList();
|
var allIds = response.BackupSetsToRestore.Select(x => x.Id).ToList();
|
||||||
if (backupSetIndexToDelete >= 0)
|
if (backupSetIndexToDelete >= 0)
|
||||||
@@ -124,7 +146,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
|
|||||||
string[] selectedIds = allIds.ToArray();
|
string[] selectedIds = allIds.ToArray();
|
||||||
Dictionary<string, object> options = new Dictionary<string, object>();
|
Dictionary<string, object> options = new Dictionary<string, object>();
|
||||||
options.Add(RestoreOptionsHelper.ReplaceDatabase, true);
|
options.Add(RestoreOptionsHelper.ReplaceDatabase, true);
|
||||||
response = await VerifyRestore(backupFiles, canRestore, true, targetDbName, selectedIds, options, (database) =>
|
response = await VerifyRestore(backupFiles, null, canRestore, true, targetDbName, selectedIds, options, (database) =>
|
||||||
{
|
{
|
||||||
bool tablesFound = true;
|
bool tablesFound = true;
|
||||||
for (int i = 0; i < tableNames.Length; i++)
|
for (int i = 0; i < tableNames.Length; i++)
|
||||||
@@ -168,7 +190,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
|
|||||||
Dictionary<string, object> options = new Dictionary<string, object>();
|
Dictionary<string, object> options = new Dictionary<string, object>();
|
||||||
options.Add(RestoreOptionsHelper.ReplaceDatabase, true);
|
options.Add(RestoreOptionsHelper.ReplaceDatabase, true);
|
||||||
|
|
||||||
await VerifyRestore(new string[] { fullBackupFilePath }, canRestore, true, testDb.DatabaseName, null, options);
|
await VerifyRestore(new string[] { fullBackupFilePath }, null, canRestore, true, testDb.DatabaseName, null, options);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -190,7 +212,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
|
|||||||
await VerifyBackupFileCreated();
|
await VerifyBackupFileCreated();
|
||||||
bool canRestore = true;
|
bool canRestore = true;
|
||||||
|
|
||||||
await VerifyRestore(new string[] { fullBackupFilePath }, canRestore, false, testDb.DatabaseName, null, null);
|
await VerifyRestore(new string[] { fullBackupFilePath }, null, canRestore, false, testDb.DatabaseName, null, null);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -207,7 +229,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
|
|||||||
|
|
||||||
string[] backupFileNames = new string[] { "FullBackup.bak", "DiffBackup.bak" };
|
string[] backupFileNames = new string[] { "FullBackup.bak", "DiffBackup.bak" };
|
||||||
bool canRestore = true;
|
bool canRestore = true;
|
||||||
var response = await VerifyRestore(backupFileNames, canRestore, false, "RestoredFromTwoBackupFile");
|
var response = await VerifyRestore(backupFileNames, null, canRestore, false, "RestoredFromTwoBackupFile");
|
||||||
Assert.True(response.BackupSetsToRestore.Count() == 2);
|
Assert.True(response.BackupSetsToRestore.Count() == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,13 +239,13 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
|
|||||||
|
|
||||||
string[] backupFileNames = new string[] { "FullBackup.bak", "DiffBackup.bak" };
|
string[] backupFileNames = new string[] { "FullBackup.bak", "DiffBackup.bak" };
|
||||||
bool canRestore = true;
|
bool canRestore = true;
|
||||||
var response = await VerifyRestore(backupFileNames, canRestore, false, "RestoredFromTwoBackupFile");
|
var response = await VerifyRestore(backupFileNames, null, canRestore, false, "RestoredFromTwoBackupFile");
|
||||||
Assert.True(response.BackupSetsToRestore.Count() == 2);
|
Assert.True(response.BackupSetsToRestore.Count() == 2);
|
||||||
var fileInfo = response.BackupSetsToRestore.FirstOrDefault(x => x.GetPropertyValueAsString(BackupSetInfo.BackupTypePropertyName) != RestoreConstants.TypeFull);
|
var fileInfo = response.BackupSetsToRestore.FirstOrDefault(x => x.GetPropertyValueAsString(BackupSetInfo.BackupTypePropertyName) != RestoreConstants.TypeFull);
|
||||||
if(fileInfo != null)
|
if(fileInfo != null)
|
||||||
{
|
{
|
||||||
var selectedBackupSets = new string[] { fileInfo.Id };
|
var selectedBackupSets = new string[] { fileInfo.Id };
|
||||||
await VerifyRestore(backupFileNames, true, false, "RestoredFromTwoBackupFile", selectedBackupSets);
|
await VerifyRestore(backupFileNames, null, true, false, "RestoredFromTwoBackupFile", selectedBackupSets);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,13 +255,13 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
|
|||||||
|
|
||||||
string[] backupFileNames = new string[] { "FullBackup.bak", "DiffBackup.bak" };
|
string[] backupFileNames = new string[] { "FullBackup.bak", "DiffBackup.bak" };
|
||||||
bool canRestore = true;
|
bool canRestore = true;
|
||||||
var response = await VerifyRestore(backupFileNames, canRestore, false, "RestoredFromTwoBackupFile");
|
var response = await VerifyRestore(backupFileNames, null, canRestore, false, "RestoredFromTwoBackupFile");
|
||||||
Assert.True(response.BackupSetsToRestore.Count() == 2);
|
Assert.True(response.BackupSetsToRestore.Count() == 2);
|
||||||
var fileInfo = response.BackupSetsToRestore.FirstOrDefault(x => x.GetPropertyValueAsString(BackupSetInfo.BackupTypePropertyName) == RestoreConstants.TypeFull);
|
var fileInfo = response.BackupSetsToRestore.FirstOrDefault(x => x.GetPropertyValueAsString(BackupSetInfo.BackupTypePropertyName) == RestoreConstants.TypeFull);
|
||||||
if (fileInfo != null)
|
if (fileInfo != null)
|
||||||
{
|
{
|
||||||
var selectedBackupSets = new string[] { fileInfo.Id };
|
var selectedBackupSets = new string[] { fileInfo.Id };
|
||||||
await VerifyRestore(backupFileNames, true, false, "RestoredFromTwoBackupFile2", selectedBackupSets);
|
await VerifyRestore(backupFileNames, null, true, false, "RestoredFromTwoBackupFile2", selectedBackupSets);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,6 +328,32 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RestoreConfigInfoRequestShouldReturnResponse()
|
||||||
|
{
|
||||||
|
await VerifyBackupFileCreated();
|
||||||
|
|
||||||
|
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
||||||
|
{
|
||||||
|
TestConnectionResult connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
|
||||||
|
|
||||||
|
string filePath = GetBackupFilePath(fullBackupFilePath);
|
||||||
|
|
||||||
|
RestoreConfigInfoRequestParams restoreParams = new RestoreConfigInfoRequestParams
|
||||||
|
{
|
||||||
|
OwnerUri = queryTempFile.FilePath
|
||||||
|
};
|
||||||
|
|
||||||
|
await RunAndVerify<RestoreConfigInfoResponse>(
|
||||||
|
test: (requestContext) => service.HandleRestoreConfigInfoRequest(restoreParams, requestContext),
|
||||||
|
verify: ((result) =>
|
||||||
|
{
|
||||||
|
Assert.True(result.ConfigInfo.Any());
|
||||||
|
Assert.True(result.ConfigInfo.ContainsKey(RestoreOptionsHelper.SourceDatabaseNamesWithBackupSets));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task RestoreDatabaseRequestShouldStartTheRestoreTask()
|
public async Task RestoreDatabaseRequestShouldStartTheRestoreTask()
|
||||||
{
|
{
|
||||||
@@ -368,36 +416,53 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
|
|||||||
|
|
||||||
private async Task<RestorePlanResponse> VerifyRestore(string backupFileName, bool canRestore, bool execute = false, string targetDatabase = null)
|
private async Task<RestorePlanResponse> VerifyRestore(string backupFileName, bool canRestore, bool execute = false, string targetDatabase = null)
|
||||||
{
|
{
|
||||||
return await VerifyRestore(new string[] { backupFileName }, canRestore, execute, targetDatabase);
|
return await VerifyRestore(new string[] { backupFileName }, null, canRestore, execute, targetDatabase);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<RestorePlanResponse> VerifyRestore(
|
private async Task<RestorePlanResponse> VerifyRestore(
|
||||||
string[] backupFileNames,
|
string[] backupFileNames = null,
|
||||||
bool canRestore,
|
string sourceDbName = null,
|
||||||
|
bool canRestore = true,
|
||||||
bool execute = false,
|
bool execute = false,
|
||||||
string targetDatabase = null,
|
string targetDatabase = null,
|
||||||
string[] selectedBackupSets = null,
|
string[] selectedBackupSets = null,
|
||||||
Dictionary<string, object> options = null,
|
Dictionary<string, object> options = null,
|
||||||
Func<Database, bool> verifyDatabase = null)
|
Func<Database, bool> verifyDatabase = null)
|
||||||
{
|
{
|
||||||
var filePaths = backupFileNames.Select(x => GetBackupFilePath(x));
|
string backUpFilePath = string.Empty;
|
||||||
string backUpFilePath = filePaths.Aggregate((current, next) => current + " ," + next);
|
if (backupFileNames != null)
|
||||||
|
{
|
||||||
|
var filePaths = backupFileNames.Select(x => GetBackupFilePath(x));
|
||||||
|
backUpFilePath = filePaths.Aggregate((current, next) => current + " ," + next);
|
||||||
|
}
|
||||||
|
|
||||||
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
||||||
{
|
{
|
||||||
TestConnectionResult connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
|
TestConnectionResult connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
|
||||||
|
|
||||||
RestoreDatabaseHelper service = new RestoreDatabaseHelper();
|
RestoreDatabaseHelper service = new RestoreDatabaseHelper();
|
||||||
|
|
||||||
|
// If source database is sepecified verfiy it's part of source db list
|
||||||
|
if(!string.IsNullOrEmpty(sourceDbName))
|
||||||
|
{
|
||||||
|
RestoreConfigInfoResponse configInfoResponse = service.CreateConfigInfoResponse(new RestoreConfigInfoRequestParams
|
||||||
|
{
|
||||||
|
OwnerUri = queryTempFile.FilePath
|
||||||
|
});
|
||||||
|
IEnumerable<string> dbNames = configInfoResponse.ConfigInfo[RestoreOptionsHelper.SourceDatabaseNamesWithBackupSets] as IEnumerable<string>;
|
||||||
|
Assert.True(dbNames.Any(x => x == sourceDbName));
|
||||||
|
}
|
||||||
var request = new RestoreParams
|
var request = new RestoreParams
|
||||||
{
|
{
|
||||||
BackupFilePaths = backUpFilePath,
|
BackupFilePaths = backUpFilePath,
|
||||||
TargetDatabaseName = targetDatabase,
|
TargetDatabaseName = targetDatabase,
|
||||||
OwnerUri = queryTempFile.FilePath,
|
OwnerUri = queryTempFile.FilePath,
|
||||||
SelectedBackupSets = selectedBackupSets
|
SelectedBackupSets = selectedBackupSets,
|
||||||
|
SourceDatabaseName = sourceDbName
|
||||||
};
|
};
|
||||||
|
request.Options[RestoreOptionsHelper.ReadHeaderFromMedia] = backupFileNames != null;
|
||||||
|
|
||||||
if(options != null)
|
if (options != null)
|
||||||
{
|
{
|
||||||
foreach (var item in options)
|
foreach (var item in options)
|
||||||
{
|
{
|
||||||
@@ -424,35 +489,45 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
|
|||||||
Assert.Equal(response.DatabaseName, targetDatabase);
|
Assert.Equal(response.DatabaseName, targetDatabase);
|
||||||
Assert.NotNull(response.PlanDetails);
|
Assert.NotNull(response.PlanDetails);
|
||||||
Assert.True(response.PlanDetails.Any());
|
Assert.True(response.PlanDetails.Any());
|
||||||
Assert.NotNull(response.PlanDetails[RestoreOptionsHelper.DefaultBackupTailLog]);
|
Assert.NotNull(response.PlanDetails[RestoreOptionsHelper.BackupTailLog]);
|
||||||
Assert.NotNull(response.PlanDetails[RestoreOptionsHelper.DefaultTailLogBackupFile]);
|
Assert.NotNull(response.PlanDetails[RestoreOptionsHelper.TailLogBackupFile]);
|
||||||
Assert.NotNull(response.PlanDetails[RestoreOptionsHelper.DefaultDataFileFolder]);
|
Assert.NotNull(response.PlanDetails[RestoreOptionsHelper.DataFileFolder]);
|
||||||
Assert.NotNull(response.PlanDetails[RestoreOptionsHelper.DefaultLogFileFolder]);
|
Assert.NotNull(response.PlanDetails[RestoreOptionsHelper.LogFileFolder]);
|
||||||
Assert.NotNull(response.PlanDetails[RestoreOptionsHelper.DefaultStandbyFile]);
|
Assert.NotNull(response.PlanDetails[RestoreOptionsHelper.StandbyFile]);
|
||||||
Assert.NotNull(response.PlanDetails[RestoreOptionsHelper.DefaultStandbyFile]);
|
Assert.NotNull(response.PlanDetails[RestoreOptionsHelper.StandbyFile]);
|
||||||
|
|
||||||
if(execute)
|
if(execute)
|
||||||
{
|
{
|
||||||
request.SessionId = response.SessionId;
|
try
|
||||||
restoreDataObject = service.CreateRestoreDatabaseTaskDataObject(request);
|
|
||||||
Assert.Equal(response.SessionId, restoreDataObject.SessionId);
|
|
||||||
request.RelocateDbFiles = !restoreDataObject.DbFilesLocationAreValid();
|
|
||||||
service.ExecuteRestore(restoreDataObject);
|
|
||||||
Assert.True(restoreDataObject.Server.Databases.Contains(targetDatabase));
|
|
||||||
|
|
||||||
if(verifyDatabase != null)
|
|
||||||
{
|
{
|
||||||
Assert.True(verifyDatabase(restoreDataObject.Server.Databases[targetDatabase]));
|
request.SessionId = response.SessionId;
|
||||||
}
|
restoreDataObject = service.CreateRestoreDatabaseTaskDataObject(request);
|
||||||
|
Assert.Equal(response.SessionId, restoreDataObject.SessionId);
|
||||||
|
request.RelocateDbFiles = !restoreDataObject.DbFilesLocationAreValid();
|
||||||
|
service.ExecuteRestore(restoreDataObject);
|
||||||
|
Assert.True(restoreDataObject.Server.Databases.Contains(targetDatabase));
|
||||||
|
|
||||||
|
if (verifyDatabase != null)
|
||||||
|
{
|
||||||
|
Assert.True(verifyDatabase(restoreDataObject.Server.Databases[targetDatabase]));
|
||||||
|
}
|
||||||
|
|
||||||
|
//To verify the backupset that are restored, verifying the database is a better options.
|
||||||
|
//Some tests still verify the number of backup sets that are executed which in some cases can be less than the selected list
|
||||||
|
if (verifyDatabase == null && selectedBackupSets != null)
|
||||||
|
{
|
||||||
|
Assert.Equal(selectedBackupSets.Count(), restoreDataObject.RestorePlanToExecute.RestoreOperations.Count());
|
||||||
|
}
|
||||||
|
|
||||||
//To verify the backupset that are restored, verifying the database is a better options.
|
|
||||||
//Some tests still verify the number of backup sets that are executed which in some cases can be less than the selected list
|
|
||||||
if (verifyDatabase == null && selectedBackupSets != null)
|
|
||||||
{
|
|
||||||
Assert.Equal(selectedBackupSets.Count(), restoreDataObject.RestorePlanToExecute.RestoreOperations.Count());
|
|
||||||
}
|
}
|
||||||
|
catch(Exception ex)
|
||||||
await DropDatabase(targetDatabase);
|
{
|
||||||
|
Assert.False(true, ex.Message);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
await DropDatabase(targetDatabase);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -514,8 +589,6 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
|
|||||||
List<string> backupFiles = new List<string>();
|
List<string> backupFiles = new List<string>();
|
||||||
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
string query = $"CREATE SCHEMA [test]";
|
string query = $"CREATE SCHEMA [test]";
|
||||||
SqlTestDb testDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, query, "RestoreTest");
|
SqlTestDb testDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, query, "RestoreTest");
|
||||||
string databaseName = testDb.DatabaseName;
|
string databaseName = testDb.DatabaseName;
|
||||||
@@ -560,6 +633,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
|
|||||||
await TestServiceProvider.Instance.RunQueryAsync(TestServerType.OnPrem, "master", query);
|
await TestServiceProvider.Instance.RunQueryAsync(TestServerType.OnPrem, "master", query);
|
||||||
backupFiles.Add(backupPath);
|
backupFiles.Add(backupPath);
|
||||||
|
|
||||||
|
databaseNameToRestoreFrom = testDb.DatabaseName;
|
||||||
// Clean up the database
|
// Clean up the database
|
||||||
testDb.Cleanup();
|
testDb.Cleanup();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,275 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
//
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.SqlServer.Management.Smo;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery
|
||||||
|
{
|
||||||
|
public class RestoreOptionsHelperTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void VerifyOptionsCreatedSuccessfullyIsResponse()
|
||||||
|
{
|
||||||
|
GeneralRequestDetails optionValues = CreateOptionsTestData();
|
||||||
|
Mock<IRestoreDatabaseTaskDataObject> restoreDatabaseTaskDataObject = CreateRestoreDatabaseTaskDataObject(optionValues);
|
||||||
|
|
||||||
|
Dictionary<string, RestorePlanDetailInfo> result = RestoreOptionsHelper.CreateRestorePlanOptions(restoreDatabaseTaskDataObject.Object);
|
||||||
|
Assert.NotNull(result);
|
||||||
|
VerifyOptions(result, optionValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RelocateAllFilesShouldBeReadOnlyGivenNoDbFiles()
|
||||||
|
{
|
||||||
|
GeneralRequestDetails optionValues = CreateOptionsTestData();
|
||||||
|
optionValues.Options["DbFiles"] = new List<DbFile>();
|
||||||
|
Mock <IRestoreDatabaseTaskDataObject> restoreDatabaseTaskDataObject = CreateRestoreDatabaseTaskDataObject(optionValues);
|
||||||
|
|
||||||
|
Dictionary<string, RestorePlanDetailInfo> result = RestoreOptionsHelper.CreateRestorePlanOptions(restoreDatabaseTaskDataObject.Object);
|
||||||
|
Assert.NotNull(result);
|
||||||
|
VerifyOptions(result, optionValues);
|
||||||
|
Assert.True(result[RestoreOptionsHelper.RelocateDbFiles].IsReadOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BackupTailLogShouldBeReadOnlyTailLogBackupNotPossible()
|
||||||
|
{
|
||||||
|
GeneralRequestDetails optionValues = CreateOptionsTestData();
|
||||||
|
optionValues.Options["IsTailLogBackupPossible"] = false;
|
||||||
|
Mock<IRestoreDatabaseTaskDataObject> restoreDatabaseTaskDataObject = CreateRestoreDatabaseTaskDataObject(optionValues);
|
||||||
|
|
||||||
|
Dictionary<string, RestorePlanDetailInfo> result = RestoreOptionsHelper.CreateRestorePlanOptions(restoreDatabaseTaskDataObject.Object);
|
||||||
|
Assert.NotNull(result);
|
||||||
|
VerifyOptions(result, optionValues);
|
||||||
|
Assert.True(result[RestoreOptionsHelper.BackupTailLog].IsReadOnly);
|
||||||
|
Assert.True(result[RestoreOptionsHelper.TailLogBackupFile].IsReadOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TailLogWithNoRecoveryShouldBeReadOnlyTailLogBackupWithNoRecoveryNotPossible()
|
||||||
|
{
|
||||||
|
GeneralRequestDetails optionValues = CreateOptionsTestData();
|
||||||
|
optionValues.Options["IsTailLogBackupWithNoRecoveryPossible"] = false;
|
||||||
|
Mock<IRestoreDatabaseTaskDataObject> restoreDatabaseTaskDataObject = CreateRestoreDatabaseTaskDataObject(optionValues);
|
||||||
|
|
||||||
|
Dictionary<string, RestorePlanDetailInfo> result = RestoreOptionsHelper.CreateRestorePlanOptions(restoreDatabaseTaskDataObject.Object);
|
||||||
|
Assert.NotNull(result);
|
||||||
|
VerifyOptions(result, optionValues);
|
||||||
|
Assert.True(result[RestoreOptionsHelper.TailLogWithNoRecovery].IsReadOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void StandbyFileShouldNotBeReadOnlyGivenRecoveryStateWithStandBy()
|
||||||
|
{
|
||||||
|
GeneralRequestDetails optionValues = CreateOptionsTestData();
|
||||||
|
optionValues.Options[RestoreOptionsHelper.RecoveryState] = DatabaseRecoveryState.WithStandBy;
|
||||||
|
Mock<IRestoreDatabaseTaskDataObject> restoreDatabaseTaskDataObject = CreateRestoreDatabaseTaskDataObject(optionValues);
|
||||||
|
|
||||||
|
Dictionary<string, RestorePlanDetailInfo> result = RestoreOptionsHelper.CreateRestorePlanOptions(restoreDatabaseTaskDataObject.Object);
|
||||||
|
Assert.NotNull(result);
|
||||||
|
VerifyOptions(result, optionValues);
|
||||||
|
Assert.False(result[RestoreOptionsHelper.StandbyFile].IsReadOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void KeepReplicationShouldNotBeReadOnlyGivenRecoveryStateWithNoRecovery()
|
||||||
|
{
|
||||||
|
GeneralRequestDetails optionValues = CreateOptionsTestData();
|
||||||
|
optionValues.Options[RestoreOptionsHelper.RecoveryState] = DatabaseRecoveryState.WithNoRecovery;
|
||||||
|
Mock<IRestoreDatabaseTaskDataObject> restoreDatabaseTaskDataObject = CreateRestoreDatabaseTaskDataObject(optionValues);
|
||||||
|
|
||||||
|
Dictionary<string, RestorePlanDetailInfo> result = RestoreOptionsHelper.CreateRestorePlanOptions(restoreDatabaseTaskDataObject.Object);
|
||||||
|
Assert.NotNull(result);
|
||||||
|
VerifyOptions(result, optionValues);
|
||||||
|
Assert.True(result[RestoreOptionsHelper.KeepReplication].IsReadOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void KeepReplicationShouldSetToDefaultValueGivenRecoveryStateWithNoRecovery()
|
||||||
|
{
|
||||||
|
RestoreParams restoreParams = CreateOptionsTestData();
|
||||||
|
restoreParams.Options[RestoreOptionsHelper.RecoveryState] = DatabaseRecoveryState.WithNoRecovery;
|
||||||
|
|
||||||
|
Mock<IRestoreDatabaseTaskDataObject> restoreDatabaseTaskDataObject = CreateRestoreDatabaseTaskDataObject(restoreParams);
|
||||||
|
Dictionary<string, RestorePlanDetailInfo> options = RestoreOptionsHelper.CreateRestorePlanOptions(restoreDatabaseTaskDataObject.Object);
|
||||||
|
|
||||||
|
restoreParams.Options[RestoreOptionsHelper.KeepReplication] = true;
|
||||||
|
|
||||||
|
bool actual = RestoreOptionsHelper.GetOptionValue<bool>(RestoreOptionsHelper.KeepReplication, options, restoreDatabaseTaskDataObject.Object);
|
||||||
|
bool expected = (bool)options[RestoreOptionsHelper.KeepReplication].DefaultValue;
|
||||||
|
|
||||||
|
Assert.Equal(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void KeepReplicationShouldSetToValueInRequestGivenRecoveryStateWithRecovery()
|
||||||
|
{
|
||||||
|
RestoreParams restoreParams = CreateOptionsTestData();
|
||||||
|
|
||||||
|
restoreParams.Options[RestoreOptionsHelper.RecoveryState] = DatabaseRecoveryState.WithRecovery;
|
||||||
|
Mock<IRestoreDatabaseTaskDataObject> restoreDatabaseTaskDataObject = CreateRestoreDatabaseTaskDataObject(restoreParams);
|
||||||
|
Dictionary<string, RestorePlanDetailInfo> options = RestoreOptionsHelper.CreateRestorePlanOptions(restoreDatabaseTaskDataObject.Object);
|
||||||
|
|
||||||
|
restoreParams.Options[RestoreOptionsHelper.KeepReplication] = true;
|
||||||
|
|
||||||
|
bool actual = RestoreOptionsHelper.GetOptionValue<bool>(RestoreOptionsHelper.KeepReplication, options, restoreDatabaseTaskDataObject.Object);
|
||||||
|
bool expected = true;
|
||||||
|
Assert.Equal(actual, expected);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private RestoreParams CreateOptionsTestData()
|
||||||
|
{
|
||||||
|
RestoreParams optionValues = new RestoreParams();
|
||||||
|
optionValues.Options.Add(RestoreOptionsHelper.CloseExistingConnections, false);
|
||||||
|
optionValues.Options.Add(RestoreOptionsHelper.DataFileFolder, "Data file folder");
|
||||||
|
optionValues.Options.Add("DbFiles", new List<DbFile>() { new DbFile("", '1', "") });
|
||||||
|
optionValues.Options.Add("DefaultDataFileFolder", "Default data file folder");
|
||||||
|
optionValues.Options.Add("DefaultLogFileFolder", "Default log file folder");
|
||||||
|
optionValues.Options.Add("IsTailLogBackupPossible", true);
|
||||||
|
optionValues.Options.Add("IsTailLogBackupWithNoRecoveryPossible", true);
|
||||||
|
optionValues.Options.Add("GetDefaultStandbyFile", "default standby file");
|
||||||
|
optionValues.Options.Add("GetDefaultTailLogbackupFile", "default tail log backup file");
|
||||||
|
optionValues.Options.Add("LogFilesFolder", "Log file folder");
|
||||||
|
optionValues.Options.Add("RelocateAllFiles", false);
|
||||||
|
optionValues.Options.Add("TailLogBackupFile", "tail log backup file");
|
||||||
|
optionValues.Options.Add("TailLogWithNoRecovery", false);
|
||||||
|
optionValues.Options.Add("BackupTailLog", false);
|
||||||
|
optionValues.Options.Add(RestoreOptionsHelper.KeepReplication, false);
|
||||||
|
optionValues.Options.Add("ReplaceDatabase", false);
|
||||||
|
optionValues.Options.Add("SetRestrictedUser", false);
|
||||||
|
optionValues.Options.Add("StandbyFile", "Stand by file");
|
||||||
|
optionValues.Options.Add(RestoreOptionsHelper.RecoveryState, DatabaseRecoveryState.WithNoRecovery.ToString());
|
||||||
|
return optionValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Mock<IRestoreDatabaseTaskDataObject> CreateRestoreDatabaseTaskDataObject(GeneralRequestDetails optionValues)
|
||||||
|
{
|
||||||
|
var restoreDataObject = new Mock<IRestoreDatabaseTaskDataObject>();
|
||||||
|
restoreDataObject.Setup(x => x.CloseExistingConnections).Returns(optionValues.GetOptionValue<bool>(RestoreOptionsHelper.CloseExistingConnections));
|
||||||
|
restoreDataObject.Setup(x => x.DataFilesFolder).Returns(optionValues.GetOptionValue<string>(RestoreOptionsHelper.DataFileFolder));
|
||||||
|
restoreDataObject.Setup(x => x.DbFiles).Returns(optionValues.GetOptionValue<List<DbFile>>("DbFiles"));
|
||||||
|
restoreDataObject.Setup(x => x.DefaultDataFileFolder).Returns(optionValues.GetOptionValue<string>("DefaultDataFileFolder"));
|
||||||
|
restoreDataObject.Setup(x => x.DefaultLogFileFolder).Returns(optionValues.GetOptionValue<string>("DefaultLogFileFolder"));
|
||||||
|
restoreDataObject.Setup(x => x.IsTailLogBackupPossible(It.IsAny<string>())).Returns(optionValues.GetOptionValue<bool>("IsTailLogBackupPossible"));
|
||||||
|
restoreDataObject.Setup(x => x.IsTailLogBackupWithNoRecoveryPossible(It.IsAny<string>())).Returns(optionValues.GetOptionValue<bool>("IsTailLogBackupWithNoRecoveryPossible"));
|
||||||
|
restoreDataObject.Setup(x => x.GetDefaultStandbyFile(It.IsAny<string>())).Returns(optionValues.GetOptionValue<string>("GetDefaultStandbyFile"));
|
||||||
|
restoreDataObject.Setup(x => x.GetDefaultTailLogbackupFile(It.IsAny<string>())).Returns(optionValues.GetOptionValue<string>("GetDefaultTailLogbackupFile"));
|
||||||
|
restoreDataObject.Setup(x => x.LogFilesFolder).Returns(optionValues.GetOptionValue<string>("LogFilesFolder"));
|
||||||
|
restoreDataObject.Setup(x => x.RelocateAllFiles).Returns(optionValues.GetOptionValue<bool>("RelocateAllFiles"));
|
||||||
|
restoreDataObject.Setup(x => x.TailLogBackupFile).Returns(optionValues.GetOptionValue<string>("TailLogBackupFile"));
|
||||||
|
restoreDataObject.Setup(x => x.TailLogWithNoRecovery).Returns(optionValues.GetOptionValue<bool>("TailLogWithNoRecovery"));
|
||||||
|
restoreDataObject.Setup(x => x.BackupTailLog).Returns(optionValues.GetOptionValue<bool>("BackupTailLog"));
|
||||||
|
restoreDataObject.Setup(x => x.RestoreParams).Returns(optionValues as RestoreParams);
|
||||||
|
restoreDataObject.Setup(x => x.RestorePlan).Returns(() => null);
|
||||||
|
RestoreOptions restoreOptions = new RestoreOptions();
|
||||||
|
restoreOptions.KeepReplication = optionValues.GetOptionValue<bool>(RestoreOptionsHelper.KeepReplication);
|
||||||
|
restoreOptions.ReplaceDatabase = optionValues.GetOptionValue<bool>("ReplaceDatabase");
|
||||||
|
restoreOptions.SetRestrictedUser = optionValues.GetOptionValue<bool>("SetRestrictedUser");
|
||||||
|
restoreOptions.StandByFile = optionValues.GetOptionValue<string>("StandbyFile");
|
||||||
|
restoreOptions.RecoveryState = optionValues.GetOptionValue<DatabaseRecoveryState>(RestoreOptionsHelper.RecoveryState);
|
||||||
|
restoreDataObject.Setup(x => x.RestoreOptions).Returns(restoreOptions);
|
||||||
|
|
||||||
|
|
||||||
|
return restoreDataObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void VerifyOptions(Dictionary<string, RestorePlanDetailInfo> optionInResponse, GeneralRequestDetails optionValues)
|
||||||
|
{
|
||||||
|
RestorePlanDetailInfo planDetailInfo = optionInResponse[RestoreOptionsHelper.DataFileFolder];
|
||||||
|
Assert.Equal(planDetailInfo.Name, RestoreOptionsHelper.DataFileFolder);
|
||||||
|
Assert.Equal(planDetailInfo.IsReadOnly, !optionValues.GetOptionValue<bool>("RelocateAllFiles"));
|
||||||
|
Assert.Equal(planDetailInfo.CurrentValue, optionValues.GetOptionValue<string>(RestoreOptionsHelper.DataFileFolder));
|
||||||
|
Assert.Equal(planDetailInfo.DefaultValue, optionValues.GetOptionValue<string>("DefaultDataFileFolder"));
|
||||||
|
Assert.Equal(planDetailInfo.IsVisiable, true);
|
||||||
|
|
||||||
|
planDetailInfo = optionInResponse[RestoreOptionsHelper.LogFileFolder];
|
||||||
|
Assert.Equal(planDetailInfo.Name, RestoreOptionsHelper.LogFileFolder);
|
||||||
|
Assert.Equal(planDetailInfo.IsReadOnly, !optionValues.GetOptionValue<bool>("RelocateAllFiles"));
|
||||||
|
Assert.Equal(planDetailInfo.CurrentValue, optionValues.GetOptionValue<string>("LogFilesFolder"));
|
||||||
|
Assert.Equal(planDetailInfo.DefaultValue, optionValues.GetOptionValue<string>("DefaultLogFileFolder"));
|
||||||
|
Assert.Equal(planDetailInfo.IsVisiable, true);
|
||||||
|
|
||||||
|
planDetailInfo = optionInResponse[RestoreOptionsHelper.RelocateDbFiles];
|
||||||
|
Assert.Equal(planDetailInfo.Name, RestoreOptionsHelper.RelocateDbFiles);
|
||||||
|
Assert.Equal(planDetailInfo.IsReadOnly, (optionValues.GetOptionValue<List<DbFile>>("DbFiles").Count == 0));
|
||||||
|
Assert.Equal(planDetailInfo.CurrentValue, optionValues.GetOptionValue<bool>("LogFilesFolder"));
|
||||||
|
Assert.Equal(planDetailInfo.DefaultValue, false);
|
||||||
|
Assert.Equal(planDetailInfo.IsVisiable, true);
|
||||||
|
|
||||||
|
planDetailInfo = optionInResponse[RestoreOptionsHelper.ReplaceDatabase];
|
||||||
|
Assert.Equal(planDetailInfo.Name, RestoreOptionsHelper.ReplaceDatabase);
|
||||||
|
Assert.Equal(planDetailInfo.IsReadOnly, false);
|
||||||
|
Assert.Equal(planDetailInfo.CurrentValue, optionValues.GetOptionValue<bool>("ReplaceDatabase"));
|
||||||
|
Assert.Equal(planDetailInfo.DefaultValue, false);
|
||||||
|
Assert.Equal(planDetailInfo.IsVisiable, true);
|
||||||
|
|
||||||
|
planDetailInfo = optionInResponse[RestoreOptionsHelper.KeepReplication];
|
||||||
|
Assert.Equal(planDetailInfo.Name, RestoreOptionsHelper.KeepReplication);
|
||||||
|
Assert.Equal(planDetailInfo.IsReadOnly, optionValues.GetOptionValue<DatabaseRecoveryState>(RestoreOptionsHelper.RecoveryState) == DatabaseRecoveryState.WithNoRecovery);
|
||||||
|
Assert.Equal(planDetailInfo.CurrentValue, optionValues.GetOptionValue<bool>(RestoreOptionsHelper.KeepReplication));
|
||||||
|
Assert.Equal(planDetailInfo.DefaultValue, false);
|
||||||
|
Assert.Equal(planDetailInfo.IsVisiable, true);
|
||||||
|
|
||||||
|
planDetailInfo = optionInResponse[RestoreOptionsHelper.SetRestrictedUser];
|
||||||
|
Assert.Equal(planDetailInfo.Name, RestoreOptionsHelper.SetRestrictedUser);
|
||||||
|
Assert.Equal(planDetailInfo.IsReadOnly, false);
|
||||||
|
Assert.Equal(planDetailInfo.CurrentValue, optionValues.GetOptionValue<bool>("SetRestrictedUser"));
|
||||||
|
Assert.Equal(planDetailInfo.DefaultValue, false);
|
||||||
|
Assert.Equal(planDetailInfo.IsVisiable, true);
|
||||||
|
|
||||||
|
planDetailInfo = optionInResponse[RestoreOptionsHelper.RecoveryState];
|
||||||
|
Assert.Equal(planDetailInfo.Name, RestoreOptionsHelper.RecoveryState);
|
||||||
|
Assert.Equal(planDetailInfo.IsReadOnly, false);
|
||||||
|
Assert.Equal(planDetailInfo.CurrentValue, optionValues.GetOptionValue<DatabaseRecoveryState>(RestoreOptionsHelper.RecoveryState).ToString());
|
||||||
|
Assert.Equal(planDetailInfo.DefaultValue, DatabaseRecoveryState.WithRecovery.ToString());
|
||||||
|
Assert.Equal(planDetailInfo.IsVisiable, true);
|
||||||
|
|
||||||
|
planDetailInfo = optionInResponse[RestoreOptionsHelper.StandbyFile];
|
||||||
|
Assert.Equal(planDetailInfo.Name, RestoreOptionsHelper.StandbyFile);
|
||||||
|
Assert.Equal(planDetailInfo.IsReadOnly, optionValues.GetOptionValue<DatabaseRecoveryState>(RestoreOptionsHelper.RecoveryState) != DatabaseRecoveryState.WithStandBy);
|
||||||
|
Assert.Equal(planDetailInfo.CurrentValue, optionValues.GetOptionValue<string>("StandbyFile"));
|
||||||
|
Assert.Equal(planDetailInfo.DefaultValue, optionValues.GetOptionValue<string>("GetDefaultStandbyFile"));
|
||||||
|
Assert.Equal(planDetailInfo.IsVisiable, true);
|
||||||
|
|
||||||
|
planDetailInfo = optionInResponse[RestoreOptionsHelper.BackupTailLog];
|
||||||
|
Assert.Equal(planDetailInfo.Name, RestoreOptionsHelper.BackupTailLog);
|
||||||
|
Assert.Equal(planDetailInfo.IsReadOnly, !optionValues.GetOptionValue<bool>("IsTailLogBackupPossible"));
|
||||||
|
Assert.Equal(planDetailInfo.CurrentValue, optionValues.GetOptionValue<bool>("BackupTailLog"));
|
||||||
|
Assert.Equal(planDetailInfo.DefaultValue, optionValues.GetOptionValue<bool>("IsTailLogBackupPossible"));
|
||||||
|
Assert.Equal(planDetailInfo.IsVisiable, true);
|
||||||
|
|
||||||
|
planDetailInfo = optionInResponse[RestoreOptionsHelper.TailLogBackupFile];
|
||||||
|
Assert.Equal(planDetailInfo.Name, RestoreOptionsHelper.TailLogBackupFile);
|
||||||
|
Assert.Equal(planDetailInfo.IsReadOnly, !optionValues.GetOptionValue<bool>("IsTailLogBackupPossible"));
|
||||||
|
Assert.Equal(planDetailInfo.CurrentValue, optionValues.GetOptionValue<string>("TailLogBackupFile"));
|
||||||
|
Assert.Equal(planDetailInfo.DefaultValue, optionValues.GetOptionValue<string>("GetDefaultTailLogbackupFile"));
|
||||||
|
Assert.Equal(planDetailInfo.IsVisiable, true);
|
||||||
|
|
||||||
|
planDetailInfo = optionInResponse[RestoreOptionsHelper.TailLogWithNoRecovery];
|
||||||
|
Assert.Equal(planDetailInfo.Name, RestoreOptionsHelper.TailLogWithNoRecovery);
|
||||||
|
Assert.Equal(planDetailInfo.IsReadOnly, !optionValues.GetOptionValue<bool>("IsTailLogBackupWithNoRecoveryPossible"));
|
||||||
|
Assert.Equal(planDetailInfo.CurrentValue, optionValues.GetOptionValue<bool>("TailLogWithNoRecovery"));
|
||||||
|
Assert.Equal(planDetailInfo.DefaultValue, optionValues.GetOptionValue<bool>("IsTailLogBackupWithNoRecoveryPossible"));
|
||||||
|
Assert.Equal(planDetailInfo.IsVisiable, true);
|
||||||
|
|
||||||
|
planDetailInfo = optionInResponse[RestoreOptionsHelper.CloseExistingConnections];
|
||||||
|
Assert.Equal(planDetailInfo.Name, RestoreOptionsHelper.CloseExistingConnections);
|
||||||
|
Assert.Equal(planDetailInfo.IsReadOnly, false);
|
||||||
|
Assert.Equal(planDetailInfo.CurrentValue, optionValues.GetOptionValue<bool>("CloseExistingConnections"));
|
||||||
|
Assert.Equal(planDetailInfo.DefaultValue, false);
|
||||||
|
Assert.Equal(planDetailInfo.IsVisiable, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user