Files
sqltoolsservice/src/Microsoft.SqlTools.ServiceLayer/Migration/MigrationService.cs
Karl Burtram b9e1618260 Revert "Rollback migration changes" (#1099)
* Revert "Revert "Initial migration service changes for assessment support (#1093)" (#1098)"

This reverts commit e96c0064ad.

* Move SQL Tools Service last in Main project list
2020-10-16 16:29:22 -07:00

247 lines
8.9 KiB
C#

//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.SqlServer.Management.Assessment;
using Microsoft.SqlServer.Management.Assessment.Checks;
using Microsoft.SqlServer.Migration.Assessment.Common.Contracts.Models;
using Microsoft.SqlServer.Migration.Assessment.Common.Engine;
using Microsoft.SqlServer.Migration.Assessment.Common.Models;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
using Microsoft.SqlTools.ServiceLayer.Hosting;
using Microsoft.SqlTools.ServiceLayer.Migration.Contracts;
using Microsoft.SqlTools.ServiceLayer.SqlAssessment;
using Microsoft.SqlTools.ServiceLayer.SqlAssessment.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.Migration
{
/// <summary>
/// Main class for Migration Service functionality
/// </summary>
public sealed class MigrationService : IDisposable
{
private static ConnectionService connectionService = null;
private static readonly Lazy<MigrationService> instance = new Lazy<MigrationService>(() => new MigrationService());
private bool disposed;
/// <summary>
/// Construct a new MigrationService instance with default parameters
/// </summary>
public MigrationService()
{
}
/// <summary>
/// Gets the singleton instance object
/// </summary>
public static MigrationService Instance
{
get { return instance.Value; }
}
/// <summary>
/// Internal for testing purposes only
/// </summary>
internal static ConnectionService ConnectionService
{
get
{
if (connectionService == null)
{
connectionService = ConnectionService.Instance;
}
return connectionService;
}
set
{
connectionService = value;
}
}
/// <summary>
/// Gets the <see cref="Engine"/> used to run assessment operations.
/// </summary>
internal Engine Engine { get; } = new Engine();
/// <summary>
/// Service host object for sending/receiving requests/events.
/// Internal for testing purposes.
/// </summary>
internal IProtocolEndpoint ServiceHost
{
get;
set;
}
/// <summary>
/// Initializes the Migration Service instance
/// </summary>
public void InitializeService(ServiceHost serviceHost)
{
this.ServiceHost = serviceHost;
this.ServiceHost.SetRequestHandler(MigrationAssessmentsRequest.Type, HandleMigrationAssessmentsRequest);
}
/// <summary>
/// Handle request to start a migration session
/// </summary>
internal async Task HandleMigrationAssessmentsRequest(
MigrationAssessmentsParams parameters,
RequestContext<MigrationAssessmentResult> requestContext)
{
string randomUri = Guid.NewGuid().ToString();
try
{
// get connection
if (!ConnectionService.TryFindConnection(parameters.OwnerUri, out var connInfo))
{
await requestContext.SendError("Could not find migration connection");
return;
}
ConnectParams connectParams = new ConnectParams
{
OwnerUri = randomUri,
Connection = connInfo.ConnectionDetails,
Type = ConnectionType.Default
};
await ConnectionService.Connect(connectParams);
var connection = await ConnectionService.Instance.GetOrOpenConnection(randomUri, ConnectionType.Default);
var serverInfo = ReliableConnectionHelper.GetServerVersion(connection);
var hostInfo = ReliableConnectionHelper.GetServerHostInfo(connection);
var server = new SqlObjectLocator
{
Connection = connection,
EngineEdition = SqlAssessmentService.GetEngineEdition(serverInfo.EngineEditionId),
Name = serverInfo.ServerName,
ServerName = serverInfo.ServerName,
Type = SqlObjectType.Server,
Urn = serverInfo.ServerName,
Version = Version.Parse(serverInfo.ServerVersion),
Platform = hostInfo.Platform
};
var db = SqlAssessmentService.GetDatabaseLocator(server, connection.Database);
var results = await GetAssessmentItems(server);
var result = new MigrationAssessmentResult();
result.Items.AddRange(results);
await requestContext.SendResult(result);
}
finally
{
ConnectionService.Disconnect(new DisconnectParams { OwnerUri = randomUri, Type = null });
}
}
internal class AssessmentRequest : IAssessmentRequest
{
private readonly Check[] checks = null;
public AssessmentRequest(ISqlObjectLocator locator)
{
Target = locator ?? throw new ArgumentNullException(nameof(locator));
}
public EvaluationContext<object> EvaluationContext { get; }
public ISqlObjectLocator Target { get; }
public IEnumerable<Check> Checks
{
get
{
return checks;
}
}
public bool TryGetData(string column, out object value)
{
return EvaluationContext.TryGetData(column, out value);
}
}
internal async Task<List<MigrationAssessmentInfo>> GetAssessmentItems(SqlObjectLocator target)
{
DmaEngine engine = new DmaEngine(target);
var assessmentResults = await engine.GetTargetAssessmentResultsList();
var result = new List<MigrationAssessmentInfo>();
foreach (var r in assessmentResults)
{
var migrationResult = r as ISqlMigrationAssessmentResult;
if (migrationResult == null)
{
continue;
}
var targetName = !string.IsNullOrWhiteSpace(migrationResult.DatabaseName)
? $"{target.ServerName}:{migrationResult.DatabaseName}"
: target.Name;
var item = new MigrationAssessmentInfo()
{
CheckId = r.Check.Id,
Description = r.Check.Description,
DisplayName = r.Check.DisplayName,
HelpLink = r.Check.HelpLink,
Level = r.Check.Level.ToString(),
TargetName = targetName,
Tags = r.Check.Tags.ToArray(),
TargetType = target.Type,
RulesetName = Engine.Configuration.DefaultRuleset.Name,
RulesetVersion = Engine.Configuration.DefaultRuleset.Version.ToString(),
Message = r.Message,
AppliesToMigrationTargetPlatform = migrationResult.AppliesToMigrationTargetPlatform.ToString(),
IssueCategory = "Category_Unknown"
};
if (migrationResult.ImpactedObjects != null)
{
ImpactedObjectInfo[] impactedObjects = new ImpactedObjectInfo[migrationResult.ImpactedObjects.Count];
for (int i = 0; i < migrationResult.ImpactedObjects.Count; ++i)
{
var impactedObject = new ImpactedObjectInfo()
{
Name = migrationResult.ImpactedObjects[i].Name,
ImpactDetail = migrationResult.ImpactedObjects[i].ImpactDetail,
ObjectType = migrationResult.ImpactedObjects[i].ObjectType
};
impactedObjects[i] = impactedObject;
}
item.ImpactedObjects = impactedObjects;
}
result.Add(item);
}
return result;
}
/// <summary>
/// Disposes the Migration Service
/// </summary>
public void Dispose()
{
if (!disposed)
{
disposed = true;
}
}
}
}