mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-23 09:35:39 -05:00
[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:
@@ -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];
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
Reference in New Issue
Block a user