[SQL Migration] SKU recommendation improvements + SQL DB integration bug fixes (#20174)

* WIP - show error message for failed SKU recommendation

* WIP - run query to get correct instance name

* WIP - integrate elastic model recommendation

* Remove private endpoint restriction text

* Add feature switch for elastic recommendation

* Clean up

* Clean up

* Misc UI fixes

* Update package.json with updated azdata dependency

* Remove unused lines

* Fix broken next button

* Vbump extension to 1.0.6

* Update SQL DB card to show number of recommendations for correct model
This commit is contained in:
Raymond Truong
2022-08-26 14:06:33 -07:00
committed by GitHub
parent bd0c4cdb51
commit bfcbd60d24
12 changed files with 300 additions and 181 deletions

View File

@@ -413,14 +413,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
CSSStyles: { ...styles.SECTION_HEADER_CSS }
}).component();
const azureStoragePrivateEndpointInfoBox = this._view.modelBuilder.infoBox()
.withProps({
text: constants.DATABASE_BACKUP_PRIVATE_ENDPOINT_INFO_TEXT,
style: 'information',
width: WIZARD_INPUT_COMPONENT_WIDTH,
CSSStyles: { ...styles.BODY_CSS }
}).component();
this._networkShareTargetDatabaseNamesTable = this._view.modelBuilder.declarativeTable()
.withProps({
columns: [
@@ -523,7 +515,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
.withItems([
blobTableText,
allFieldsRequiredLabel,
azureStoragePrivateEndpointInfoBox,
this._blobContainerTargetDatabaseNamesTable])
.withProps({ CSSStyles: { 'display': 'none', } })
.component();
@@ -553,14 +544,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
CSSStyles: { ...styles.BODY_CSS, 'margin-bottom': '12px' }
}).component();
const azureStoragePrivateEndpointInfoBox = this._view.modelBuilder.infoBox()
.withProps({
text: constants.DATABASE_BACKUP_PRIVATE_ENDPOINT_INFO_TEXT,
style: 'information',
width: WIZARD_INPUT_COMPONENT_WIDTH,
CSSStyles: { ...styles.BODY_CSS }
}).component();
const subscriptionLabel = this._view.modelBuilder.text()
.withProps({
value: constants.SUBSCRIPTION,
@@ -673,7 +656,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
.withItems([
azureAccountHeader,
azureAccountHelpText,
azureStoragePrivateEndpointInfoBox,
subscriptionLabel,
this._networkShareContainerSubscription,
locationLabel,

View File

@@ -68,10 +68,12 @@ export class SKURecommendationPage extends MigrationWizardPage {
private _skuScaleFactorText!: azdata.TextComponent;
private _skuTargetPercentileText!: azdata.TextComponent;
private _skuEnablePreviewSkuText!: azdata.TextComponent;
private _skuEnableElasticRecommendationsText!: azdata.TextComponent;
private assessmentGroupContainer!: azdata.FlexContainer;
private _disposables: vscode.Disposable[] = [];
private _serverName: string = '';
private _supportedProducts: Product[] = [
{
type: MigrationTargetType.SQLMI,
@@ -357,11 +359,11 @@ export class SKURecommendationPage extends MigrationWizardPage {
CSSStyles: { 'margin': '12px 0' }
}).component();
const serverName = this.migrationStateModel.serverName || (await this.migrationStateModel.getSourceConnectionProfile()).serverName;
this._serverName = this.migrationStateModel.serverName || (await this.migrationStateModel.getSourceConnectionProfile()).serverName;
const miDialog = new AssessmentResultsDialog('ownerUri', this.migrationStateModel, constants.ASSESSMENT_TILE(serverName), this, MigrationTargetType.SQLMI);
const vmDialog = new AssessmentResultsDialog('ownerUri', this.migrationStateModel, constants.ASSESSMENT_TILE(serverName), this, MigrationTargetType.SQLVM);
const dbDialog = new AssessmentResultsDialog('ownerUri', this.migrationStateModel, constants.ASSESSMENT_TILE(serverName), this, MigrationTargetType.SQLDB);
const miDialog = new AssessmentResultsDialog('ownerUri', this.migrationStateModel, constants.ASSESSMENT_TILE(this._serverName), this, MigrationTargetType.SQLMI);
const vmDialog = new AssessmentResultsDialog('ownerUri', this.migrationStateModel, constants.ASSESSMENT_TILE(this._serverName), this, MigrationTargetType.SQLVM);
const dbDialog = new AssessmentResultsDialog('ownerUri', this.migrationStateModel, constants.ASSESSMENT_TILE(this._serverName), this, MigrationTargetType.SQLDB);
this._disposables.push(button.onDidClick(async (e) => {
switch (this._rbg.selectedCardId) {
@@ -437,8 +439,6 @@ export class SKURecommendationPage extends MigrationWizardPage {
level: azdata.window.MessageLevel.Error
};
const serverName = (await this.migrationStateModel.getSourceConnectionProfile()).serverName;
if (this.migrationStateModel._runAssessments) {
const errors: string[] = [];
await this._setAssessmentState(true, false);
@@ -453,22 +453,22 @@ export class SKURecommendationPage extends MigrationWizardPage {
e => `message: ${e.message}${EOL}errorSummary: ${e.errorSummary}${EOL}possibleCauses: ${e.possibleCauses}${EOL}guidance: ${e.guidance}${EOL}errorId: ${e.errorId}`)!);
}
} catch (e) {
errors.push(constants.SKU_RECOMMENDATION_ASSESSMENT_UNEXPECTED_ERROR(serverName, e));
errors.push(constants.SKU_RECOMMENDATION_ASSESSMENT_UNEXPECTED_ERROR(this._serverName, e));
logError(TelemetryViews.MigrationWizardSkuRecommendationPage, 'SkuRecommendationUnexpectedError', e);
} finally {
this.migrationStateModel._runAssessments = errors.length > 0;
if (errors.length > 0) {
this.wizard.message = {
text: constants.SKU_RECOMMENDATION_ASSESSMENT_ERROR(serverName),
text: constants.SKU_RECOMMENDATION_ASSESSMENT_ERROR(this._serverName),
description: errors.join(EOL),
level: azdata.window.MessageLevel.Error
};
this._assessmentStatusIcon.iconPath = IconPathHelper.error;
this._igComponent.value = constants.ASSESSMENT_FAILED(serverName);
this._detailsComponent.value = constants.SKU_RECOMMENDATION_ASSESSMENT_ERROR(serverName);
this._igComponent.value = constants.ASSESSMENT_FAILED(this._serverName);
this._detailsComponent.value = constants.SKU_RECOMMENDATION_ASSESSMENT_ERROR(this._serverName);
} else {
this._assessmentStatusIcon.iconPath = IconPathHelper.completedMigration;
this._igComponent.value = constants.ASSESSMENT_COMPLETED(serverName);
this._igComponent.value = constants.ASSESSMENT_COMPLETED(this._serverName);
this._detailsComponent.value = constants.SKU_RECOMMENDATION_ALL_SUCCESSFUL(
this.migrationStateModel._assessmentResults?.databaseAssessments?.length);
}
@@ -476,7 +476,7 @@ export class SKURecommendationPage extends MigrationWizardPage {
} else {
// use prior assessment results
this._assessmentStatusIcon.iconPath = IconPathHelper.completedMigration;
this._igComponent.value = constants.ASSESSMENT_COMPLETED(serverName);
this._igComponent.value = constants.ASSESSMENT_COMPLETED(this._serverName);
this._detailsComponent.value = constants.SKU_RECOMMENDATION_ALL_SUCCESSFUL(
this.migrationStateModel._assessmentResults?.databaseAssessments?.length);
}
@@ -529,20 +529,30 @@ export class SKURecommendationPage extends MigrationWizardPage {
}
private async _setAssessmentState(assessing: boolean, failedAssessment: boolean): Promise<void> {
await utils.updateControlDisplay(this._assessmentComponent, assessing);
await utils.updateControlDisplay(this._skipAssessmentCheckbox, !assessing && failedAssessment);
await utils.updateControlDisplay(
this._assessmentComponent,
assessing,
'block');
await utils.updateControlDisplay(
this._skipAssessmentCheckbox,
!assessing && failedAssessment,
'block');
await utils.updateControlDisplay(
this._skipAssessmentSubText,
!assessing && failedAssessment,
'block');
await utils.updateControlDisplay(this._formContainer.component(), !assessing);
await utils.updateControlDisplay(
this._formContainer.component(),
!assessing,
'block');
await utils.updateControlDisplay(
this._chooseTargetComponent,
!failedAssessment || this._skipAssessmentCheckbox.checked === true);
!failedAssessment || this._skipAssessmentCheckbox.checked === true,
'block');
await utils.updateControlDisplay(
this.assessmentGroupContainer,
this._rbg.selectedCardId !== undefined && (!failedAssessment || this._skipAssessmentCheckbox.checked === true));
this._rbg.selectedCardId !== undefined && (!failedAssessment || this._skipAssessmentCheckbox.checked === true),
'inline');
this._assessmentLoader.loading = assessing;
}
@@ -611,8 +621,6 @@ export class SKURecommendationPage extends MigrationWizardPage {
if (!this.migrationStateModel._assessmentResults) {
this._rbg.cards[index].descriptions[CardDescriptionIndex.ASSESSMENT_STATUS].textValue = '';
} else {
// TO-DO: add the assessed db counts
// this._rbg.cards[index].descriptions[5].textValue = constants.ASSESSED_DBS(dbCount);
if (this.hasRecommendations()) {
this._rbg.cards[index].descriptions[CardDescriptionIndex.VIEW_SKU_DETAILS].linkDisplayValue = constants.VIEW_DETAILS;
this._rbg.cards[index].descriptions[CardDescriptionIndex.SKU_RECOMMENDATION].textStyles = {
@@ -641,7 +649,11 @@ export class SKURecommendationPage extends MigrationWizardPage {
constants.CAN_BE_MIGRATED(dbWithoutIssuesCount, dbCount);
if (this.hasRecommendations()) {
recommendation = this.migrationStateModel._skuRecommendationResults.recommendations.sqlMiRecommendationResults[0];
if (this.migrationStateModel._skuEnableElastic) {
recommendation = this.migrationStateModel._skuRecommendationResults.recommendations?.elasticSqlMiRecommendationResults[0];
} else {
recommendation = this.migrationStateModel._skuRecommendationResults.recommendations?.sqlMiRecommendationResults[0];
}
// result returned but no SKU recommended
if (!recommendation?.targetSku) {
@@ -672,7 +684,8 @@ export class SKURecommendationPage extends MigrationWizardPage {
constants.CAN_BE_MIGRATED(dbCount, dbCount);
if (this.hasRecommendations()) {
recommendation = this.migrationStateModel._skuRecommendationResults.recommendations.sqlVmRecommendationResults[0];
// elastic model currently doesn't support SQL VM, so show the baseline model results regardless of user preference
recommendation = this.migrationStateModel._skuRecommendationResults.recommendations?.sqlVmRecommendationResults[0];
// result returned but no SKU recommended
if (!recommendation?.targetSku) {
@@ -708,9 +721,10 @@ export class SKURecommendationPage extends MigrationWizardPage {
constants.CAN_BE_MIGRATED(dbWithoutIssuesCount, dbCount);
if (this.hasRecommendations()) {
const successfulRecommendationsCount =
this.migrationStateModel._skuRecommendationResults.recommendations.sqlDbRecommendationResults
.filter(r => r.targetSku !== null).length;
const recommendations = this.migrationStateModel._skuEnableElastic
? this.migrationStateModel._skuRecommendationResults.recommendations!.elasticSqlDbRecommendationResults
: this.migrationStateModel._skuRecommendationResults.recommendations!.sqlDbRecommendationResults;
const successfulRecommendationsCount = recommendations.filter(r => r.targetSku !== null).length;
this._rbg.cards[index].descriptions[CardDescriptionIndex.SKU_RECOMMENDATION].textValue =
constants.RECOMMENDATIONS_AVAILABLE(successfulRecommendationsCount);
}
@@ -768,7 +782,7 @@ export class SKURecommendationPage extends MigrationWizardPage {
private async createAssessmentInfo(): Promise<azdata.TextComponent> {
this._assessmentInfo = this._view.modelBuilder.text()
.withProps({
value: constants.ASSESSMENT_IN_PROGRESS_CONTENT((await this.migrationStateModel.getSourceConnectionProfile()).serverName),
value: constants.ASSESSMENT_IN_PROGRESS_CONTENT(this._serverName),
CSSStyles: {
...styles.BODY_CSS,
'width': '660px'
@@ -1070,6 +1084,9 @@ export class SKURecommendationPage extends MigrationWizardPage {
this.migrationStateModel._skuEnablePreview ? constants.YES : constants.NO);
this._skuEnablePreviewSkuText = skuEnablePreviewParameterGroup.text;
const skuEnableElasticRecommendationsParameterGroup = createParameterGroup(constants.ELASTIC_RECOMMENDATION_LABEL, this.migrationStateModel._skuEnableElastic ? constants.YES : constants.NO);
this._skuEnableElasticRecommendationsText = skuEnableElasticRecommendationsParameterGroup.text;
const parametersContainer = _view.modelBuilder.flexContainer()
.withProps({
CSSStyles: {
@@ -1082,6 +1099,7 @@ export class SKURecommendationPage extends MigrationWizardPage {
scaleFactorParameterGroup.flexContainer,
skuTargetPercentileParameterGroup.flexContainer,
skuEnablePreviewParameterGroup.flexContainer,
skuEnableElasticRecommendationsParameterGroup.flexContainer
]);
container.addItems([
@@ -1096,6 +1114,7 @@ export class SKURecommendationPage extends MigrationWizardPage {
this._skuScaleFactorText.value = this.migrationStateModel._skuScalingFactor.toString();
this._skuTargetPercentileText.value = constants.PERCENTAGE(this.migrationStateModel._skuTargetPercentile);
this._skuEnablePreviewSkuText.value = this.migrationStateModel._skuEnablePreview ? constants.YES : constants.NO;
this._skuEnableElasticRecommendationsText.value = this.migrationStateModel._skuEnableElastic ? constants.YES : constants.NO;
await this.refreshAzureRecommendation();
}
@@ -1103,6 +1122,16 @@ export class SKURecommendationPage extends MigrationWizardPage {
await this.startCardLoading();
this._skuLastRefreshTimeText.value = constants.LAST_REFRESHED_TIME();
await this.migrationStateModel.getSkuRecommendations();
const skuRecommendationError = this.migrationStateModel._skuRecommendationResults?.recommendationError;
if (skuRecommendationError) {
this.wizard.message = {
text: constants.SKU_RECOMMENDATION_ERROR(this._serverName),
description: skuRecommendationError.message,
level: azdata.window.MessageLevel.Error
};
}
await this.refreshSkuRecommendationComponents();
this._skuLastRefreshTimeText.value = constants.LAST_REFRESHED_TIME(new Date().toLocaleString());
}

View File

@@ -109,7 +109,6 @@ export class TargetSelectionPage extends MigrationWizardPage {
// and came back, forcibly reload the location/resource group/resource values since they will now be different
this._migrationTargetPlatform = this.migrationStateModel._targetType;
await this._azureResourceTable.setDataValues([]);
this._targetPasswordInputBox.value = '';
this.migrationStateModel._sqlMigrationServices = undefined!;
this.migrationStateModel._targetServerInstance = undefined!;
@@ -119,7 +118,6 @@ export class TargetSelectionPage extends MigrationWizardPage {
}
if (this.migrationStateModel._didUpdateDatabasesForMigration) {
await this._azureResourceTable.setDataValues([]);
this._initializeSourceTargetDatabaseMap();
this._updateConnectionButtonState();
}