add assessment error handling and bypass option (#17369)

* add assessment error handling and bypass option

* add desriptive text and results warnings
This commit is contained in:
brian-harris
2021-10-15 15:15:52 -07:00
committed by GitHub
parent 59b43d8c67
commit 2e677dbda6
5 changed files with 195 additions and 90 deletions

View File

@@ -163,7 +163,11 @@ export function findDropDownItemIndex(dropDown: DropDownComponent, value: string
} }
export function hashString(value: string): string { export function hashString(value: string): string {
return crypto.createHash('sha512').update(value).digest('hex'); if (value?.length > 0) {
return crypto.createHash('sha512').update(value).digest('hex');
}
return '';
} }
export function debounce(delay: number): Function { export function debounce(delay: number): Function {

View File

@@ -5,6 +5,7 @@
import { AzureAccount } from 'azurecore'; import { AzureAccount } from 'azurecore';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
import { EOL } from 'os';
import { MigrationStatus } from '../models/migrationLocalStorage'; import { MigrationStatus } from '../models/migrationLocalStorage';
import { MigrationSourceAuthenticationType } from '../models/stateMachine'; import { MigrationSourceAuthenticationType } from '../models/stateMachine';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
@@ -32,9 +33,21 @@ export const SKU_RECOMMENDATION_PAGE_TITLE = localize('sql.migration.wizard.sku.
export const SKU_RECOMMENDATION_ALL_SUCCESSFUL = (databaseCount: number): string => { export const SKU_RECOMMENDATION_ALL_SUCCESSFUL = (databaseCount: number): string => {
return localize('sql.migration.wizard.sku.all', "Based on the assessment results, all {0} of your databases in an online state can be migrated to Azure SQL.", databaseCount); return localize('sql.migration.wizard.sku.all', "Based on the assessment results, all {0} of your databases in an online state can be migrated to Azure SQL.", databaseCount);
}; };
export const SKU_RECOMMENDATION_ERROR = localize('sql.migration.wizard.sku.error', "An error occurred while assessing your databases.");
export const SKU_RECOMMENDATION_ASSESSMENT_ERROR = (serverName: string): string => { export const SKU_RECOMMENDATION_ASSESSMENT_ERROR = (serverName: string): string => {
return localize('sql.migration.wizard.sku.assessment.error', "An error occurred while assessing the server '{0}'.", serverName); return localize('sql.migration.wizard.sku.assessment.error', "An error occurred while assessing the server '{0}'.", serverName);
}; };
export const SKU_RECOMMENDATION_ASSESSMENT_UNEXPECTED_ERROR = (serverName: string, error: Error): string => {
return localize(
'sql.migration.wizard.sku.assessment.unexpected.error',
"An unexpected error occurred while assessing the server '{0}'.{3}Message: {1}{3}stack: {2}",
serverName,
error.message,
error.stack,
EOL);
};
export const SKU_RECOMMENDATION_ASSESSMENT_ERROR_BYPASS = localize('sql.migration.wizard.sku.assessment.error.bypass', 'Check this option to skip assessment and continue the migration.');
export const SKU_RECOMMENDATION_ASSESSMENT_ERROR_DETAIL = localize('sql.migration.wizard.sku.assessment.error.detail', '[There are no assessment results to validate readiness of your database migration. By checking this box, you acknowledge you want to proceed migrating your database to the desired Azure SQL target.]',);
export const REFRESH_ASSESSMENT_BUTTON_LABEL = localize('sql.migration.refresh.assessment.button.label', "Refresh assessment"); export const REFRESH_ASSESSMENT_BUTTON_LABEL = localize('sql.migration.refresh.assessment.button.label', "Refresh assessment");
export const SKU_RECOMMENDATION_CHOOSE_A_TARGET = localize('sql.migration.wizard.sku.choose_a_target', "Choose your Azure SQL target"); export const SKU_RECOMMENDATION_CHOOSE_A_TARGET = localize('sql.migration.wizard.sku.choose_a_target', "Choose your Azure SQL target");
export const SKU_RECOMMENDATION_SUBSCRIPTION_INFO = localize('sql.migration.sku.subscription', "Subscription name for your Azure SQL target"); export const SKU_RECOMMENDATION_SUBSCRIPTION_INFO = localize('sql.migration.sku.subscription', "Subscription name for your Azure SQL target");
@@ -56,6 +69,9 @@ export const SELECT_DATABASE_TO_MIGRATE = localize('sql.migration.select.databas
export const ASSESSMENT_COMPLETED = (serverName: string): string => { export const ASSESSMENT_COMPLETED = (serverName: string): string => {
return localize('sql.migration.generic.congratulations', "We have completed the assessment of your SQL Server instance '{0}'.", serverName); return localize('sql.migration.generic.congratulations', "We have completed the assessment of your SQL Server instance '{0}'.", serverName);
}; };
export const ASSESSMENT_FAILED = (serverName: string): string => {
return localize('sql.migration.asessment.failed', "The assessment of your SQL Server instance '{0}' failed.", serverName);
};
export function ASSESSMENT_TILE(serverName: string): string { export function ASSESSMENT_TILE(serverName: string): string {
return localize('sql.migration.assessment', "Assessment results for '{0}'", serverName); return localize('sql.migration.assessment', "Assessment results for '{0}'", serverName);
} }
@@ -496,6 +512,8 @@ export const ISSUES_DETAILS = localize('sql.migration.issues.details', "Issue de
export const SELECT_DB_PROMPT = localize('sql.migration.select.prompt', "Click on SQL Server instance or any of the databases on the left to view its details."); export const SELECT_DB_PROMPT = localize('sql.migration.select.prompt', "Click on SQL Server instance or any of the databases on the left to view its details.");
export const NO_ISSUES_FOUND_VM = localize('sql.migration.no.issues.vm', "No issues found for migrating to SQL Server on Azure Virtual Machine."); export const NO_ISSUES_FOUND_VM = localize('sql.migration.no.issues.vm', "No issues found for migrating to SQL Server on Azure Virtual Machine.");
export const NO_ISSUES_FOUND_MI = localize('sql.migration.no.issues.mi', "No issues found for migrating to SQL Server on Azure SQL Managed Instance."); export const NO_ISSUES_FOUND_MI = localize('sql.migration.no.issues.mi', "No issues found for migrating to SQL Server on Azure SQL Managed Instance.");
export const NO_RESULTS_AVAILABLE = localize('sql.migration.no.results', 'Assessment results are unavailable.');
export function IMPACT_OBJECT_TYPE(objectType?: string): string { export function IMPACT_OBJECT_TYPE(objectType?: string): string {
return objectType ? localize('sql.migration.impact.object.type', "Type: {0}", objectType) : ''; return objectType ? localize('sql.migration.impact.object.type', "Type: {0}", objectType) : '';
} }

View File

@@ -10,6 +10,7 @@ import * as constants from '../../constants/strings';
import { debounce } from '../../api/utils'; import { debounce } from '../../api/utils';
import { IconPath, IconPathHelper } from '../../constants/iconPathHelper'; import { IconPath, IconPathHelper } from '../../constants/iconPathHelper';
import * as styles from '../../constants/styles'; import * as styles from '../../constants/styles';
import { EOL } from 'os';
const styleLeft: azdata.CssStyles = { const styleLeft: azdata.CssStyles = {
'border': 'none', 'border': 'none',
@@ -45,6 +46,7 @@ const headerRight: azdata.CssStyles = {
export class SqlDatabaseTree { export class SqlDatabaseTree {
private _view!: azdata.ModelView; private _view!: azdata.ModelView;
private _dialog!: azdata.window.Dialog;
private _instanceTable!: azdata.DeclarativeTableComponent; private _instanceTable!: azdata.DeclarativeTableComponent;
private _databaseTable!: azdata.DeclarativeTableComponent; private _databaseTable!: azdata.DeclarativeTableComponent;
private _assessmentResultsTable!: azdata.DeclarativeTableComponent; private _assessmentResultsTable!: azdata.DeclarativeTableComponent;
@@ -84,6 +86,7 @@ export class SqlDatabaseTree {
async createRootContainer(dialog: azdata.window.Dialog, view: azdata.ModelView): Promise<azdata.Component> { async createRootContainer(dialog: azdata.window.Dialog, view: azdata.ModelView): Promise<azdata.Component> {
this._view = view; this._view = view;
this._dialog = dialog;
const selectDbMessage = this.createSelectDbMessage(); const selectDbMessage = this.createSelectDbMessage();
this._resultComponent = await this.createComponentResult(view); this._resultComponent = await this.createComponentResult(view);
@@ -388,16 +391,21 @@ export class SqlDatabaseTree {
private createNoIssuesText(): azdata.FlexContainer { private createNoIssuesText(): azdata.FlexContainer {
let message: azdata.TextComponent; let message: azdata.TextComponent;
const failedAssessment = this.handleFailedAssessment();
if (this._targetType === MigrationTargetType.SQLVM) { if (this._targetType === MigrationTargetType.SQLVM) {
message = this._view.modelBuilder.text().withProps({ message = this._view.modelBuilder.text().withProps({
value: constants.NO_ISSUES_FOUND_VM, value: failedAssessment
? constants.NO_RESULTS_AVAILABLE
: constants.NO_ISSUES_FOUND_VM,
CSSStyles: { CSSStyles: {
...styles.BODY_CSS ...styles.BODY_CSS
} }
}).component(); }).component();
} else { } else {
message = this._view.modelBuilder.text().withProps({ message = this._view.modelBuilder.text().withProps({
value: constants.NO_ISSUES_FOUND_MI, value: failedAssessment
? constants.NO_RESULTS_AVAILABLE
: constants.NO_ISSUES_FOUND_MI,
CSSStyles: { CSSStyles: {
...styles.BODY_CSS ...styles.BODY_CSS
} }
@@ -415,6 +423,34 @@ export class SqlDatabaseTree {
return this._noIssuesContainer; return this._noIssuesContainer;
} }
private handleFailedAssessment(): boolean {
const failedAssessment: boolean = this._model._assessmentResults.assessmentError !== undefined
|| (this._model._assessmentResults?.errors?.length || 0) > 0;
if (failedAssessment) {
this._dialog.message = {
level: azdata.window.MessageLevel.Warning,
text: constants.ASSESSMENT_MIGRATION_WARNING,
description: this.getAssessmentError(),
};
}
return failedAssessment;
}
private getAssessmentError(): string {
const errors: string[] = [];
const assessmentError = this._model._assessmentResults.assessmentError;
if (assessmentError) {
errors.push(`message: ${assessmentError.message}${EOL}stack: ${assessmentError.stack}`);
}
if (this._model?._assessmentResults?.errors?.length! > 0) {
errors.push(...this._model._assessmentResults.errors?.map(
e => `message: ${e.message}${EOL}errorSummary: ${e.errorSummary}${EOL}possibleCauses: ${e.possibleCauses}${EOL}guidance: ${e.guidance}${EOL}errorId: ${e.errorId}`)!);
}
return errors.join(EOL);
}
private createSelectDbMessage(): azdata.FlexContainer { private createSelectDbMessage(): azdata.FlexContainer {
const message = this._view.modelBuilder.text().withProps({ const message = this._view.modelBuilder.text().withProps({
value: constants.SELECT_DB_PROMPT, value: constants.SELECT_DB_PROMPT,

View File

@@ -231,6 +231,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
const ownerUri = await azdata.connection.getUriForConnection(this.sourceConnectionId); const ownerUri = await azdata.connection.getUriForConnection(this.sourceConnectionId);
try { try {
const response = (await this.migrationService.getAssessments(ownerUri, this._databaseAssessment))!; const response = (await this.migrationService.getAssessments(ownerUri, this._databaseAssessment))!;
this._assessmentApiResponse = response;
if (response?.assessmentResult) { if (response?.assessmentResult) {
response.assessmentResult.items = response.assessmentResult.items?.filter( response.assessmentResult.items = response.assessmentResult.items?.filter(
issue => issue.appliesToMigrationTargetPlatform === targetType); issue => issue.appliesToMigrationTargetPlatform === targetType);
@@ -238,24 +239,41 @@ export class MigrationStateModel implements Model, vscode.Disposable {
response.assessmentResult.databases?.forEach( response.assessmentResult.databases?.forEach(
database => database.items = database.items?.filter( database => database.items = database.items?.filter(
issue => issue.appliesToMigrationTargetPlatform === targetType)); issue => issue.appliesToMigrationTargetPlatform === targetType));
this._assessmentResults = {
issues: this._assessmentApiResponse?.assessmentResult?.items || [],
databaseAssessments: this._assessmentApiResponse?.assessmentResult?.databases?.map(d => {
return {
name: d.name,
issues: d.items,
errors: d.errors,
};
}) ?? [],
errors: this._assessmentApiResponse?.errors ?? []
};
} else {
this._assessmentResults = {
issues: [],
databaseAssessments: this._databaseAssessment?.map(database => {
return {
name: database,
issues: [],
errors: []
};
}) ?? [],
errors: response?.errors ?? [],
};
} }
this._assessmentApiResponse = response;
this._assessmentResults = {
issues: this._assessmentApiResponse?.assessmentResult?.items || [],
databaseAssessments: this._assessmentApiResponse?.assessmentResult?.databases?.map(d => {
return {
name: d.name,
issues: d.items,
errors: d.errors
};
}) ?? [],
errors: this._assessmentApiResponse?.errors ?? []
};
} catch (error) { } catch (error) {
this._assessmentResults = { this._assessmentResults = {
issues: [], issues: [],
databaseAssessments: [], databaseAssessments: this._databaseAssessment?.map(database => {
return {
name: database,
issues: [],
errors: []
};
}) ?? [],
errors: [], errors: [],
assessmentError: error assessmentError: error
}; };
@@ -277,7 +295,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
}); });
const serverAssessmentErrorsMap: Map<number, number> = new Map(); const serverAssessmentErrorsMap: Map<number, number> = new Map();
this._assessmentApiResponse.assessmentResult.errors.forEach(e => { this._assessmentApiResponse?.assessmentResult?.errors?.forEach(e => {
serverAssessmentErrorsMap.set(e.errorId, serverAssessmentErrorsMap.get(e.errorId) ?? 0 + 1); serverAssessmentErrorsMap.set(e.errorId, serverAssessmentErrorsMap.get(e.errorId) ?? 0 + 1);
}); });
@@ -291,8 +309,8 @@ export class MigrationStateModel implements Model, vscode.Disposable {
); );
}); });
const startTime = new Date(this._assessmentApiResponse.startTime); const startTime = new Date(this._assessmentApiResponse?.startTime);
const endTime = new Date(this._assessmentApiResponse.endedTime); const endTime = new Date(this._assessmentApiResponse?.endedTime);
sendSqlMigrationActionEvent( sendSqlMigrationActionEvent(
TelemetryViews.MigrationWizardTargetSelectionPage, TelemetryViews.MigrationWizardTargetSelectionPage,
@@ -302,33 +320,33 @@ export class MigrationStateModel implements Model, vscode.Disposable {
'tenantId': this._azureAccount.properties.tenants[0].id, 'tenantId': this._azureAccount.properties.tenants[0].id,
'subscriptionId': this._targetSubscription?.id, 'subscriptionId': this._targetSubscription?.id,
'resourceGroup': this._resourceGroup?.name, 'resourceGroup': this._resourceGroup?.name,
'hashedServerName': hashString(this._assessmentApiResponse.assessmentResult.name), 'hashedServerName': hashString(this._assessmentApiResponse?.assessmentResult?.name),
'startTime': startTime.toString(), 'startTime': startTime.toString(),
'endTime': endTime.toString(), 'endTime': endTime.toString(),
'serverVersion': this._assessmentApiResponse.assessmentResult.serverVersion, 'serverVersion': this._assessmentApiResponse?.assessmentResult?.serverVersion,
'serverEdition': this._assessmentApiResponse.assessmentResult.serverEdition, 'serverEdition': this._assessmentApiResponse?.assessmentResult?.serverEdition,
'platform': this._assessmentApiResponse.assessmentResult.serverHostPlatform, 'platform': this._assessmentApiResponse?.assessmentResult?.serverHostPlatform,
'engineEdition': this._assessmentApiResponse.assessmentResult.serverEngineEdition, 'engineEdition': this._assessmentApiResponse?.assessmentResult?.serverEngineEdition,
'serverIssues': JSON.stringify(serverIssues), 'serverIssues': JSON.stringify(serverIssues),
'serverErrors': JSON.stringify(serverErrors) 'serverErrors': JSON.stringify(serverErrors),
}, },
{ {
'issuesCount': this._assessmentResults.issues.length, 'issuesCount': this._assessmentResults.issues.length,
'warningsCount': this._assessmentResults.databaseAssessments.reduce((count, d) => count + d.issues.length, 0), 'warningsCount': this._assessmentResults.databaseAssessments.reduce((count, d) => count + d.issues.length, 0),
'durationInMilliseconds': endTime.getTime() - startTime.getTime(), 'durationInMilliseconds': endTime.getTime() - startTime.getTime(),
'databaseCount': this._assessmentResults.databaseAssessments.length, 'databaseCount': this._assessmentResults.databaseAssessments.length,
'serverHostCpuCount': this._assessmentApiResponse.assessmentResult.cpuCoreCount, 'serverHostCpuCount': this._assessmentApiResponse?.assessmentResult?.cpuCoreCount,
'serverHostPhysicalMemoryInBytes': this._assessmentApiResponse.assessmentResult.physicalServerMemory, 'serverHostPhysicalMemoryInBytes': this._assessmentApiResponse?.assessmentResult?.physicalServerMemory,
'serverDatabases': this._assessmentApiResponse.assessmentResult.numberOfUserDatabases, 'serverDatabases': this._assessmentApiResponse?.assessmentResult?.numberOfUserDatabases,
'serverDatabasesReadyForMigration': this._assessmentApiResponse.assessmentResult.sqlManagedInstanceTargetReadiness.numberOfDatabasesReadyForMigration, 'serverDatabasesReadyForMigration': this._assessmentApiResponse?.assessmentResult?.sqlManagedInstanceTargetReadiness?.numberOfDatabasesReadyForMigration,
'offlineDatabases': this._assessmentApiResponse.assessmentResult.sqlManagedInstanceTargetReadiness.numberOfNonOnlineDatabases 'offlineDatabases': this._assessmentApiResponse?.assessmentResult?.sqlManagedInstanceTargetReadiness?.numberOfNonOnlineDatabases,
} }
); );
const databaseWarningsMap: Map<string, number> = new Map(); const databaseWarningsMap: Map<string, number> = new Map();
const databaseErrorsMap: Map<number, number> = new Map(); const databaseErrorsMap: Map<number, number> = new Map();
this._assessmentApiResponse.assessmentResult.databases.forEach(d => { this._assessmentApiResponse?.assessmentResult?.databases.forEach(d => {
sendSqlMigrationActionEvent( sendSqlMigrationActionEvent(
TelemetryViews.MigrationWizardTargetSelectionPage, TelemetryViews.MigrationWizardTargetSelectionPage,
@@ -980,7 +998,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
'resourceGroup': this._resourceGroup?.name, 'resourceGroup': this._resourceGroup?.name,
'location': this._targetServerInstance.location, 'location': this._targetServerInstance.location,
'targetType': this._targetType, 'targetType': this._targetType,
'hashedServerName': hashString(this._assessmentApiResponse.assessmentResult.name), 'hashedServerName': hashString(this._assessmentApiResponse?.assessmentResult?.name),
'hashedDatabaseName': hashString(this._migrationDbs[i]), 'hashedDatabaseName': hashString(this._migrationDbs[i]),
'migrationMode': isOfflineMigration ? 'offline' : 'online', 'migrationMode': isOfflineMigration ? 'offline' : 'online',
'migrationStartTime': new Date().toString(), 'migrationStartTime': new Date().toString(),

View File

@@ -26,6 +26,8 @@ export class SKURecommendationPage extends MigrationWizardPage {
private _igComponent!: azdata.TextComponent; private _igComponent!: azdata.TextComponent;
private _assessmentStatusIcon!: azdata.ImageComponent; private _assessmentStatusIcon!: azdata.ImageComponent;
private _detailsComponent!: azdata.TextComponent; private _detailsComponent!: azdata.TextComponent;
private _skipAssessmentCheckbox!: azdata.CheckBoxComponent;
private _skipAssessmentSubText!: azdata.TextComponent;
private _chooseTargetComponent!: azdata.DivContainer; private _chooseTargetComponent!: azdata.DivContainer;
private _azureSubscriptionText!: azdata.TextComponent; private _azureSubscriptionText!: azdata.TextComponent;
private _managedInstanceSubscriptionDropdown!: azdata.DropDownComponent; private _managedInstanceSubscriptionDropdown!: azdata.DropDownComponent;
@@ -90,6 +92,29 @@ export class SKURecommendationPage extends MigrationWizardPage {
}); });
this._detailsComponent = this.createDetailsComponent(view); // The details of what can be moved this._detailsComponent = this.createDetailsComponent(view); // The details of what can be moved
this._skipAssessmentCheckbox = view.modelBuilder.checkBox().withProps({
label: constants.SKU_RECOMMENDATION_ASSESSMENT_ERROR_BYPASS,
checked: false,
CSSStyles: {
...styles.SECTION_HEADER_CSS,
'margin': '10px 0 0 0',
'display': 'none'
},
}).component();
this._skipAssessmentSubText = view.modelBuilder.text().withProps({
value: constants.SKU_RECOMMENDATION_ASSESSMENT_ERROR_DETAIL,
CSSStyles: {
'margin': '0 0 0 15px',
'font-size': '13px',
'color': 'red',
'width': '590px',
'display': 'none'
},
}).component();
this._disposables.push(this._skipAssessmentCheckbox.onChanged(async (value) => {
await this._setAssessmentState(false, true);
}));
const refreshAssessmentButton = this._view.modelBuilder.button().withProps({ const refreshAssessmentButton = this._view.modelBuilder.button().withProps({
iconPath: IconPathHelper.refresh, iconPath: IconPathHelper.refresh,
@@ -113,6 +138,8 @@ export class SKURecommendationPage extends MigrationWizardPage {
igContainer, igContainer,
this._detailsComponent, this._detailsComponent,
refreshAssessmentButton, refreshAssessmentButton,
this._skipAssessmentCheckbox,
this._skipAssessmentSubText,
] ]
).withProps({ ).withProps({
CSSStyles: { CSSStyles: {
@@ -501,50 +528,49 @@ export class SKURecommendationPage extends MigrationWizardPage {
text: '', text: '',
level: azdata.window.MessageLevel.Error level: azdata.window.MessageLevel.Error
}; };
await this._assessmentComponent.updateCssStyles({ display: 'block' });
await this._formContainer.component().updateCssStyles({ display: 'none' });
this._assessmentLoader.loading = true; await this._setAssessmentState(true, false);
const serverName = (await this.migrationStateModel.getSourceConnectionProfile()).serverName; const serverName = (await this.migrationStateModel.getSourceConnectionProfile()).serverName;
this._igComponent.value = constants.ASSESSMENT_COMPLETED(serverName); const errors: string[] = [];
try { try {
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage) { if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage) {
this.migrationStateModel._assessmentResults = <ServerAssessment>this.migrationStateModel.savedInfo.serverAssessment; this.migrationStateModel._assessmentResults = <ServerAssessment>this.migrationStateModel.savedInfo.serverAssessment;
} else { } else {
await this.migrationStateModel.getDatabaseAssessments(MigrationTargetType.SQLMI); await this.migrationStateModel.getDatabaseAssessments(MigrationTargetType.SQLMI);
} }
this._detailsComponent.value = constants.SKU_RECOMMENDATION_ALL_SUCCESSFUL(this.migrationStateModel._assessmentResults.databaseAssessments.length);
const errors: string[] = [];
const assessmentError = this.migrationStateModel._assessmentResults.assessmentError; const assessmentError = this.migrationStateModel._assessmentResults.assessmentError;
if (assessmentError) { if (assessmentError) {
errors.push(`message: ${assessmentError.message} errors.push(`message: ${assessmentError.message}${EOL}stack: ${assessmentError.stack}`);
stack: ${assessmentError.stack}
`);
} }
if (this.migrationStateModel?._assessmentResults?.errors?.length! > 0) { if (this.migrationStateModel?._assessmentResults?.errors?.length! > 0) {
errors.push(...this.migrationStateModel._assessmentResults.errors?.map(e => `message: ${e.message} errors.push(...this.migrationStateModel._assessmentResults.errors?.map(
errorSummary: ${e.errorSummary} e => `message: ${e.message}${EOL}errorSummary: ${e.errorSummary}${EOL}possibleCauses: ${e.possibleCauses}${EOL}guidance: ${e.guidance}${EOL}errorId: ${e.errorId}`)!);
possibleCauses: ${e.possibleCauses}
guidance: ${e.guidance}
errorId: ${e.errorId}
`)!);
} }
} catch (e) {
console.log(e);
errors.push(constants.SKU_RECOMMENDATION_ASSESSMENT_UNEXPECTED_ERROR(serverName, e));
} finally {
this.migrationStateModel._runAssessments = errors.length > 0;
if (errors.length > 0) { if (errors.length > 0) {
this.wizard.message = { this.wizard.message = {
text: constants.SKU_RECOMMENDATION_ASSESSMENT_ERROR(serverName), text: constants.SKU_RECOMMENDATION_ASSESSMENT_ERROR(serverName),
description: errors.join(EOL), description: errors.join(EOL),
level: azdata.window.MessageLevel.Error level: azdata.window.MessageLevel.Error
}; };
this._assessmentStatusIcon.iconPath = IconPathHelper.error;
this._igComponent.value = constants.ASSESSMENT_FAILED(serverName);
this._detailsComponent.value = constants.SKU_RECOMMENDATION_ERROR;
} else {
this._assessmentStatusIcon.iconPath = IconPathHelper.completedMigration;
this._igComponent.value = constants.ASSESSMENT_COMPLETED(serverName);
this._detailsComponent.value = constants.SKU_RECOMMENDATION_ALL_SUCCESSFUL(this.migrationStateModel._assessmentResults.databaseAssessments.length);
} }
this.migrationStateModel._runAssessments = errors.length > 0;
} catch (e) {
console.log(e);
} }
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.SKURecommendation) { if ((this.migrationStateModel.resumeAssessment) && this.migrationStateModel.savedInfo.closedPage >= Page.SKURecommendation) {
if (this.migrationStateModel.savedInfo.migrationTargetType) { if (this.migrationStateModel.savedInfo.migrationTargetType) {
this._rbg.selectedCardId = this.migrationStateModel.savedInfo.migrationTargetType; this._rbg.selectedCardId = this.migrationStateModel.savedInfo.migrationTargetType;
await this.refreshCardText(); await this.refreshCardText();
@@ -552,9 +578,39 @@ errorId: ${e.errorId}
} }
await this.refreshCardText(); await this.refreshCardText();
this._assessmentLoader.loading = false; await this._setAssessmentState(false, this.migrationStateModel._runAssessments);
await this._assessmentComponent.updateCssStyles({ display: 'none' }); }
await this._formContainer.component().updateCssStyles({ display: 'block' });
private async _setAssessmentState(assessing: boolean, failedAssessment: boolean): Promise<void> {
let display: azdata.DisplayType = assessing ? 'block' : 'none';
await this._assessmentComponent.updateCssStyles({ 'display': display });
this._assessmentComponent.display = display;
display = !assessing && failedAssessment ? 'block' : 'none';
await this._skipAssessmentCheckbox.updateCssStyles({ 'display': display });
this._skipAssessmentCheckbox.display = display;
await this._skipAssessmentSubText.updateCssStyles({ 'display': display });
this._skipAssessmentSubText.display = display;
await this._formContainer.component().updateCssStyles({ 'display': !assessing ? 'block' : 'none' });
display = failedAssessment && !this._skipAssessmentCheckbox.checked ? 'none' : 'block';
await this._chooseTargetComponent.updateCssStyles({ 'display': display });
this._chooseTargetComponent.display = display;
display = !this._rbg.selectedCardId || failedAssessment && !this._skipAssessmentCheckbox.checked ? 'none' : 'inline';
await this.assessmentGroupContainer.updateCssStyles({ 'display': display });
this.assessmentGroupContainer.display = display;
display = this._rbg.selectedCardId
&& (!failedAssessment || this._skipAssessmentCheckbox.checked)
&& this.migrationStateModel._migrationDbs.length > 0
? 'inline'
: 'none';
await this._targetContainer.updateCssStyles({ 'display': display });
this._targetContainer.display = display;
this._assessmentLoader.loading = assessing;
} }
private async populateSubscriptionDropdown(): Promise<void> { private async populateSubscriptionDropdown(): Promise<void> {
@@ -698,18 +754,9 @@ errorId: ${e.errorId}
return true; return true;
}); });
this.wizard.nextButton.enabled = false; this.wizard.nextButton.enabled = false;
if (this.migrationStateModel._runAssessments) { await this.constructDetails();
await this.constructDetails();
}
await this._assessmentComponent.updateCssStyles({
display: 'none'
});
await this._formContainer.component().updateCssStyles({
display: 'block'
});
await this.populateSubscriptionDropdown(); await this.populateSubscriptionDropdown();
this.wizard.nextButton.enabled = true; this.wizard.nextButton.enabled = this.migrationStateModel._assessmentResults !== undefined;
} }
public async onPageLeave(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> { public async onPageLeave(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
@@ -726,15 +773,6 @@ errorId: ${e.errorId}
protected async handleStateChange(e: StateChangeEvent): Promise<void> { protected async handleStateChange(e: StateChangeEvent): Promise<void> {
} }
public async refreshDatabaseCount(selectedDbs: string[]): Promise<void> {
this.wizard.message = {
text: '',
level: azdata.window.MessageLevel.Error
};
this.migrationStateModel._migrationDbs = selectedDbs;
await this.refreshCardText();
}
public async refreshCardText(): Promise<void> { public async refreshCardText(): Promise<void> {
this._rbgLoader.loading = true; this._rbgLoader.loading = true;
if (this._rbg.selectedCardId === MigrationTargetType.SQLMI) { if (this._rbg.selectedCardId === MigrationTargetType.SQLMI) {
@@ -747,25 +785,16 @@ errorId: ${e.errorId}
this._targetContainer.display = (this.migrationStateModel._migrationDbs.length === 0) ? 'none' : 'inline'; this._targetContainer.display = (this.migrationStateModel._migrationDbs.length === 0) ? 'none' : 'inline';
if (this.migrationStateModel._assessmentResults) { if (this.migrationStateModel._assessmentResults) {
const dbCount = this.migrationStateModel._assessmentResults.databaseAssessments.length; const dbCount = this.migrationStateModel._assessmentResults.databaseAssessments?.length;
const dbWithoutIssuesCount = this.migrationStateModel._assessmentResults.databaseAssessments?.filter(db => db.issues?.length === 0).length;
this._rbg.cards[0].descriptions[1].textValue = constants.CAN_BE_MIGRATED(dbWithoutIssuesCount, dbCount);
this._rbg.cards[1].descriptions[1].textValue = constants.CAN_BE_MIGRATED(dbCount, dbCount);
const dbWithoutIssuesCount = this.migrationStateModel._assessmentResults.databaseAssessments.filter(db => db.issues.length === 0).length; await this._rbg.updateProperties({ cards: this._rbg.cards });
const miCardText = constants.CAN_BE_MIGRATED(dbWithoutIssuesCount, dbCount);
this._rbg.cards[0].descriptions[1].textValue = miCardText;
const vmCardText = constants.CAN_BE_MIGRATED(dbCount, dbCount);
this._rbg.cards[1].descriptions[1].textValue = vmCardText;
await this._rbg.updateProperties({
cards: this._rbg.cards
});
} else { } else {
this._rbg.cards[0].descriptions[1].textValue = ''; this._rbg.cards[0].descriptions[1].textValue = '';
this._rbg.cards[1].descriptions[1].textValue = ''; this._rbg.cards[1].descriptions[1].textValue = '';
await this._rbg.updateProperties({ cards: this._rbg.cards });
await this._rbg.updateProperties({
cards: this._rbg.cards
});
} }
if (this._rbg.selectedCardId) { if (this._rbg.selectedCardId) {