mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-20 01:25:41 -05:00
Backup in-progress work (#347)
* Initial checkin of backup service implementation * Backup initial changes for the quick scenario * misc changes * Misc changes for backup in-progress work
This commit is contained in:
@@ -0,0 +1,360 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.ComponentModel;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.SqlServer.Management.Sdk.Sfc;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.SqlTools.ServiceLayer.Common;
|
||||
using Microsoft.Data.Tools.DataSets;
|
||||
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
{
|
||||
public class BackupFactory
|
||||
{
|
||||
private CDataContainer dataContainer;
|
||||
private ServerConnection serverConnection;
|
||||
private BackupRestoreUtil backupRestoreUtil = null;
|
||||
private UrlControl urlControl;
|
||||
|
||||
/// <summary>
|
||||
/// Constants
|
||||
/// </summary>
|
||||
private const int constDeviceTypeFile = 2;
|
||||
private const int constDeviceTypeTape = 5;
|
||||
private const int constDeviceTypeMediaSet = 3;
|
||||
|
||||
/// <summary>
|
||||
/// UI input values
|
||||
/// </summary>
|
||||
private BackupInfo backupInfo;
|
||||
public BackupComponent backupComponent { get; set; }
|
||||
public BackupType backupType { get; set; } // 0 for Full, 1 for Differential, 2 for Log
|
||||
public BackupDeviceType backupDeviceType { get; set; }
|
||||
|
||||
private BackupActionType backupActionType = BackupActionType.Database;
|
||||
private bool IsBackupIncremental = false;
|
||||
private bool isLocalPrimaryReplica;
|
||||
|
||||
/// this is used when the backup dialog is launched in the context of a backup device
|
||||
/// The InitialBackupDestination will be loaded in LoadData
|
||||
private string initialBackupDestination = string.Empty;
|
||||
|
||||
// Helps in populating the properties of an Azure blob given its URI
|
||||
private class BlobProperties
|
||||
{
|
||||
private string containerName;
|
||||
|
||||
public string ContainerName
|
||||
{
|
||||
get { return this.containerName; }
|
||||
}
|
||||
|
||||
private string fileName;
|
||||
|
||||
public string FileName
|
||||
{
|
||||
get { return this.fileName; }
|
||||
}
|
||||
|
||||
public BlobProperties(Uri blobUri)
|
||||
{
|
||||
// Extracts the container name and the filename from URI of the strict format https://<StorageAccount_Path>/<ContainerName>/<FileName>
|
||||
// The input URI should be well formed (Current used context - URI is read from msdb - well formed)
|
||||
|
||||
this.containerName = string.Empty;
|
||||
this.fileName = string.Empty;
|
||||
if (blobUri == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
string[] seg = blobUri.Segments;
|
||||
if (seg.Length >= 2)
|
||||
{
|
||||
this.containerName = seg[1].Replace("/", "");
|
||||
}
|
||||
if (seg.Length >= 3)
|
||||
{
|
||||
this.fileName = seg[2].Replace("/", "");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#region ctors
|
||||
|
||||
/// <summary>
|
||||
/// Ctor
|
||||
/// </summary>
|
||||
public BackupFactory()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize variables
|
||||
/// </summary>
|
||||
/// <param name="dataContainer"></param>
|
||||
/// <param name="sqlConnection"></param>
|
||||
/// <param name="input"></param>
|
||||
public void Initialize(CDataContainer dataContainer, SqlConnection sqlConnection, BackupInfo input)
|
||||
{
|
||||
this.dataContainer = dataContainer;
|
||||
this.serverConnection = new ServerConnection(sqlConnection); // @@ check the value!
|
||||
this.backupRestoreUtil = new BackupRestoreUtil(this.dataContainer, this.serverConnection);
|
||||
//this.urlControl.SqlServer = dataContainer.Server;
|
||||
this.backupInfo = input;
|
||||
|
||||
// convert the types
|
||||
this.backupComponent = (BackupComponent)input.BackupComponent;
|
||||
this.backupType = (BackupType)input.BackupType;
|
||||
this.backupDeviceType = (BackupDeviceType)input.BackupDeviceType;
|
||||
|
||||
if (this.backupRestoreUtil.IsHADRDatabase(this.backupInfo.DatabaseName))
|
||||
{
|
||||
this.isLocalPrimaryReplica = this.backupRestoreUtil.IsLocalPrimaryReplica(this.backupInfo.DatabaseName);
|
||||
}
|
||||
|
||||
//TODO: when is backup device not null?
|
||||
//bStatus = param.GetParam("backupdevice", ref this.initialBackupDestination);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods for UI logic
|
||||
|
||||
// Return recovery model of the current database
|
||||
private string GetRecoveryModel()
|
||||
{
|
||||
RecoveryModel recoveryModel = this.backupRestoreUtil.GetRecoveryModel(this.backupInfo.DatabaseName);
|
||||
return recoveryModel.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return true if backup to URL is supported in the current SQL Server version
|
||||
/// </summary>
|
||||
private bool BackupToUrlSupported()
|
||||
{
|
||||
return BackupRestoreBase.IsBackupUrlDeviceSupported(this.dataContainer.Server.PingSqlServerVersion(this.dataContainer.ServerName)); //@@ originally, DataContainer.Server.ServerVersion
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private string GetDefaultBackupSetName()
|
||||
{
|
||||
string bkpsetName = this.backupInfo.DatabaseName + "-"
|
||||
+ this.backupType.ToString() + " "
|
||||
+ this.backupComponent.ToString() + " "
|
||||
+ BackupConstants.Backup;
|
||||
return bkpsetName;
|
||||
}
|
||||
|
||||
private void SetBackupProps()
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (this.backupType)
|
||||
{
|
||||
case BackupType.Full:
|
||||
if (this.backupComponent == BackupComponent.Database) // define the value as const!!
|
||||
{
|
||||
this.backupActionType = BackupActionType.Database;
|
||||
}
|
||||
else if ((this.backupComponent == BackupComponent.Files) && (null != this.backupInfo.SelectedFileGroup) && (this.backupInfo.SelectedFileGroup.Count > 0))
|
||||
{
|
||||
this.backupActionType = BackupActionType.Files;
|
||||
}
|
||||
this.IsBackupIncremental = false;
|
||||
break;
|
||||
case BackupType.Differential:
|
||||
if ((this.backupComponent == BackupComponent.Files) && (0 != this.backupInfo.SelectedFiles.Length))
|
||||
{
|
||||
this.backupActionType = BackupActionType.Files;
|
||||
this.IsBackupIncremental = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.backupActionType = BackupActionType.Database;
|
||||
this.IsBackupIncremental = true;
|
||||
}
|
||||
break;
|
||||
case BackupType.TransactionLog:
|
||||
this.backupActionType = BackupActionType.Log;
|
||||
this.IsBackupIncremental = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
//throw new Exception("Unexpected error");
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the backup properties from the general tab
|
||||
/// </summary>
|
||||
public void PerformBackup()
|
||||
{
|
||||
// Set backup action
|
||||
this.SetBackupProps();
|
||||
Backup bk = new Backup();
|
||||
try
|
||||
{
|
||||
bk.Database = this.backupInfo.DatabaseName;
|
||||
bk.Action = this.backupActionType;
|
||||
bk.Incremental = this.IsBackupIncremental;
|
||||
if (bk.Action == BackupActionType.Files)
|
||||
{
|
||||
IDictionaryEnumerator IEnum = this.backupInfo.SelectedFileGroup.GetEnumerator();
|
||||
IEnum.Reset();
|
||||
while (IEnum.MoveNext())
|
||||
{
|
||||
string CurrentKey = Convert.ToString(IEnum.Key,
|
||||
System.Globalization.CultureInfo.InvariantCulture);
|
||||
string CurrentValue = Convert.ToString(IEnum.Value,
|
||||
System.Globalization.CultureInfo.InvariantCulture);
|
||||
if (CurrentKey.IndexOf(",", StringComparison.Ordinal) < 0)
|
||||
{
|
||||
// is a file group
|
||||
bk.DatabaseFileGroups.Add(CurrentValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
// is a file
|
||||
int Idx = CurrentValue.IndexOf(".", StringComparison.Ordinal);
|
||||
CurrentValue = CurrentValue.Substring(Idx + 1, CurrentValue.Length - Idx - 1);
|
||||
bk.DatabaseFiles.Add(CurrentValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool bBackupToUrl = false;
|
||||
if (this.backupDeviceType == BackupDeviceType.Url)
|
||||
{
|
||||
bBackupToUrl = true;
|
||||
}
|
||||
|
||||
bk.BackupSetName = this.backupInfo.BackupsetName;
|
||||
|
||||
if (false == bBackupToUrl)
|
||||
{
|
||||
for (int i = 0; i < this.backupInfo.BackupPathList.Count; i++)
|
||||
{
|
||||
string DestName = Convert.ToString(this.backupInfo.BackupPathList[i], System.Globalization.CultureInfo.InvariantCulture);
|
||||
int deviceType = (int)(this.backupInfo.arChangesList[DestName]);
|
||||
switch (deviceType)
|
||||
{
|
||||
case (int)DeviceType.LogicalDevice:
|
||||
int backupDeviceType =
|
||||
GetDeviceType(Convert.ToString(DestName,
|
||||
System.Globalization.CultureInfo.InvariantCulture));
|
||||
|
||||
if ((this.backupDeviceType == BackupDeviceType.Disk && backupDeviceType == constDeviceTypeFile)
|
||||
|| (this.backupDeviceType == BackupDeviceType.Tape && backupDeviceType == constDeviceTypeTape))
|
||||
{
|
||||
bk.Devices.AddDevice(DestName, DeviceType.LogicalDevice);
|
||||
}
|
||||
break;
|
||||
case (int)DeviceType.File:
|
||||
if (this.backupDeviceType == BackupDeviceType.Disk)
|
||||
{
|
||||
bk.Devices.AddDevice(DestName, DeviceType.File);
|
||||
}
|
||||
break;
|
||||
case (int)DeviceType.Tape:
|
||||
if (this.backupDeviceType == BackupDeviceType.Tape)
|
||||
{
|
||||
bk.Devices.AddDevice(DestName, DeviceType.Tape);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*if (this.urlControl.ListBakDestUrls.Count > 0)
|
||||
{
|
||||
// Append the URL filename to the URL prefix
|
||||
foreach (string urlPath in this.urlControl.ListBakDestUrls.ToArray())
|
||||
{
|
||||
if (!String.IsNullOrWhiteSpace(urlPath))
|
||||
{
|
||||
bk.Devices.AddDevice(urlPath, DeviceType.Url);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
/*
|
||||
if (this.dataContainer.HashTable.ContainsKey(bk.BackupSetName))
|
||||
{
|
||||
this.dataContainer.HashTable.Remove(bk.BackupSetName);
|
||||
}
|
||||
this.dataContainer.HashTable.Add(bk.BackupSetName, bk);*/
|
||||
|
||||
//TODO: This should be changed to get user inputs
|
||||
bk.FormatMedia = false;
|
||||
bk.Initialize = false;
|
||||
bk.SkipTapeHeader = true;
|
||||
bk.Checksum = false;
|
||||
bk.ContinueAfterError = false;
|
||||
bk.LogTruncation = BackupTruncateLogType.Truncate;
|
||||
|
||||
// Execute backup
|
||||
bk.SqlBackup(this.dataContainer.Server);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private ArrayList getBackupDestinationList()
|
||||
{
|
||||
//TODO: return the latest backup destination paths to show to UI dialog
|
||||
return null;
|
||||
}
|
||||
|
||||
private int GetDeviceType(string deviceName)
|
||||
{
|
||||
Enumerator en = new Enumerator();
|
||||
Request req = new Request();
|
||||
DataSet ds = new DataSet();
|
||||
ds.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
||||
int Result = -1;
|
||||
SqlExecutionModes execMode = this.serverConnection.SqlExecutionModes;
|
||||
this.serverConnection.SqlExecutionModes = SqlExecutionModes.ExecuteSql;
|
||||
try
|
||||
{
|
||||
req.Urn = "Server/BackupDevice[@Name='" + Urn.EscapeString(deviceName) + "']";
|
||||
req.Fields = new string[1];
|
||||
req.Fields[0] = "BackupDeviceType";
|
||||
ds = en.Process(this.serverConnection, req);
|
||||
int iCount = ds.Tables[0].Rows.Count;
|
||||
if (iCount > 0)
|
||||
{
|
||||
Result = Convert.ToInt16(ds.Tables[0].Rows[0]["BackupDeviceType"],
|
||||
System.Globalization.CultureInfo.InvariantCulture);
|
||||
return Result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return constDeviceTypeMediaSet;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.serverConnection.SqlExecutionModes = execMode;
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,198 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.SqlServer.Management.Diagnostics;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Common
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Provides connection and enumerator context for a node
|
||||
/// </summary>
|
||||
public interface IManagedConnection : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Connection information.
|
||||
/// </summary>
|
||||
SqlOlapConnectionInfoBase Connection
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Free any resources for this connection
|
||||
/// </summary>
|
||||
void Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// interface used by the objectexplorer. Allows us to "pool" the main connection
|
||||
/// </summary>
|
||||
internal interface IManagedConnection2 : IManagedConnection
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of IManagedConnection. Allows the use of a direct or indirect connection
|
||||
/// in the object explorer that takes care of the connection.
|
||||
/// </summary>
|
||||
internal class ManagedConnection : IManagedConnection2
|
||||
{
|
||||
#region private members
|
||||
private bool connectionAddedToActiveConnections = false;
|
||||
private bool closeOnDispose = false;
|
||||
private SqlOlapConnectionInfoBase connection;
|
||||
private bool closed = false;
|
||||
#endregion
|
||||
|
||||
#region Construction
|
||||
/// <summary>
|
||||
/// Create a new managed connection
|
||||
/// </summary>
|
||||
/// <param name="connection">connection wish to manage</param>
|
||||
public ManagedConnection(SqlOlapConnectionInfoBase connection)
|
||||
: this(connection, false)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// create a new managed connection.
|
||||
/// </summary>
|
||||
/// <param name="connection">connection</param>
|
||||
/// <param name="attemptToPool">true if we are going to try and reuse the
|
||||
/// connection if possible</param>
|
||||
public ManagedConnection(SqlOlapConnectionInfoBase sourceConnection, bool attemptToPool)
|
||||
{
|
||||
// parameter check
|
||||
if (sourceConnection == null)
|
||||
{
|
||||
throw new ArgumentNullException("sourceConnection");
|
||||
}
|
||||
|
||||
// see if the connection can restrict access (single user mode)
|
||||
IRestrictedAccess access = sourceConnection as IRestrictedAccess;
|
||||
// see if it is cloneable
|
||||
ICloneable cloneable = sourceConnection as ICloneable;
|
||||
lock (ActiveConnections)
|
||||
{
|
||||
// if it's not single user mode then we can see if the object can be cloned
|
||||
if (access == null || access.SingleConnection == false)
|
||||
{
|
||||
// if we are going to attempt to pool, see if the connection is in use
|
||||
if (attemptToPool && !ActiveConnections.Contains(SharedConnectionUtil.GetConnectionKeyName(sourceConnection)))
|
||||
{
|
||||
// add it to the hashtable to indicate use.
|
||||
ActiveConnections.Add(SharedConnectionUtil.GetConnectionKeyName(sourceConnection), sourceConnection);
|
||||
this.connection = sourceConnection;
|
||||
this.closeOnDispose = false;
|
||||
this.connectionAddedToActiveConnections = true;
|
||||
}
|
||||
else if (cloneable != null)
|
||||
{
|
||||
this.connection = (SqlOlapConnectionInfoBase)cloneable.Clone();
|
||||
this.closeOnDispose = true;
|
||||
}
|
||||
else if (sourceConnection is SqlConnectionInfoWithConnection)
|
||||
{
|
||||
this.connection = ((SqlConnectionInfoWithConnection)sourceConnection).Copy();
|
||||
this.closeOnDispose = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if everything else has failed just use to passed in connection.
|
||||
if (this.connection == null)
|
||||
{
|
||||
this.connection = sourceConnection;
|
||||
}
|
||||
|
||||
// always set the lock timeout to prevent the shell from not responding
|
||||
if (this.connection is SqlConnectionInfoWithConnection)
|
||||
{
|
||||
// set lock_timeout to 10 seconds
|
||||
((SqlConnectionInfoWithConnection)this.connection).ServerConnection.LockTimeout = 10;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable implementation
|
||||
public void Dispose()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IManagedConnection implementation
|
||||
/// <summary>
|
||||
/// Connection
|
||||
/// </summary>
|
||||
public SqlOlapConnectionInfoBase Connection
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.connection;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close the current connection if applicable.
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
if (this.closed)
|
||||
return;
|
||||
|
||||
if (this.closeOnDispose)
|
||||
{
|
||||
IDisposable disp = this.connection as IDisposable;
|
||||
if (disp != null)
|
||||
{
|
||||
disp.Dispose();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we are not closing the connection and it is a sql connection then ensure it
|
||||
// is left in the master database.
|
||||
SqlConnectionInfoWithConnection sqlConnection = this.connection as SqlConnectionInfoWithConnection;
|
||||
if (sqlConnection != null && sqlConnection.ServerConnection.DatabaseEngineType == DatabaseEngineType.Standalone)
|
||||
{
|
||||
try
|
||||
{
|
||||
sqlConnection.ServerConnection.ExecuteNonQuery("use [master]");
|
||||
}
|
||||
// don't error if this fails
|
||||
catch
|
||||
{ }
|
||||
}
|
||||
}
|
||||
if (this.connectionAddedToActiveConnections)
|
||||
{
|
||||
lock (ActiveConnections)
|
||||
{
|
||||
ActiveConnections.Remove(SharedConnectionUtil.GetConnectionKeyName(connection));
|
||||
}
|
||||
}
|
||||
|
||||
this.connection = null;
|
||||
this.closed = true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region static helpers
|
||||
/// <summary>
|
||||
/// hashtable we use to keep track of actively used main connections
|
||||
/// </summary>
|
||||
private static Hashtable activeConnections = null;
|
||||
private Hashtable ActiveConnections
|
||||
{
|
||||
get
|
||||
{
|
||||
if (activeConnections == null)
|
||||
{
|
||||
activeConnections = new Hashtable();
|
||||
}
|
||||
return activeConnections;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// To access DataModelingSandbox from NavigableItem
|
||||
/// </summary>
|
||||
public interface ISandboxLoader
|
||||
{
|
||||
/// <summary>
|
||||
/// Get sandbox
|
||||
/// </summary>
|
||||
/// <returns>DataModelingSandbox object associated with this NavigableItem</returns>
|
||||
object GetSandbox();
|
||||
|
||||
/// <summary>
|
||||
/// Refresh sandbox data associated with this NavigableItem
|
||||
/// </summary>
|
||||
void RefreshSandboxData();
|
||||
|
||||
/// <summary>
|
||||
/// Delete sandbox from cache
|
||||
/// </summary>
|
||||
void DeleteSandbox();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,488 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Xml;
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// SqlTools Parameters, used to define what goes into starting up a Workbench Form
|
||||
/// AKA a dbCommander, AKA a "dialog"
|
||||
/// These parameters are xml snippets
|
||||
/// </summary>
|
||||
public class STParameters
|
||||
{
|
||||
public XmlDocument m_doc;
|
||||
|
||||
/// <summary>
|
||||
/// The data type we are interested in
|
||||
/// </summary>
|
||||
public enum STType { eNULL, eInt, eLong, eString };
|
||||
|
||||
/// <summary>
|
||||
/// default constructor
|
||||
/// </summary>
|
||||
public STParameters()
|
||||
{
|
||||
//
|
||||
// TODO: Add constructor logic here
|
||||
//
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="xmlDoc">The xml snippet used to control the dbCommander</param>
|
||||
public STParameters(XmlDocument xmlDoc)
|
||||
{
|
||||
m_doc = xmlDoc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changing the xml snippet we are using
|
||||
/// </summary>
|
||||
/// <param name="xmlDoc">the new xml snippet</param>
|
||||
public void SetDocument(XmlDocument xmlDoc)
|
||||
{
|
||||
m_doc = xmlDoc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Access to the xml we are using for dbCommander parameters
|
||||
/// </summary>
|
||||
/// <returns>our current parameters</returns>
|
||||
public XmlDocument GetDocument()
|
||||
{
|
||||
return m_doc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Search for an xml tag, and return its value
|
||||
/// </summary>
|
||||
/// <param name="parameterName">the xml tag name</param>
|
||||
/// <param name="value">the value of that tag</param>
|
||||
/// <returns>flag that is true if the data was found, false if not</returns>
|
||||
public bool GetBaseParam(string parameterName, ref object value)
|
||||
{
|
||||
XmlNodeList nodeList = null;
|
||||
bool parameterExists;
|
||||
|
||||
if (m_doc == null)
|
||||
return false;
|
||||
|
||||
parameterExists = false;
|
||||
nodeList = m_doc.GetElementsByTagName(parameterName);
|
||||
|
||||
if (nodeList.Count > 1)
|
||||
{
|
||||
value = null;
|
||||
}
|
||||
else if (nodeList.Count != 0) // anything there?
|
||||
{
|
||||
try
|
||||
{
|
||||
XmlNode node = nodeList.Item(0);
|
||||
if (null != node)
|
||||
{
|
||||
value = node.InnerText as object;
|
||||
parameterExists = true;
|
||||
}
|
||||
}
|
||||
catch (Exception /*e*/)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return parameterExists;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds an existing xml tag, and sets it to a new value, or if the tag is not found
|
||||
/// create it and set it's value
|
||||
/// </summary>
|
||||
/// <param name="parameterName">tag name</param>
|
||||
/// <param name="value">new value</param>
|
||||
/// <returns>flag that is true if the tag was set, false if not</returns>
|
||||
public bool SetBaseParam(string parameterName, object value)
|
||||
{
|
||||
XmlNodeList nodeList;
|
||||
bool success = false;
|
||||
|
||||
nodeList = m_doc.GetElementsByTagName(parameterName);
|
||||
|
||||
if (nodeList.Count == 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
nodeList.Item(0).InnerText = (string)value;
|
||||
success = true;
|
||||
}
|
||||
catch (InvalidCastException /*e*/)
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (nodeList.Count == 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
XmlElement xmlElement = m_doc.CreateElement(parameterName);
|
||||
XmlNode root = m_doc.DocumentElement;
|
||||
|
||||
nodeList = m_doc.GetElementsByTagName("params");
|
||||
|
||||
if (nodeList.Count == 1 && value is string)
|
||||
{
|
||||
xmlElement.InnerText = (string)value;
|
||||
nodeList.Item(0).InsertAfter(xmlElement, nodeList.Item(0).LastChild);
|
||||
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
string sz = e.ToString();
|
||||
success = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return success;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get back an interger parameter.
|
||||
/// NOTE: if the tag exists, but it contains non-numeric data, this will throw
|
||||
/// An exception of type 'System.FormatException'
|
||||
/// with Additional information: Could not find any parsible digits.
|
||||
/// </summary>
|
||||
/// <param name="parameterName">xml tag name for the parameter of interest</param>
|
||||
/// <param name="iValue">out value of parameter</param>
|
||||
/// <returns>flag that is true if the data was found, false if not</returns>
|
||||
public bool GetParam(string parameterName, out int value)
|
||||
{
|
||||
bool parameterExists = false;
|
||||
|
||||
value = 0;
|
||||
|
||||
try
|
||||
{
|
||||
object oAux = null;
|
||||
|
||||
if (parameterExists = GetBaseParam(parameterName, ref oAux))
|
||||
{
|
||||
try
|
||||
{
|
||||
value = Convert.ToInt32((string)oAux, 10); // data is always a string, it is the value of XmlNode.InnerText
|
||||
}
|
||||
catch (FormatException e)
|
||||
{
|
||||
Debug.WriteLine(String.Format(System.Globalization.CultureInfo.CurrentCulture, "Non numeric data in tag: {0}", parameterName));
|
||||
Debug.WriteLine(e.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InvalidCastException /*e*/)
|
||||
{
|
||||
}
|
||||
|
||||
return parameterExists;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Accessor for a boolean parameter
|
||||
/// NOTE: if the tag exists, but it contains non-numeric data, this will throw
|
||||
/// An exception of type 'System.FormatException'
|
||||
/// with Additional information: Could not find any parsible digits.
|
||||
/// </summary>
|
||||
/// <param name="parameterName">xml tag name for the parameter of interest</param>
|
||||
/// <param name="value">out value of parameter</param>
|
||||
/// <returns>flag that is true if the data was found, false if not</returns>
|
||||
public bool GetParam(string parameterName, ref bool value)
|
||||
{
|
||||
bool parameterExists = false;
|
||||
|
||||
value = false;
|
||||
|
||||
try
|
||||
{
|
||||
object oAux = null;
|
||||
if (parameterExists = GetBaseParam(parameterName, ref oAux))
|
||||
{
|
||||
try
|
||||
{
|
||||
value = Convert.ToBoolean((string)oAux, System.Globalization.CultureInfo.InvariantCulture); // data is always a string, it is the value of XmlNode.InnerText
|
||||
}
|
||||
catch (FormatException e)
|
||||
{
|
||||
Debug.WriteLine(String.Format(System.Globalization.CultureInfo.CurrentCulture, "Non boolean data in tag: {0}", parameterName));
|
||||
Debug.WriteLine(e.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InvalidCastException /*e*/)
|
||||
{
|
||||
}
|
||||
|
||||
return parameterExists;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Accessor to a string parameter
|
||||
/// </summary>
|
||||
/// <param name="parameterName">xml tag name for the parameter of interest</param>
|
||||
/// <param name="value">out value of parameter</param>
|
||||
/// <returns>flag that is true if the data was found, false if not</returns>
|
||||
public bool GetParam(string parameterName, ref string value)
|
||||
{
|
||||
bool parameterExists;
|
||||
|
||||
value = "";
|
||||
parameterExists = false;
|
||||
|
||||
try
|
||||
{
|
||||
object oAux = null;
|
||||
|
||||
if (parameterExists = GetBaseParam(parameterName, ref oAux))
|
||||
{
|
||||
value = (string)oAux;
|
||||
}
|
||||
}
|
||||
catch (InvalidCastException /*e*/)
|
||||
{
|
||||
}
|
||||
|
||||
return parameterExists;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Accessor to long parameter (Int64)
|
||||
/// NOTE: if the tag exists, but it contains non-numeric data, this will throw
|
||||
/// An exception of type 'System.FormatException'
|
||||
/// with Additional information: Could not find any parsible digits.
|
||||
/// </summary>
|
||||
/// <param name="parameterName">xml tag name for the parameter of interest</param>
|
||||
/// <param name="value">out value of parameter</param>
|
||||
/// <returns>flag that is true if the data was found, false if not</returns>
|
||||
public bool GetParam(string parameterName, out long value)
|
||||
{
|
||||
bool parameterExists = false;
|
||||
|
||||
value = 0;
|
||||
|
||||
try
|
||||
{
|
||||
object oAux = null;
|
||||
|
||||
if (parameterExists = GetBaseParam(parameterName, ref oAux))
|
||||
{
|
||||
try
|
||||
{
|
||||
value = Convert.ToInt64((string)oAux, 10); // data is always a string, it is the value of XmlNode.InnerText
|
||||
}
|
||||
catch (FormatException e)
|
||||
{
|
||||
Debug.WriteLine(String.Format(System.Globalization.CultureInfo.CurrentCulture, "Non numeric data in tag: {0}", parameterName));
|
||||
Debug.WriteLine(e.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InvalidCastException /*e*/)
|
||||
{
|
||||
}
|
||||
|
||||
return parameterExists;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Set an int (Int32) parameter
|
||||
/// </summary>
|
||||
/// <param name="parameterName">tag name for parameter</param>
|
||||
/// <param name="value">integer value</param>
|
||||
/// <returns>true if set was successful, false if not</returns>
|
||||
public bool SetParam(string parameterName, int value)
|
||||
{
|
||||
bool success;
|
||||
|
||||
success = SetBaseParam(parameterName, (object)value);
|
||||
|
||||
return success;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a string parameter
|
||||
/// </summary>
|
||||
/// <param name="parameterName">tag name for parameter</param>
|
||||
/// <param name="value">string value</param>
|
||||
/// <returns>true if set was successful, false if not</returns>
|
||||
public bool SetParam(string parameterName, string value)
|
||||
{
|
||||
bool success;
|
||||
|
||||
success = SetBaseParam(parameterName, (object)value);
|
||||
|
||||
return success;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a long (Int64) parameter
|
||||
/// </summary>
|
||||
/// <param name="parameterName">tag name for parameter</param>
|
||||
/// <param name="value">long value</param>
|
||||
/// <returns>true if set was successful, false if not</returns>
|
||||
public bool SetParam(string parameterName, long value)
|
||||
{
|
||||
bool success;
|
||||
|
||||
success = SetBaseParam(parameterName, (object)value);
|
||||
|
||||
return success;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a string collection parameter
|
||||
/// </summary>
|
||||
/// <param name="parameterName">name of collection</param>
|
||||
/// <param name="list">collection that gets filled up with parameters</param>
|
||||
/// <param name="getInnerXml">true if we want to get at inner nodes, false if not</param>
|
||||
/// <returns>true if parameter(s) exist</returns>
|
||||
public bool GetParam(string parameterName, System.Collections.Specialized.StringCollection list, bool getInnerXml)
|
||||
{
|
||||
/// necessary for OALP objects path that is in an XML form
|
||||
if (true == getInnerXml)
|
||||
{
|
||||
XmlNodeList nodeList;
|
||||
bool parameterExists;
|
||||
long lCount;
|
||||
|
||||
parameterExists = false;
|
||||
nodeList = m_doc.GetElementsByTagName(parameterName);
|
||||
|
||||
list.Clear();
|
||||
|
||||
lCount = nodeList.Count;
|
||||
|
||||
if (lCount > 0)
|
||||
{
|
||||
parameterExists = true;
|
||||
|
||||
for (long i = 0; i < lCount; i++)
|
||||
{
|
||||
list.Add(nodeList.Item((int)i).InnerXml);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
parameterExists = false;
|
||||
}
|
||||
|
||||
return parameterExists;
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetParam(parameterName, list);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Access to a collection of parameters
|
||||
/// </summary>
|
||||
/// <param name="parameterName">name of collection</param>
|
||||
/// <param name="list">list to fill with parameters</param>
|
||||
/// <returns>parameter(s) exist</returns>
|
||||
public bool GetParam(string parameterName, System.Collections.Specialized.StringCollection list)
|
||||
{
|
||||
XmlNodeList nodeList;
|
||||
bool parameterExists;
|
||||
long lCount;
|
||||
|
||||
parameterExists = false;
|
||||
nodeList = m_doc.GetElementsByTagName(parameterName);
|
||||
|
||||
list.Clear();
|
||||
|
||||
lCount = nodeList.Count;
|
||||
|
||||
if (lCount > 0)
|
||||
{
|
||||
parameterExists = true;
|
||||
|
||||
for (long i = 0; i < lCount; i++)
|
||||
{
|
||||
list.Add(nodeList.Item((int)i).InnerText);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
parameterExists = false;
|
||||
}
|
||||
|
||||
return parameterExists;
|
||||
}
|
||||
|
||||
public bool GetParam(string parameterName, ref ArrayList list)
|
||||
{
|
||||
System.Collections.Specialized.StringCollection stringList = new System.Collections.Specialized.StringCollection();
|
||||
bool parameterExists = GetParam(parameterName, stringList);
|
||||
list.Clear();
|
||||
if (!parameterExists)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < stringList.Count; i++)
|
||||
{
|
||||
list.Add(stringList[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This function does nothing but return false
|
||||
/// </summary>
|
||||
/// <param name="parameterName">ignored</param>
|
||||
/// <param name="type">ignored</param>
|
||||
/// <returns>always false</returns>
|
||||
public bool GetParamType(string parameterName, STType type)
|
||||
{
|
||||
bool whatever = false;
|
||||
|
||||
return whatever;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This function does nothing but return false
|
||||
/// </summary>
|
||||
/// <param name="parameterName">ignored</param>
|
||||
/// <param name="type">ignored</param>
|
||||
/// <returns>always false</returns>
|
||||
public bool SetParamType(string parameterName, STType type)
|
||||
{
|
||||
bool whatever = false;
|
||||
|
||||
return whatever;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
//using Microsoft.SqlServer.Management.AzureCredential;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
//using Microsoft.SqlServer.Management.Smo.RegSvrEnum;
|
||||
using SFC = Microsoft.SqlServer.Management.Sdk.Sfc;
|
||||
//using Microsoft.SqlServer.StorageClient;
|
||||
//using Microsoft.SqlServer.Management.SqlMgmt;
|
||||
|
||||
/// <summary>
|
||||
/// Summary description for SharedConectionUtil
|
||||
/// Moved GetConnectionName static call in a public class acessible for both
|
||||
/// OEXM and OE
|
||||
/// </summary>
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Common
|
||||
{
|
||||
internal class SharedConnectionUtil
|
||||
{
|
||||
public SharedConnectionUtil()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="ci"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetConnectionKeyName(SqlOlapConnectionInfoBase ci)
|
||||
{
|
||||
|
||||
//// Note that these strings are not localized. The returned string is used by OE in a
|
||||
//// hash of connections so it can tell if it already has such a connection open. This
|
||||
//// string is never seen by the user. For the string seen by the user, see
|
||||
//// ServerNameHandler.cs.
|
||||
string displayName = String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} (", ci.ServerName);
|
||||
|
||||
if (!string.IsNullOrEmpty(ci.DatabaseName))
|
||||
{
|
||||
displayName += ", " + ci.DatabaseName;
|
||||
}
|
||||
|
||||
return displayName;
|
||||
|
||||
//switch (ci.ServerType)
|
||||
//{
|
||||
// case ConnectionType.AzureStorage:
|
||||
// AzureStorageConnectionInfo azureCI = ci as AzureStorageConnectionInfo;
|
||||
// displayName = "AzureStorage," + azureCI.BlobClient.BaseUri;
|
||||
// break;
|
||||
// case ConnectionType.AzureAccount:
|
||||
// if (ci is CertificateBasedAuthenticationInfo)
|
||||
// {
|
||||
// displayName = "AzureSubscription," + (ci as CertificateBasedAuthenticationInfo).SubscriptionId;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// displayName = "AzureSubscription";
|
||||
// }
|
||||
// break;
|
||||
// case ConnectionType.Sql:
|
||||
// displayName += "SQLServer";
|
||||
// SqlConnectionInfo sqlCi = ci as SqlConnectionInfo;
|
||||
// if (sqlCi.UseIntegratedSecurity == true)
|
||||
// {
|
||||
// displayName += ", trusted";
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// displayName += String.Format(System.Globalization.CultureInfo.InvariantCulture, ", user = {0}", sqlCi.UserName);
|
||||
// //In Cloud a user can have access to only a few UDBs without access to master DB
|
||||
// // and hence need to show different OE hierarchy trees for each DB
|
||||
// //Same is the case with a contained user.
|
||||
|
||||
|
||||
// if (ServerInfoCache.GetDatabaseEngineType(ci.ServerName) == DatabaseEngineType.SqlAzureDatabase
|
||||
// || SFC.ExecuteSql.GetDatabaseEngineType(ci) == DatabaseEngineType.SqlAzureDatabase
|
||||
// || SFC.ExecuteSql.IsContainedAuthentication(ci))
|
||||
// {
|
||||
// if (!string.IsNullOrEmpty(ci.DatabaseName))
|
||||
// {
|
||||
// displayName += ", " + ci.DatabaseName;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// break;
|
||||
// case ConnectionType.Olap:
|
||||
// displayName += "OLAP";
|
||||
// break;
|
||||
// case ConnectionType.SqlCE:
|
||||
// displayName += "SqlCE";
|
||||
// break;
|
||||
// case ConnectionType.ReportServer:
|
||||
// displayName += "Rs";
|
||||
// displayName += String.Format(System.Globalization.CultureInfo.InvariantCulture, ", connection = {0}", ci.ConnectionString);
|
||||
// break;
|
||||
// case ConnectionType.IntegrationServer:
|
||||
// displayName += "SSIS";
|
||||
// break;
|
||||
//}
|
||||
//displayName += ")";
|
||||
//return displayName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,57 @@
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts
|
||||
{
|
||||
public class BackupInfo
|
||||
{
|
||||
public string BackupType { get; set; }
|
||||
/// <summary>
|
||||
/// Name of the datbase to perfom backup
|
||||
/// </summary>
|
||||
public string DatabaseName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Component to backup - Database or Files
|
||||
/// </summary>
|
||||
public int BackupComponent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of backup - Full/Differential/Log
|
||||
/// </summary>
|
||||
public int BackupType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Backup device - Disk, Url, etc.
|
||||
/// </summary>
|
||||
public int BackupDeviceType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The text input of selected files
|
||||
/// </summary>
|
||||
public string SelectedFiles { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Backupset name
|
||||
/// </summary>
|
||||
public string BackupsetName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of selected file groups
|
||||
/// </summary>
|
||||
public Dictionary<string, string> SelectedFileGroup { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of {key: backup path, value: device type}
|
||||
/// </summary>
|
||||
public Dictionary<string, int> arChangesList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of selected backup paths
|
||||
/// </summary>
|
||||
public ArrayList BackupPathList { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
{
|
||||
|
||||
public static class BackupConstants
|
||||
{
|
||||
public static string Backup = "Backup";
|
||||
|
||||
public static string RecoveryModelFull = "Full";
|
||||
public static string RecoveryModelSimple = "Simple";
|
||||
public static string RecoveryModelBulk = "BulkLogged";
|
||||
|
||||
public static string BackupTypeFull = "Full";
|
||||
public static string BackupTypeDiff = "Differential";
|
||||
public static string BackupTypeTLog = "Transaction Log";
|
||||
|
||||
public static string Database = "Database";
|
||||
public static string FileFilegroups = "File and Filegroups";
|
||||
public static string Filegroup = "Filegroup";
|
||||
public static string File = "File";
|
||||
}
|
||||
|
||||
public static class RestoreConstants
|
||||
{
|
||||
public static string File = "File";
|
||||
public static string Url = "URL";
|
||||
|
||||
public static string Data = "Rows Data";
|
||||
public static string FileStream = "FILESTREAM Data";
|
||||
public static string Log = "Log";
|
||||
public static string FullText = "Full Text";
|
||||
public static string NotKnown = "Not known";
|
||||
|
||||
public static string TypeFull = "Full";
|
||||
public static string TypeTransactionLog = "Transaction Log";
|
||||
public static string TypeDifferential = "Differential";
|
||||
public static string TypeFilegroup = "Filegroup";
|
||||
public static string TypeFilegroupDifferential = "Filegroup Differential";
|
||||
public static string ComponentDatabase = "Database";
|
||||
public static string ComponentFile = "File";
|
||||
|
||||
}
|
||||
}
|
||||
@@ -4,23 +4,32 @@
|
||||
//
|
||||
|
||||
using Microsoft.SqlTools.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
||||
using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.SqlTools.ServiceLayer.Common;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
{
|
||||
public class DisasterRecoveryService
|
||||
{
|
||||
private static readonly Lazy<DisasterRecoveryService> instance = new Lazy<DisasterRecoveryService>(() => new DisasterRecoveryService());
|
||||
private static ConnectionService connectionService = null;
|
||||
private BackupFactory backupFactory;
|
||||
|
||||
/// <summary>
|
||||
/// Default, parameterless constructor.
|
||||
/// </summary>
|
||||
internal DisasterRecoveryService()
|
||||
{
|
||||
this.backupFactory = new BackupFactory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -31,10 +40,29 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
get { return instance.Value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal for testing purposes only
|
||||
/// </summary>
|
||||
internal static ConnectionService ConnectionServiceInstance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (connectionService == null)
|
||||
{
|
||||
connectionService = ConnectionService.Instance;
|
||||
}
|
||||
return connectionService;
|
||||
}
|
||||
set
|
||||
{
|
||||
connectionService = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the service instance
|
||||
/// </summary>
|
||||
public void InitializeService(ServiceHost serviceHost )
|
||||
public void InitializeService(ServiceHost serviceHost)
|
||||
{
|
||||
serviceHost.SetRequestHandler(BackupRequest.Type, HandleBackupRequest);
|
||||
}
|
||||
@@ -45,8 +73,89 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
internal static async Task HandleBackupRequest(
|
||||
BackupParams backupParams,
|
||||
RequestContext<BackupResponse> requestContext)
|
||||
{
|
||||
{
|
||||
ConnectionInfo connInfo;
|
||||
DisasterRecoveryService.ConnectionServiceInstance.TryFindConnection(
|
||||
backupParams.OwnerUri,
|
||||
out connInfo);
|
||||
CDataContainer dataContainer;
|
||||
|
||||
if (connInfo != null)
|
||||
{
|
||||
char[] passwordArray = connInfo.ConnectionDetails.Password.ToCharArray();
|
||||
if (string.Equals(connInfo.ConnectionDetails.AuthenticationType, "SqlLogin", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (char* passwordPtr = passwordArray)
|
||||
{
|
||||
dataContainer = new CDataContainer(
|
||||
CDataContainer.ServerType.SQL,
|
||||
connInfo.ConnectionDetails.ServerName,
|
||||
false,
|
||||
connInfo.ConnectionDetails.UserName,
|
||||
new System.Security.SecureString(passwordPtr, passwordArray.Length),
|
||||
string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dataContainer = new CDataContainer(
|
||||
CDataContainer.ServerType.SQL,
|
||||
connInfo.ConnectionDetails.ServerName,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
}
|
||||
|
||||
SqlConnection sqlConn = GetSqlConnection(connInfo);
|
||||
if (sqlConn != null)
|
||||
{
|
||||
DisasterRecoveryService.Instance.InitializeBackup(dataContainer, sqlConn, backupParams.BackupInfo);
|
||||
DisasterRecoveryService.Instance.PerformBackup();
|
||||
}
|
||||
}
|
||||
|
||||
await requestContext.SendResult(new BackupResponse());
|
||||
}
|
||||
|
||||
internal static SqlConnection GetSqlConnection(ConnectionInfo connInfo)
|
||||
{
|
||||
try
|
||||
{
|
||||
// increase the connection timeout to at least 30 seconds and and build connection string
|
||||
// enable PersistSecurityInfo to handle issues in SMO where the connection context is lost in reconnections
|
||||
int? originalTimeout = connInfo.ConnectionDetails.ConnectTimeout;
|
||||
bool? originalPersistSecurityInfo = connInfo.ConnectionDetails.PersistSecurityInfo;
|
||||
connInfo.ConnectionDetails.ConnectTimeout = Math.Max(30, originalTimeout ?? 0);
|
||||
connInfo.ConnectionDetails.PersistSecurityInfo = true;
|
||||
string connectionString = ConnectionService.BuildConnectionString(connInfo.ConnectionDetails);
|
||||
connInfo.ConnectionDetails.ConnectTimeout = originalTimeout;
|
||||
connInfo.ConnectionDetails.PersistSecurityInfo = originalPersistSecurityInfo;
|
||||
|
||||
// open a dedicated binding server connection
|
||||
SqlConnection sqlConn = new SqlConnection(connectionString);
|
||||
sqlConn.Open();
|
||||
return sqlConn;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void InitializeBackup(CDataContainer dataContainer, SqlConnection sqlConnection, BackupInfo input)
|
||||
{
|
||||
this.backupFactory.Initialize(dataContainer, sqlConnection, input);
|
||||
}
|
||||
|
||||
private void PerformBackup()
|
||||
{
|
||||
this.backupFactory.PerformBackup();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
{
|
||||
//TODO: Should URL be added to Carbon?
|
||||
public partial class UrlControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Server
|
||||
/// </summary>
|
||||
public Microsoft.SqlServer.Management.Smo.Server SqlServer;
|
||||
|
||||
/// <summary>
|
||||
/// list of Backup Urls
|
||||
/// </summary>
|
||||
private ArrayList listBakDestUrls;
|
||||
|
||||
public UrlControl()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of backup urls
|
||||
/// </summary>
|
||||
public ArrayList ListBakDestUrls
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.listBakDestUrls;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@
|
||||
},
|
||||
"Microsoft.SqlTools.Credentials": {
|
||||
"target": "project"
|
||||
}
|
||||
}
|
||||
},
|
||||
"frameworks": {
|
||||
"netcoreapp1.0": {
|
||||
|
||||
Reference in New Issue
Block a user