diff --git a/src/Microsoft.SqlTools.ServiceLayer/Migration/Contracts/MigrationAssessmentsRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/Migration/Contracts/MigrationAssessmentsRequest.cs
index fd46743b..6b9befd7 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Migration/Contracts/MigrationAssessmentsRequest.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Migration/Contracts/MigrationAssessmentsRequest.cs
@@ -3,8 +3,8 @@
// 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.SqlServer.Migration.Assessment.Common.Contracts.Models;
namespace Microsoft.SqlTools.ServiceLayer.Migration.Contracts
{
@@ -16,20 +16,25 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration.Contracts
public class MigrationAssessmentResult
{
///
- /// Gets the collection of assessment results.
+ /// Errors that happen while running the assessment
///
- public List Items { get; } = new List();
-
+ public ErrorModel[] Errors { get; set; }
///
- /// Gets or sets a value indicating
- /// if assessment operation was successful.
+ /// Result of the assessment
///
- public bool Success { get; set; }
-
+ public ServerAssessmentProperties AssessmentResult { get; set; }
///
- /// Gets or sets an status message for the operation.
+ /// Start time of the assessment
///
- public string ErrorMessage { get; set; }
+ public string StartTime { get; set; }
+ ///
+ /// End time of the assessment
+ ///
+ public string EndedTime { get; set; }
+ ///
+ /// Contains the raw assessment response
+ ///
+ public ISqlMigrationAssessmentModel RawAssessmentResult { get; set; }
}
///
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Migration/Contracts/MigrationObjectProperties.cs b/src/Microsoft.SqlTools.ServiceLayer/Migration/Contracts/MigrationObjectProperties.cs
new file mode 100644
index 00000000..3ade4c56
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Migration/Contracts/MigrationObjectProperties.cs
@@ -0,0 +1,133 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using Microsoft.SqlServer.Migration.Assessment.Common.Contracts.TargetAssessment.Models;
+
+namespace Microsoft.SqlTools.ServiceLayer.Migration.Contracts
+{
+ public class ServerAssessmentProperties
+ {
+ ///
+ /// Name of the server
+ ///
+ public string Name { get; set; }
+ ///
+ /// Cpu cores for the server host
+ ///
+ public long CpuCoreCount { get; set; }
+ ///
+ /// Server host physical memory size
+ ///
+ public double PhysicalServerMemory { get; set; }
+ ///
+ /// Host operating system of the SQL server
+ ///
+ public string ServerHostPlatform { get; set; }
+ ///
+ /// Version of the SQL server
+ ///
+ public string ServerVersion { get; set; }
+ ///
+ /// SQL server engine edition
+ ///
+ public string ServerEngineEdition { get; set; }
+ ///
+ /// SQL server edition
+ ///
+ public string ServerEdition { get; set; }
+ ///
+ /// We use this flag to indicate if the SQL server is part of the failover cluster
+ ///
+ public bool IsClustered { get; set; }
+ ///
+ /// Returns the total number of dbs assessed
+ ///
+ public long NumberOfUserDatabases { get; set; }
+ ///
+ /// Returns the assessment status
+ ///
+ public int SqlAssessmentStatus { get; set; }
+ ///
+ /// Count of Dbs assessed
+ ///
+ public long AssessedDatabaseCount{get; set;}
+ ///
+ /// Give assessed server stats for SQL MI compatibility
+ ///
+ public IServerTargetReadiness SQLManagedInstanceTargetReadiness { get; set; }
+ ///
+ /// Server assessment results
+ ///
+ public MigrationAssessmentInfo[] Items { get; set; }
+ ///
+ /// Server assessment errors
+ ///
+ public ErrorModel[] Errors { get; set; }
+ ///
+ /// List of databases that are assessed
+ ///
+ public DatabaseAssessmentProperties[] Databases { get; set; }
+ }
+
+ public class DatabaseAssessmentProperties
+ {
+ ///
+ /// Name of the database
+ ///
+ public string Name { get; set; }
+ ///
+ /// Compatibility level of the database
+ ///
+ public string CompatibilityLevel { get; set; }
+ ///
+ /// Size of the database
+ ///
+ public double DatabaseSize { get; set; }
+ ///
+ /// Flag that indicates if the database is replicated
+ ///
+ public bool IsReplicationEnabled { get; set; }
+ ///
+ /// Time taken for assessing the database
+ ///
+ public double AssessmentTimeInMilliseconds { get; set; }
+ ///
+ /// Database Assessment Results
+ ///
+ public MigrationAssessmentInfo[] Items { get; set; }
+ ///
+ /// Database assessment errors
+ ///
+ public ErrorModel[] Errors {get; set;}
+ ///
+ /// Flags that indicate if the database is ready for migration
+ ///
+ public IDatabaseTargetReadiness SQLManagedInstanceTargetReadiness { get; set; }
+ }
+
+ public class ErrorModel
+ {
+ ///
+ /// Id of the assessment error
+ ///
+ public string ErrorId { get; set; }
+ ///
+ /// Error message
+ ///
+ public string Message { get; set; }
+ ///
+ /// Summary of the Error
+ ///
+ public string ErrorSummary { get; set; }
+ ///
+ /// Possible causes for the error
+ ///
+ public string PossibleCauses { get; set; }
+ ///
+ /// Possible mitigation for the error
+ ///
+ public string Guidance { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Migration/MigrationService.cs b/src/Microsoft.SqlTools.ServiceLayer/Migration/MigrationService.cs
index 0f1ec874..6afc85d3 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Migration/MigrationService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Migration/MigrationService.cs
@@ -5,27 +5,23 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Threading.Tasks;
-using Microsoft.SqlServer.Management.Assessment;
+using Microsoft.SqlServer.DataCollection.Common;
using Microsoft.SqlServer.Management.Assessment.Checks;
+using Microsoft.SqlServer.Management.Assessment;
using Microsoft.SqlServer.Migration.Assessment.Common.Contracts.Models;
using Microsoft.SqlServer.Migration.Assessment.Common.Engine;
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.Utility;
+using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Hosting;
using Microsoft.SqlTools.ServiceLayer.Migration.Contracts;
using Microsoft.SqlTools.ServiceLayer.SqlAssessment;
-using Microsoft.Win32.SafeHandles;
-using Microsoft.SqlServer.DataCollection.Common;
-using System.ComponentModel;
-using System.Runtime.InteropServices;
-using System.Text.RegularExpressions;
-using System.Security.Principal;
-using System.IO;
+using Microsoft.SqlTools.Utility;
+
namespace Microsoft.SqlTools.ServiceLayer.Migration
{
///
@@ -95,8 +91,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration
{
this.ServiceHost = serviceHost;
this.ServiceHost.SetRequestHandler(MigrationAssessmentsRequest.Type, HandleMigrationAssessmentsRequest);
- this.ServiceHost.SetRequestHandler(ValidateWindowsAccountRequest.Type, HandleValidateWindowsAccountRequest);
- this.ServiceHost.SetRequestHandler(ValidateNetworkFileShareRequest.Type, HandleValidateNetworkFileShareRequest);
}
///
@@ -143,10 +137,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration
var db = SqlAssessmentService.GetDatabaseLocator(server, connection.Database);
var connectionString = ConnectionService.BuildConnectionString(connInfo.ConnectionDetails);
- var results = await GetAssessmentItems(server, connectionString);
- var result = new MigrationAssessmentResult();
- result.Items.AddRange(results);
- await requestContext.SendResult(result);
+
+ var results = await GetAssessmentItems(connectionString);
+ await requestContext.SendResult(results);
}
catch (Exception e)
{
@@ -186,67 +179,124 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration
}
}
- internal async Task> GetAssessmentItems(SqlObjectLocator target, string connectionString)
+ internal async Task GetAssessmentItems(string connectionString)
{
SqlAssessmentConfiguration.EnableLocalLogging = true;
SqlAssessmentConfiguration.EnableReportCreation = true;
SqlAssessmentConfiguration.AssessmentReportAndLogsRootFolderPath = Path.GetDirectoryName(Logger.LogFileFullPath);
DmaEngine engine = new DmaEngine(connectionString);
var assessmentResults = await engine.GetTargetAssessmentResultsList();
-
- var result = new List();
- foreach (var r in assessmentResults)
+ Dictionary assessmentResultLookup = new Dictionary();
+ foreach (ISqlMigrationAssessmentResult r in assessmentResults)
{
- var migrationResult = r as ISqlMigrationAssessmentResult;
- if (migrationResult == null)
- {
- continue;
- }
+ assessmentResultLookup.Add(CreateAssessmentResultKey(r as ISqlMigrationAssessmentResult), r as ISqlMigrationAssessmentResult);
+ }
+ ISqlMigrationAssessmentModel contextualizedAssessmentResult = await engine.GetTargetAssessmentResultsList(System.Threading.CancellationToken.None);
+ return new MigrationAssessmentResult()
+ {
+ AssessmentResult = ParseServerAssessmentInfo(contextualizedAssessmentResult.Servers[0], assessmentResultLookup),
+ Errors = ParseAssessmentError(contextualizedAssessmentResult.Errors),
+ StartTime = contextualizedAssessmentResult.StartedOn.ToString(),
+ EndedTime = contextualizedAssessmentResult.EndedOn.ToString(),
+ RawAssessmentResult = contextualizedAssessmentResult
+ };
+ }
- var targetName = !string.IsNullOrWhiteSpace(migrationResult.DatabaseName)
- ? $"{target.ServerName}:{migrationResult.DatabaseName}"
- : target.Name;
- var ruleId = migrationResult.FeatureId.ToString();
+ internal ServerAssessmentProperties ParseServerAssessmentInfo(IServerAssessmentInfo server, Dictionary assessmentResultLookup)
+ {
+ return new ServerAssessmentProperties()
+ {
+ CpuCoreCount = server.Properties.ServerCoreCount,
+ PhysicalServerMemory = server.Properties.MaxServerMemoryInUse,
+ ServerHostPlatform = server.Properties.ServerHostPlatform,
+ ServerVersion = server.Properties.ServerVersion,
+ ServerEngineEdition = server.Properties.ServerEngineEdition,
+ ServerEdition = server.Properties.ServerEdition,
+ IsClustered = server.Properties.IsClustered,
+ NumberOfUserDatabases = server.Properties.NumberOfUserDatabases,
+ SqlAssessmentStatus = (int)server.Status,
+ AssessedDatabaseCount = server.Properties.NumberOfUserDatabases,
+ SQLManagedInstanceTargetReadiness = server.TargetReadinesses[Microsoft.SqlServer.DataCollection.Common.Contracts.Advisor.TargetType.AzureSqlManagedInstance],
+ Errors = ParseAssessmentError(server.Errors),
+ Items = ParseAssessmentResult(server.ServerAssessments, assessmentResultLookup),
+ Databases = ParseDatabaseAssessmentInfo(server.Databases, assessmentResultLookup),
+ Name = server.Properties.ServerName
+ };
+ }
- var item = new MigrationAssessmentInfo()
+ internal DatabaseAssessmentProperties[] ParseDatabaseAssessmentInfo(IList databases, Dictionary assessmentResultLookup)
+ {
+ return databases.Select(d =>
+ {
+ return new DatabaseAssessmentProperties()
{
- CheckId = r.Check.Id,
- Description = r.Check.Description,
- DisplayName = r.Check.DisplayName,
- HelpLink = r.Check.HelpLink,
- Level = r.Check.Level.ToString(),
- TargetName = targetName,
- DatabaseName = migrationResult.DatabaseName,
- ServerName = migrationResult.ServerName,
- Tags = r.Check.Tags.ToArray(),
- TargetType = target.Type,
+ Name = d.Properties.Name,
+ CompatibilityLevel = d.Properties.CompatibilityLevel.ToString(),
+ DatabaseSize = d.Properties.SizeMB,
+ IsReplicationEnabled = d.Properties.IsReplicationEnabled,
+ AssessmentTimeInMilliseconds = d.Properties.TSqlScriptAnalysisTimeElapse.TotalMilliseconds,
+ Errors = ParseAssessmentError(d.Errors),
+ Items = ParseAssessmentResult(d.DatabaseAssessments, assessmentResultLookup),
+ SQLManagedInstanceTargetReadiness = d.TargetReadinesses[Microsoft.SqlServer.DataCollection.Common.Contracts.Advisor.TargetType.AzureSqlManagedInstance]
+ };
+ }).ToArray();
+ }
+ internal ErrorModel[] ParseAssessmentError(IList errors)
+ {
+ return errors.Select(e =>
+ {
+ return new ErrorModel()
+ {
+ ErrorId = e.ErrorID.ToString(),
+ Message = e.Message,
+ ErrorSummary = e.ErrorSummary,
+ PossibleCauses = e.PossibleCauses,
+ Guidance = e.Guidance,
+ };
+ }).ToArray();
+ }
+ internal MigrationAssessmentInfo[] ParseAssessmentResult(IList assessmentResults, Dictionary assessmentResultLookup)
+ {
+ return assessmentResults.Select(r =>
+ {
+ var check = assessmentResultLookup[CreateAssessmentResultKey(r)].Check;
+ return new MigrationAssessmentInfo()
+ {
+ CheckId = check.Id,
+ Description = check.Description,
+ DisplayName = check.DisplayName,
+ HelpLink = check.HelpLink,
+ Level = check.Level.ToString(),
+ TargetName = r.AppliesToMigrationTargetPlatform.ToString(),
+ DatabaseName = r.DatabaseName,
+ ServerName = r.ServerName,
+ Tags = check.Tags.ToArray(),
RulesetName = Engine.Configuration.DefaultRuleset.Name,
RulesetVersion = Engine.Configuration.DefaultRuleset.Version.ToString(),
- RuleId = ruleId,
+ RuleId = r.FeatureId.ToString(),
Message = r.Message,
- AppliesToMigrationTargetPlatform = migrationResult.AppliesToMigrationTargetPlatform.ToString(),
- IssueCategory = "Category_Unknown"
+ AppliesToMigrationTargetPlatform = r.AppliesToMigrationTargetPlatform.ToString(),
+ IssueCategory = r.IssueCategory.ToString(),
+ ImpactedObjects = ParseImpactedObjects(r.ImpactedObjects)
};
-
- if (migrationResult.ImpactedObjects != null)
+ }).ToArray();
+ }
+ internal ImpactedObjectInfo[] ParseImpactedObjects(IList impactedObjects)
+ {
+ return impactedObjects.Select(i =>
+ {
+ return new ImpactedObjectInfo()
{
- 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;
- }
+ Name = i.Name,
+ ImpactDetail = i.ImpactDetail,
+ ObjectType = i.ObjectType
+ };
+ }).ToArray();
+ }
- result.Add(item);
- }
- return result;
+ internal string CreateAssessmentResultKey(ISqlMigrationAssessmentResult assessment)
+ {
+ return assessment.ServerName+assessment.DatabaseName+assessment.FeatureId.ToString()+assessment.IssueCategory.ToString()+assessment.Message + assessment.TargetType.ToString() + assessment.AppliesToMigrationTargetPlatform.ToString();
}
///
@@ -259,130 +309,5 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration
disposed = true;
}
}
-
- [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
- public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
- int dwLogonType, int dwLogonProvider, out SafeAccessTokenHandle phToken);
-
- internal async Task HandleValidateWindowsAccountRequest(
- ValidateWindowsAccountRequestParams parameters,
- RequestContext requestContext)
- {
- if (!ValidateWindowsDomainUsername(parameters.Username))
- {
- await requestContext.SendError("Invalid user name format. Example: Domain\\username");
- return;
- }
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- int separator = parameters.Username.IndexOf("\\");
- string domainName = parameters.Username.Substring(0, separator);
- string userName = parameters.Username.Substring(separator + 1, parameters.Username.Length - separator - 1);
-
- const int LOGON32_PROVIDER_DEFAULT = 0;
- const int LOGON32_LOGON_INTERACTIVE = 2;
-
- SafeAccessTokenHandle safeAccessTokenHandle;
- bool returnValue = LogonUser(userName, domainName, parameters.Password,
- LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
- out safeAccessTokenHandle);
-
- if (!returnValue)
- {
- int ret = Marshal.GetLastWin32Error();
- string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
- await requestContext.SendError(errorMessage);
- }
- else
- {
- await requestContext.SendResult(true);
- }
- }
- else
- {
- await requestContext.SendResult(true);
- }
- }
-
- internal async Task HandleValidateNetworkFileShareRequest(
- ValidateNetworkFileShareRequestParams parameters,
- RequestContext requestContext)
- {
- if (!ValidateWindowsDomainUsername(parameters.Username))
- {
- await requestContext.SendError("Invalid user name format. Example: Domain\\username");
- return;
- }
-
- if (!ValidateUNCPath(parameters.Path))
- {
- await requestContext.SendError("Invalid network share path. Example: \\\\Servername.domainname.com\\Backupfolder");
- return;
- }
-
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- int separator = parameters.Username.IndexOf("\\");
- string domainName = parameters.Username.Substring(0, separator);
- string userName = parameters.Username.Substring(separator + 1, parameters.Username.Length - separator - 1);
-
- const int LOGON32_PROVIDER_WINNT50 = 3;
- const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
-
- SafeAccessTokenHandle safeAccessTokenHandle;
- bool returnValue = LogonUser(
- userName,
- domainName,
- parameters.Password,
- LOGON32_LOGON_NEW_CREDENTIALS,
- LOGON32_PROVIDER_WINNT50,
- out safeAccessTokenHandle);
-
- if (!returnValue)
- {
- int ret = Marshal.GetLastWin32Error();
- string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
- await requestContext.SendError(errorMessage);
- return;
- }
- await WindowsIdentity.RunImpersonated(
- safeAccessTokenHandle,
- // User action
- async () =>
- {
- if(!Directory.Exists(parameters.Path)){
- await requestContext.SendError("Cannot connect to file share");
- } else {
- await requestContext.SendResult(true);
- }
- }
- );
- }
- else
- {
- await requestContext.SendResult(true);
- }
- }
-
- ///
- /// Check if the username is in 'domain\username' format.
- ///
- ///
- internal bool ValidateWindowsDomainUsername(string username)
- {
- var domainUserRegex = new Regex(@"^(?[A-Za-z0-9\._-]*)\\(?[A-Za-z0-9\._-]*)$");
- return domainUserRegex.IsMatch(username);
- }
-
-
- ///
- /// Checks if the file path is in UNC format '\\Servername.domainname.com\Backupfolder'
- ///
- ///
- ///
- internal bool ValidateUNCPath(string path)
- {
- return new Uri(path).IsUnc;
- }
}
}