diff --git a/extensions/sql-migration/src/constants/strings.ts b/extensions/sql-migration/src/constants/strings.ts index 98169a8f19..63b346202d 100644 --- a/extensions/sql-migration/src/constants/strings.ts +++ b/extensions/sql-migration/src/constants/strings.ts @@ -19,8 +19,8 @@ export function WIZARD_TITLE(instanceName: string): string { // Resume Migration Dialog export const RESUME_TITLE = localize('sql.migration.resume.title', "Run migration workflow again"); -export const START_MIGRATION = localize('sql.migration.resume.start', "Start with migration assessment again (recommended)"); -export const CONTINUE_MIGRATION = localize('sql.migration.resume.continue', "Continue last migration attempt..."); +export const START_MIGRATION = localize('sql.migration.resume.start', "Start a new session"); +export const CONTINUE_MIGRATION = localize('sql.migration.resume.continue', "Resume previously saved session"); // Databases for assessment export const DATABASE_FOR_ASSESSMENT_PAGE_TITLE = localize('sql.migration.database.assessment.title', "Databases for assessment"); @@ -63,13 +63,13 @@ export const REFRESH_ASSESSMENT_BUTTON_LABEL = localize('sql.migration.refresh.a export const SKU_RECOMMENDATION_CHOOSE_A_TARGET = localize('sql.migration.wizard.sku.choose_a_target', "Choose your Azure SQL target"); -export const SKU_RECOMMENDATION_MI_CARD_TEXT = localize('sql.migration.sku.mi.card.title', "Azure SQL Managed Instance (PaaS)"); -export const SKU_RECOMMENDATION_DB_CARD_TEXT = localize('sql.migration.sku.db.card.title', "Azure SQL Database (PaaS)"); -export const SKU_RECOMMENDATION_VM_CARD_TEXT = localize('sql.migration.sku.vm.card.title', "SQL Server on Azure Virtual Machine (IaaS)"); +export const SKU_RECOMMENDATION_MI_CARD_TEXT = localize('sql.migration.sku.mi.card.title', "Azure SQL Managed Instance"); +export const SKU_RECOMMENDATION_DB_CARD_TEXT = localize('sql.migration.sku.db.card.title', "Azure SQL Database"); +export const SKU_RECOMMENDATION_VM_CARD_TEXT = localize('sql.migration.sku.vm.card.title', "SQL Server on Azure Virtual Machine"); export const SELECT_AZURE_MI = localize('sql.migration.select.azure.mi', "Select your target Azure subscription and your target Azure SQL Managed Instance."); export const SELECT_AZURE_VM = localize('sql.migration.select.azure.vm', "Select your target Azure Subscription and your target SQL Server on Azure Virtual Machine for your target."); -export const SKU_RECOMMENDATION_VIEW_ASSESSMENT_MI = localize('sql.migration.sku.recommendation.view.assessment.mi', "To migrate to Azure SQL Managed Instance (PaaS), view assessment results and select one or more databases."); -export const SKU_RECOMMENDATION_VIEW_ASSESSMENT_VM = localize('sql.migration.sku.recommendation.view.assessment.vm', "To migrate to SQL Server on Azure Virtual Machine (IaaS), view assessment results and select one or more databases."); +export const SKU_RECOMMENDATION_VIEW_ASSESSMENT_MI = localize('sql.migration.sku.recommendation.view.assessment.mi', "To migrate to Azure SQL Managed Instance, view assessment results and select one or more databases."); +export const SKU_RECOMMENDATION_VIEW_ASSESSMENT_VM = localize('sql.migration.sku.recommendation.view.assessment.vm', "To migrate to SQL Server on Azure Virtual Machine, view assessment results and select one or more databases."); export const VIEW_SELECT_BUTTON_LABEL = localize('sql.migration.view.select.button.label', "View/Select"); export function TOTAL_DATABASES_SELECTED(selectedDbCount: number, totalDbCount: number): string { return localize('total.databases.selected', "{0} of {1} databases selected", selectedDbCount, totalDbCount); @@ -232,6 +232,9 @@ export const EMPTY_TIME = localize('sql.migration.sku.recommendations.empty.time export function LAST_REFRESHED_TIME(d: string = EMPTY_TIME): string { return localize('sql.migration.sku.recommendations.lastRefreshed', "Last refreshed: {0}", d); } +export function TIME_IN_MINUTES(val: number): number { + return val * 60000; +} // Azure SQL Target export const AZURE_SQL_TARGET_PAGE_TITLE = localize('sql.migration.wizard.target.title', "Azure SQL target"); @@ -721,7 +724,7 @@ export const REFRESH_BUTTON_LABEL = localize('sql.migration.status.refresh.label // Saved Assessment Dialog export const NEXT_LABEL = localize('sql.migration.saved.assessment.next', "Next"); export const CANCEL_LABEL = localize('sql.migration.saved.assessment.cancel', "Cancel"); -export const SAVED_ASSESSMENT_RESULT = localize('sql.migration.saved.assessment.result', "Saved assessment result"); +export const SAVED_ASSESSMENT_RESULT = localize('sql.migration.saved.assessment.result', "Saved session"); // Retry Migration export const MIGRATION_CANNOT_RETRY = localize('sql.migration.cannot.retry', 'Migration cannot be retried.'); diff --git a/extensions/sql-migration/src/dialog/assessmentResults/savedAssessmentDialog.ts b/extensions/sql-migration/src/dialog/assessmentResults/savedAssessmentDialog.ts index c06d1d5ed2..e5abce8f3e 100644 --- a/extensions/sql-migration/src/dialog/assessmentResults/savedAssessmentDialog.ts +++ b/extensions/sql-migration/src/dialog/assessmentResults/savedAssessmentDialog.ts @@ -89,14 +89,6 @@ export class SavedAssessmentDialog { public initializePageContent(view: azdata.ModelView): azdata.FlexContainer { const buttonGroup = 'resumeMigration'; - const pageTitle = view.modelBuilder.text().withProps({ - CSSStyles: { - ...styles.PAGE_TITLE_CSS, - 'margin-bottom': '12px' - }, - value: constants.RESUME_TITLE - }).component(); - const radioStart = view.modelBuilder.radioButton().withProps({ label: constants.START_MIGRATION, name: buttonGroup, @@ -137,7 +129,6 @@ export class SavedAssessmentDialog { 'margin': '20px 15px', } }).component(); - flex.addItem(pageTitle, { flex: '0 0 auto' }); flex.addItem(radioStart, { flex: '0 0 auto' }); flex.addItem(radioContinue, { flex: '0 0 auto' }); diff --git a/extensions/sql-migration/src/dialog/skuRecommendationResults/getAzureRecommendationDialog.ts b/extensions/sql-migration/src/dialog/skuRecommendationResults/getAzureRecommendationDialog.ts index 34af826a48..8aad372b44 100644 --- a/extensions/sql-migration/src/dialog/skuRecommendationResults/getAzureRecommendationDialog.ts +++ b/extensions/sql-migration/src/dialog/skuRecommendationResults/getAzureRecommendationDialog.ts @@ -308,7 +308,7 @@ export class GetAzureRecommendationDialog { this.dialog.okButton.label = GetAzureRecommendationDialog.StartButtonText; this._disposables.push(this.dialog.okButton.onClick(async () => await this.execute())); - this.dialog.cancelButton.onClick(() => this._isOpen = false); + this._disposables.push(this.dialog.cancelButton.onClick(() => this._isOpen = false)); const dialogSetupPromises: Thenable[] = []; dialogSetupPromises.push(this.initializeDialog(this.dialog)); diff --git a/extensions/sql-migration/src/dialog/skuRecommendationResults/skuEditParametersDialog.ts b/extensions/sql-migration/src/dialog/skuRecommendationResults/skuEditParametersDialog.ts index 3020bcf9dc..715f7dc1b2 100644 --- a/extensions/sql-migration/src/dialog/skuRecommendationResults/skuEditParametersDialog.ts +++ b/extensions/sql-migration/src/dialog/skuRecommendationResults/skuEditParametersDialog.ts @@ -207,7 +207,7 @@ export class SkuEditParametersDialog { this.dialog.okButton.label = SkuEditParametersDialog.UpdateButtonText; this._disposables.push(this.dialog.okButton.onClick(async () => await this.execute())); - this.dialog.cancelButton.onClick(() => this._isOpen = false); + this._disposables.push(this.dialog.cancelButton.onClick(() => this._isOpen = false)); const dialogSetupPromises: Thenable[] = []; dialogSetupPromises.push(this.initializeDialog(this.dialog)); diff --git a/extensions/sql-migration/src/models/stateMachine.ts b/extensions/sql-migration/src/models/stateMachine.ts index 680e3b7388..903039cfd2 100644 --- a/extensions/sql-migration/src/models/stateMachine.ts +++ b/extensions/sql-migration/src/models/stateMachine.ts @@ -13,7 +13,7 @@ import * as constants from '../constants/strings'; import { MigrationLocalStorage } from './migrationLocalStorage'; import * as nls from 'vscode-nls'; import { v4 as uuidv4 } from 'uuid'; -import { sendSqlMigrationActionEvent, TelemetryAction, TelemetryViews } from '../telemtery'; +import { sendSqlMigrationActionEvent, TelemetryAction, TelemetryViews, logError } from '../telemtery'; import { hashString, deepClone } from '../api/utils'; import { SKURecommendationPage } from '../wizard/skuRecommendationPage'; const localize = nls.loadMessageBundle(); @@ -218,7 +218,7 @@ export class MigrationStateModel implements Model, vscode.Disposable { public refreshPerfDataCollectionFrequency = this._performanceDataQueryIntervalInSeconds * 1000; private _autoRefreshPerfDataCollectionHandle!: NodeJS.Timeout; - public refreshGetSkuRecommendationFrequency = 600000; // 10 minutes + public refreshGetSkuRecommendationFrequency = constants.TIME_IN_MINUTES(10); private _autoRefreshGetSkuRecommendationHandle!: NodeJS.Timeout; public _skuScalingFactor!: number; @@ -370,9 +370,6 @@ export class MigrationStateModel implements Model, vscode.Disposable { // clone list of databases currently being assessed and store them, so that if the user ever changes the list we can refresh new recommendations this._skuRecommendationRecommendedDatabaseList = this._databaseAssessment.slice(); - console.log('sqlinstancerequirements: '); - console.log(this._skuRecommendationApiResponse.instanceRequirements); - if (response?.sqlDbRecommendationResults || response?.sqlMiRecommendationResults || response?.sqlVmRecommendationResults) { this._skuRecommendationResults = { recommendations: { @@ -394,7 +391,7 @@ export class MigrationStateModel implements Model, vscode.Disposable { } } catch (error) { - console.log(error); + logError(TelemetryViews.SkuRecommendationWizard, 'GetSkuRecommendationFailed', error); this._skuRecommendationResults = { recommendations: { @@ -490,7 +487,7 @@ export class MigrationStateModel implements Model, vscode.Disposable { ); } catch (e) { - console.log(e); + logError(TelemetryViews.SkuRecommendationWizard, 'GetSkuRecommendationTelemetryFailed', e); } } @@ -537,7 +534,7 @@ export class MigrationStateModel implements Model, vscode.Disposable { ); } catch (e) { - console.log(e); + logError(TelemetryViews.DataCollectionWizard, 'StartDataCollectionTelemetryFailed', e); } } @@ -581,7 +578,7 @@ export class MigrationStateModel implements Model, vscode.Disposable { clearInterval(this._autoRefreshGetSkuRecommendationHandle); } catch (error) { - console.log(error); + logError(TelemetryViews.DataCollectionWizard, 'StopDataCollectionFailed', error); } // Generate telemetry for stop data collection request @@ -602,7 +599,7 @@ export class MigrationStateModel implements Model, vscode.Disposable { ); } catch (e) { - console.log(e); + logError(TelemetryViews.DataCollectionWizard, 'StopDataCollectionTelemetryFailed', e); } } @@ -620,15 +617,15 @@ export class MigrationStateModel implements Model, vscode.Disposable { } } catch (error) { - console.log(error); + logError(TelemetryViews.DataCollectionWizard, 'RefreshDataCollectionFailed', error); } return true; } public async isWaitingForFirstTimeRefresh(): Promise { - const elapsedTimeInMins = Math.abs(new Date().getTime() - new Date(this._perfDataCollectionStartDate!).getTime()) / 60000; - const skuRecAutoRefreshTimeInMins = this.refreshGetSkuRecommendationFrequency / 60000; + const elapsedTimeInMins = Math.abs(new Date().getTime() - new Date(this._perfDataCollectionStartDate!).getTime()) / constants.TIME_IN_MINUTES(1); + const skuRecAutoRefreshTimeInMins = this.refreshGetSkuRecommendationFrequency / constants.TIME_IN_MINUTES(1); return elapsedTimeInMins < skuRecAutoRefreshTimeInMins; } @@ -1392,6 +1389,14 @@ export class MigrationStateModel implements Model, vscode.Disposable { localize('sql.migration.starting.migration.error', "An error occurred while starting the migration: '{0}'", e.message)); console.log(e); } + finally { + // kill existing data collection if user start migration + await this.refreshPerfDataCollection(); + if ((!this.resumeAssessment || this.retryMigration) && this._perfDataCollectionIsCollecting) { + void this.stopPerfDataCollection(); + void vscode.window.showInformationMessage(constants.AZURE_RECOMMENDATION_STOP_POPUP); + } + } } } diff --git a/extensions/sql-migration/src/wizard/wizardController.ts b/extensions/sql-migration/src/wizard/wizardController.ts index 487996bd6c..b00dec4fc4 100644 --- a/extensions/sql-migration/src/wizard/wizardController.ts +++ b/extensions/sql-migration/src/wizard/wizardController.ts @@ -22,6 +22,7 @@ export const WIZARD_INPUT_COMPONENT_WIDTH = '600px'; export class WizardController { private _wizardObject!: azdata.window.Wizard; private _model!: MigrationStateModel; + private _disposables: vscode.Disposable[] = []; constructor(private readonly extensionContext: vscode.ExtensionContext, model: MigrationStateModel) { this._model = model; } @@ -111,16 +112,16 @@ export class WizardController { this._model.extensionContext.subscriptions.push(this._wizardObject.doneButton.onClick(async (e) => { await stateModel.startMigration(); })); - saveAndCloseButton.onClick(async () => { + this._disposables.push(saveAndCloseButton.onClick(async () => { await stateModel.saveInfo(serverName, this._wizardObject.currentPage); await this._wizardObject.close(); if (stateModel.performanceCollectionInProgress()) { void vscode.window.showInformationMessage(loc.SAVE_AND_CLOSE_POPUP); } - }); + })); - this._wizardObject.cancelButton.onClick(e => { + this._disposables.push(this._wizardObject.cancelButton.onClick(e => { sendSqlMigrationActionEvent( TelemetryViews.SqlMigrationWizard, TelemetryAction.PageButtonClick, @@ -129,11 +130,11 @@ export class WizardController { 'buttonPressed': TelemetryAction.Cancel, 'pageTitle': this._wizardObject.pages[this._wizardObject.currentPage].title }, {}); - }); + })); this._wizardObject.doneButton.label = loc.START_MIGRATION_TEXT; - this._wizardObject.doneButton.onClick(e => { + this._disposables.push(this._wizardObject.doneButton.onClick(e => { sendSqlMigrationActionEvent( TelemetryViews.SqlMigrationWizard, TelemetryAction.PageButtonClick, @@ -142,7 +143,7 @@ export class WizardController { 'buttonPressed': TelemetryAction.Done, 'pageTitle': this._wizardObject.pages[this._wizardObject.currentPage].title }, {}); - }); + })); } private async sendPageButtonClickEvent(pageChangeInfo: azdata.window.WizardPageChangeInfo) {