[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

@@ -83,17 +83,19 @@ export class AssessmentResultsDialog {
this._disposables.push(
this._saveButton.onClick(async () => {
const folder = await utils.promptUserForFolder();
const destinationFilePath = path.join(folder, AssessmentResultsDialog._assessmentReportName);
if (this.model._assessmentReportFilePath) {
fs.copyFile(this.model._assessmentReportFilePath, destinationFilePath, (err) => {
if (err) {
console.log(err);
} else {
void vscode.window.showInformationMessage(constants.SAVE_ASSESSMENT_REPORT_SUCCESS(destinationFilePath));
}
});
} else {
console.log('assessment report not found');
if (folder) {
const destinationFilePath = path.join(folder, AssessmentResultsDialog._assessmentReportName);
if (this.model._assessmentReportFilePath) {
fs.copyFile(this.model._assessmentReportFilePath, destinationFilePath, (err) => {
if (err) {
console.log(err);
} else {
void vscode.window.showInformationMessage(constants.SAVE_ASSESSMENT_REPORT_SUCCESS(destinationFilePath));
}
});
} else {
console.log('assessment report not found');
}
}
}));
this.dialog.customButtons = [this._saveButton];

View File

@@ -333,8 +333,6 @@ export class GetAzureRecommendationDialog {
const serverName = (await this.migrationStateModel.getSourceConnectionProfile()).serverName;
const errors: string[] = [];
try {
void vscode.window.showInformationMessage(constants.AZURE_RECOMMENDATION_OPEN_EXISTING_POPUP);
await this.skuRecommendationPage.startCardLoading();
await this.migrationStateModel.getSkuRecommendations();

View File

@@ -24,12 +24,11 @@ export class SkuEditParametersDialog {
private _scaleFactorInput!: azdata.InputBoxComponent;
private _targetPercentileDropdown!: azdata.DropDownComponent;
private _enablePreviewValue!: boolean;
private _enableElasticRecommendation!: boolean;
constructor(
public skuRecommendationPage: SKURecommendationPage,
public migrationStateModel: MigrationStateModel) {
this._enablePreviewValue = true;
}
private async initializeDialog(dialog: azdata.window.Dialog): Promise<void> {
@@ -182,6 +181,70 @@ export class SkuEditParametersDialog {
CSSStyles: { ...styles.BODY_CSS, }
}).component();
const enableElasticLabel = _view.modelBuilder.text().withProps({
value: constants.ELASTIC_RECOMMENDATION_LABEL,
width: WIZARD_INPUT_COMPONENT_WIDTH,
requiredIndicator: true,
CSSStyles: {
...styles.LABEL_CSS,
}
}).component();
const elasticButtonGroup = 'enableElasticRecommendations';
const enableElasticRadioButtonContainer = _view.modelBuilder.flexContainer()
.withProps({
CSSStyles: {
'flex-direction': 'row',
'width': 'fit-content',
'margin-top': '-1em',
'margin-bottom': '8px',
}
}).component();
const enableElasticButton = _view.modelBuilder.radioButton()
.withProps({
name: elasticButtonGroup,
label: constants.YES,
checked: this._enableElasticRecommendation,
CSSStyles: {
...styles.BODY_CSS,
'width': 'fit-content',
'margin': '0'
},
}).component();
this._disposables.push(enableElasticButton.onDidChangeCheckedState(async (e) => {
if (e) {
this._enableElasticRecommendation = true;
}
}));
const disableElasticButton = _view.modelBuilder.radioButton()
.withProps({
name: elasticButtonGroup,
label: constants.NO,
checked: !this._enableElasticRecommendation,
CSSStyles: {
...styles.BODY_CSS,
'width': 'fit-content',
'margin': '0 12px',
}
}).component();
this._disposables.push(disableElasticButton.onDidChangeCheckedState(async (e) => {
if (e) {
this._enableElasticRecommendation = false;
}
}));
enableElasticRadioButtonContainer.addItems([
enableElasticButton,
disableElasticButton
]);
const enableElasticInfoBox = _view.modelBuilder.infoBox()
.withProps({
text: constants.ELASTIC_RECOMMENDATION_INFO,
style: 'information',
CSSStyles: {
...styles.BODY_CSS,
}
}).component();
container.addItems([
description,
scaleFactorLabel,
@@ -191,6 +254,9 @@ export class SkuEditParametersDialog {
enablePreviewLabel,
enablePreviewRadioButtonContainer,
enablePreviewInfoBox,
enableElasticLabel,
enableElasticRadioButtonContainer,
enableElasticInfoBox,
]);
return container;
}
@@ -219,6 +285,7 @@ export class SkuEditParametersDialog {
this._scaleFactorInput.value = this.migrationStateModel._skuScalingFactor.toString();
this._enablePreviewValue = this.migrationStateModel._skuEnablePreview;
this._enableElasticRecommendation = this.migrationStateModel._skuEnableElastic;
(<azdata.CategoryValue[]>this._targetPercentileDropdown.values)?.forEach((percentile, index) => {
if ((<azdata.CategoryValue>percentile).name.toLowerCase() === this.migrationStateModel._skuTargetPercentile.toString()) {
selectDropDownIndex(this._targetPercentileDropdown, index);
@@ -232,6 +299,7 @@ export class SkuEditParametersDialog {
this.migrationStateModel._skuScalingFactor = Number(this._scaleFactorInput.value!);
this.migrationStateModel._skuTargetPercentile = Number((<azdata.CategoryValue>this._targetPercentileDropdown.value).name);
this.migrationStateModel._skuEnablePreview = this._enablePreviewValue;
this.migrationStateModel._skuEnableElastic = this._enableElasticRecommendation;
await this.skuRecommendationPage.refreshSkuParameters();
}

View File

@@ -20,6 +20,7 @@ export class SkuRecommendationResultsDialog {
private _isOpen: boolean = false;
private dialog: azdata.window.Dialog | undefined;
private migrationStateModel: MigrationStateModel;
// Dialog Name for Telemetry
public dialogName: string | undefined;
@@ -45,6 +46,7 @@ export class SkuRecommendationResultsDialog {
}
this.title = constants.RECOMMENDATIONS_TITLE(this.targetName);
this.migrationStateModel = model;
}
private async initializeDialog(dialog: azdata.window.Dialog): Promise<void> {
@@ -158,17 +160,24 @@ export class SkuRecommendationResultsDialog {
if (this._targetType === MigrationTargetType.SQLDB) {
const databaseNameLabel = _view.modelBuilder.text()
.withProps({
value: constants.SOURCE_DATABASE,
CSSStyles: { ...styles.LABEL_CSS, 'margin': '0', }
}).component();
const databaseNameValue = _view.modelBuilder.text()
.withProps({
value: recommendation.databaseName!,
CSSStyles: { ...styles.SECTION_HEADER_CSS, }
CSSStyles: { ...styles.BODY_CSS, 'margin': '0', }
}).component();
recommendationContainer.addItem(databaseNameLabel);
recommendationContainer.addItem(databaseNameValue);
}
const targetDeploymentTypeLabel = _view.modelBuilder.text()
.withProps({
value: constants.TARGET_DEPLOYMENT_TYPE,
CSSStyles: { ...styles.LABEL_CSS, 'margin': '0', }
CSSStyles: { ...styles.LABEL_CSS, 'margin': '12px 0 0', }
}).component();
const targetDeploymentTypeValue = _view.modelBuilder.text()
.withProps({
@@ -456,15 +465,25 @@ export class SkuRecommendationResultsDialog {
switch (this._targetType) {
case MigrationTargetType.SQLMI:
this.targetRecommendations = recommendations?.sqlMiRecommendationResults;
if (this.migrationStateModel._skuEnableElastic) {
this.targetRecommendations = recommendations?.elasticSqlMiRecommendationResults;
} else {
this.targetRecommendations = recommendations?.sqlMiRecommendationResults;
}
break;
case MigrationTargetType.SQLVM:
// elastic model currently doesn't support SQL VM, so show the baseline model results regardless of user preference
// this.targetRecommendations = recommendations?.elasticModelResults.sqlDbRecommendationResults;
this.targetRecommendations = recommendations?.sqlVmRecommendationResults;
break;
case MigrationTargetType.SQLDB:
this.targetRecommendations = recommendations?.sqlDbRecommendationResults;
if (this.migrationStateModel._skuEnableElastic) {
this.targetRecommendations = recommendations?.elasticSqlDbRecommendationResults;
} else {
this.targetRecommendations = recommendations?.sqlDbRecommendationResults;
}
break;
}
@@ -484,38 +503,39 @@ export class SkuRecommendationResultsDialog {
this._disposables.push(
this._saveButton.onClick(async () => {
const folder = await utils.promptUserForFolder();
if (folder) {
if (this.model._skuRecommendationReportFilePaths) {
if (this.model._skuRecommendationReportFilePaths) {
let sourceFilePath: string | undefined;
let destinationFilePath: string | undefined;
let sourceFilePath: string | undefined;
let destinationFilePath: string | undefined;
switch (this._targetType) {
case MigrationTargetType.SQLMI:
sourceFilePath = this.model._skuRecommendationReportFilePaths.find(filePath => filePath.includes('SkuRecommendationReport-AzureSqlManagedInstance'));
destinationFilePath = path.join(folder, 'SkuRecommendationReport-AzureSqlManagedInstance.html');
break;
switch (this._targetType) {
case MigrationTargetType.SQLMI:
sourceFilePath = this.model._skuRecommendationReportFilePaths.find(filePath => filePath.includes('SkuRecommendationReport-AzureSqlManagedInstance'));
destinationFilePath = path.join(folder, 'SkuRecommendationReport-AzureSqlManagedInstance.html');
break;
case MigrationTargetType.SQLVM:
sourceFilePath = this.model._skuRecommendationReportFilePaths.find(filePath => filePath.includes('SkuRecommendationReport-AzureSqlVirtualMachine'));
destinationFilePath = path.join(folder, 'SkuRecommendationReport-AzureSqlVirtualMachine.html');
break;
case MigrationTargetType.SQLVM:
sourceFilePath = this.model._skuRecommendationReportFilePaths.find(filePath => filePath.includes('SkuRecommendationReport-AzureSqlVirtualMachine'));
destinationFilePath = path.join(folder, 'SkuRecommendationReport-AzureSqlVirtualMachine.html');
break;
case MigrationTargetType.SQLDB:
sourceFilePath = this.model._skuRecommendationReportFilePaths.find(filePath => filePath.includes('SkuRecommendationReport-AzureSqlDatabase'));
destinationFilePath = path.join(folder, 'SkuRecommendationReport-AzureSqlDatabase.html');
break;
}
fs.copyFile(sourceFilePath!, destinationFilePath, (err) => {
if (err) {
console.log(err);
} else {
void vscode.window.showInformationMessage(constants.SAVE_RECOMMENDATION_REPORT_SUCCESS(destinationFilePath!));
case MigrationTargetType.SQLDB:
sourceFilePath = this.model._skuRecommendationReportFilePaths.find(filePath => filePath.includes('SkuRecommendationReport-AzureSqlDatabase'));
destinationFilePath = path.join(folder, 'SkuRecommendationReport-AzureSqlDatabase.html');
break;
}
});
} else {
console.log('recommendation report not found');
fs.copyFile(sourceFilePath!, destinationFilePath, (err) => {
if (err) {
console.log(err);
} else {
void vscode.window.showInformationMessage(constants.SAVE_RECOMMENDATION_REPORT_SUCCESS(destinationFilePath!));
}
});
} else {
console.log('recommendation report not found');
}
}
}));
this.dialog.customButtons = [this._saveButton];