diff --git a/extensions/sql-migration/images/retry.svg b/extensions/sql-migration/images/retry.svg
new file mode 100644
index 0000000000..b217549642
--- /dev/null
+++ b/extensions/sql-migration/images/retry.svg
@@ -0,0 +1,3 @@
+
diff --git a/extensions/sql-migration/package.json b/extensions/sql-migration/package.json
index f1367b6d2c..6b180e4ad6 100644
--- a/extensions/sql-migration/package.json
+++ b/extensions/sql-migration/package.json
@@ -2,7 +2,7 @@
"name": "sql-migration",
"displayName": "%displayName%",
"description": "%description%",
- "version": "0.1.8",
+ "version": "0.1.9",
"publisher": "Microsoft",
"preview": true,
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt",
@@ -81,6 +81,11 @@
"command": "sqlmigration.cancel.migration",
"title": "%cancel-migration-menu%",
"category": "%migration-context-menu-category%"
+ },
+ {
+ "command": "sqlmigration.retry.migration",
+ "title": "%retry-migration-menu%",
+ "category": "%migration-context-menu-category%"
}
],
"menu": {
@@ -116,6 +121,10 @@
{
"command": "sqlmigration.cancel.migration",
"when": "false"
+ },
+ {
+ "command": "sqlmigration.retry.migration",
+ "when": "false"
}
]
},
@@ -174,4 +183,4 @@
"devDependencies": {
"@types/uuid": "^8.3.1"
}
-}
\ No newline at end of file
+}
diff --git a/extensions/sql-migration/package.nls.json b/extensions/sql-migration/package.nls.json
index ab535bbd8e..e38506c24a 100644
--- a/extensions/sql-migration/package.nls.json
+++ b/extensions/sql-migration/package.nls.json
@@ -14,5 +14,6 @@
"view-target-menu": "Azure SQL Target details",
"view-service-menu": "Database Migration Service details",
"copy-migration-menu": "Copy migration details",
- "cancel-migration-menu": "Cancel migration"
+ "cancel-migration-menu": "Cancel migration",
+ "retry-migration-menu": "Retry migration"
}
diff --git a/extensions/sql-migration/src/api/azure.ts b/extensions/sql-migration/src/api/azure.ts
index a6c8cb08d5..65f5cbfba8 100644
--- a/extensions/sql-migration/src/api/azure.ts
+++ b/extensions/sql-migration/src/api/azure.ts
@@ -362,6 +362,15 @@ export function getFullResourceGroupFromId(id: string): string {
return id.replace(RegExp('/providers/.*'), '').toLowerCase();
}
+export function getResourceName(id: string): string {
+ const splitResourceId = id.split('/');
+ return splitResourceId[splitResourceId.length - 1];
+}
+
+export function getBlobContainerId(resourceGroupId: string, storageAccountName: string, blobContainerName: string): string {
+ return `${resourceGroupId}/providers/Microsoft.Storage/storageAccounts/${storageAccountName}/blobServices/default/containers/${blobContainerName}`;
+}
+
export interface SqlMigrationServiceProperties {
name: string;
subscriptionId: string;
diff --git a/extensions/sql-migration/src/constants/helper.ts b/extensions/sql-migration/src/constants/helper.ts
index 43b8ce0906..096cb5572b 100644
--- a/extensions/sql-migration/src/constants/helper.ts
+++ b/extensions/sql-migration/src/constants/helper.ts
@@ -3,7 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { MigrationContext } from '../models/migrationLocalStorage';
+import { MigrationContext, MigrationStatus } from '../models/migrationLocalStorage';
+import { MigrationMode, MigrationTargetType } from '../models/stateMachine';
import * as loc from './strings';
export enum SQLTargetAssetType {
@@ -22,6 +23,28 @@ export function getMigrationTargetType(migration: MigrationContext): string {
}
}
-export function getMigrationMode(migration: MigrationContext): string {
- return migration.migrationContext.properties.offlineConfiguration?.offline?.valueOf() ? loc.OFFLINE : loc.OFFLINE;
+export function getMigrationTargetTypeEnum(migration: MigrationContext): MigrationTargetType | undefined {
+ switch (migration.targetManagedInstance.type) {
+ case SQLTargetAssetType.SQLMI:
+ return MigrationTargetType.SQLMI;
+ case SQLTargetAssetType.SQLVM:
+ return MigrationTargetType.SQLVM;
+ default:
+ return undefined;
+ }
+}
+
+export function getMigrationMode(migration: MigrationContext): string {
+ return migration.migrationContext.properties.offlineConfiguration?.offline?.valueOf() ? loc.OFFLINE : loc.ONLINE;
+}
+
+export function getMigrationModeEnum(migration: MigrationContext): MigrationMode {
+ return migration.migrationContext.properties.offlineConfiguration?.offline?.valueOf() ? MigrationMode.OFFLINE : MigrationMode.ONLINE;
+}
+
+export function canRetryMigration(status: string | undefined): boolean {
+ return status === undefined ||
+ status === MigrationStatus.Failed ||
+ status === MigrationStatus.Succeeded ||
+ status === MigrationStatus.Canceled;
}
diff --git a/extensions/sql-migration/src/constants/iconPathHelper.ts b/extensions/sql-migration/src/constants/iconPathHelper.ts
index b9723f6154..7916658fbb 100644
--- a/extensions/sql-migration/src/constants/iconPathHelper.ts
+++ b/extensions/sql-migration/src/constants/iconPathHelper.ts
@@ -39,6 +39,7 @@ export class IconPathHelper {
public static newSupportRequest: IconPath;
public static emptyTable: IconPath;
public static addAzureAccount: IconPath;
+ public static retry: IconPath;
public static setExtensionContext(context: vscode.ExtensionContext) {
IconPathHelper.copy = {
@@ -153,5 +154,9 @@ export class IconPathHelper {
light: context.asAbsolutePath('images/noAzureAccount.svg'),
dark: context.asAbsolutePath('images/noAzureAccount.svg')
};
+ IconPathHelper.retry = {
+ light: context.asAbsolutePath('images/retry.svg'),
+ dark: context.asAbsolutePath('images/retry.svg')
+ };
}
}
diff --git a/extensions/sql-migration/src/constants/strings.ts b/extensions/sql-migration/src/constants/strings.ts
index ea0c79bad6..d2d8343509 100644
--- a/extensions/sql-migration/src/constants/strings.ts
+++ b/extensions/sql-migration/src/constants/strings.ts
@@ -537,7 +537,14 @@ export const AUTHENTICATION_TYPE = localize('sql.migration.authentication.type',
export const REFRESH_BUTTON_LABEL = localize('sql.migration.status.refresh.label', 'Refresh');
// 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");
+
+// Retry Migration
+export const MIGRATION_CANNOT_RETRY = localize('sql.migration.cannot.retry', 'Migration cannot be retried.');
+export const RETRY_MIGRATION = localize('sql.migration.retry.migration', "Retry migration");
+export const MIGRATION_RETRY_ERROR = localize('sql.migration.retry.migration.error', 'An error occurred while retrying the migration.');
+
+export const INVALID_OWNER_URI = localize('sql.migration.invalid.owner.uri.error', 'Cannot connect to the database due to invalid OwnerUri (Parameter \'OwnerUri\')');
+export const DATABASE_BACKUP_PAGE_LOAD_ERROR = localize('sql.migration.database.backup.load.error', 'An error occurred while accessing database details.');
diff --git a/extensions/sql-migration/src/dashboard/sqlServerDashboard.ts b/extensions/sql-migration/src/dashboard/sqlServerDashboard.ts
index a5e780ce70..aea2db2bae 100644
--- a/extensions/sql-migration/src/dashboard/sqlServerDashboard.ts
+++ b/extensions/sql-migration/src/dashboard/sqlServerDashboard.ts
@@ -33,6 +33,7 @@ interface StatusCard {
}
export class DashboardWidget {
+ private _context: vscode.ExtensionContext;
private _migrationStatusCardsContainer!: azdata.FlexContainer;
private _migrationStatusCardLoadingContainer!: azdata.LoadingComponent;
@@ -52,7 +53,8 @@ export class DashboardWidget {
private isRefreshing: boolean = false;
- constructor() {
+ constructor(context: vscode.ExtensionContext) {
+ this._context = context;
}
private async getCurrentMigrations(): Promise {
@@ -470,7 +472,7 @@ export class DashboardWidget {
this._disposables.push(this._viewAllMigrationsButton.onDidClick(async (e) => {
const migrationStatus = await this.getCurrentMigrations();
- new MigrationStatusDialog(migrationStatus ? migrationStatus : await this.getMigrations(), AdsMigrationStatus.ALL).initialize();
+ new MigrationStatusDialog(this._context, migrationStatus ? migrationStatus : await this.getMigrations(), AdsMigrationStatus.ALL).initialize();
}));
const refreshButton = view.modelBuilder.hyperlink().withProps({
@@ -596,7 +598,7 @@ export class DashboardWidget {
loc.MIGRATION_IN_PROGRESS
);
this._disposables.push(this._inProgressMigrationButton.container.onDidClick(async (e) => {
- const dialog = new MigrationStatusDialog(await this.getCurrentMigrations(), AdsMigrationStatus.ONGOING);
+ const dialog = new MigrationStatusDialog(this._context, await this.getCurrentMigrations(), AdsMigrationStatus.ONGOING);
dialog.initialize();
}));
@@ -610,7 +612,7 @@ export class DashboardWidget {
true
);
this._disposables.push(this._inProgressWarningMigrationButton.container.onDidClick(async (e) => {
- const dialog = new MigrationStatusDialog(await this.getCurrentMigrations(), AdsMigrationStatus.ONGOING);
+ const dialog = new MigrationStatusDialog(this._context, await this.getCurrentMigrations(), AdsMigrationStatus.ONGOING);
dialog.initialize();
}));
@@ -623,7 +625,7 @@ export class DashboardWidget {
loc.MIGRATION_COMPLETED
);
this._disposables.push(this._successfulMigrationButton.container.onDidClick(async (e) => {
- const dialog = new MigrationStatusDialog(await this.getCurrentMigrations(), AdsMigrationStatus.SUCCEEDED);
+ const dialog = new MigrationStatusDialog(this._context, await this.getCurrentMigrations(), AdsMigrationStatus.SUCCEEDED);
dialog.initialize();
}));
this._migrationStatusCardsContainer.addItem(
@@ -636,7 +638,7 @@ export class DashboardWidget {
loc.MIGRATION_CUTOVER_CARD
);
this._disposables.push(this._completingMigrationButton.container.onDidClick(async (e) => {
- const dialog = new MigrationStatusDialog(await this.getCurrentMigrations(), AdsMigrationStatus.COMPLETING);
+ const dialog = new MigrationStatusDialog(this._context, await this.getCurrentMigrations(), AdsMigrationStatus.COMPLETING);
dialog.initialize();
}));
this._migrationStatusCardsContainer.addItem(
@@ -648,7 +650,7 @@ export class DashboardWidget {
loc.MIGRATION_FAILED
);
this._disposables.push(this._failedMigrationButton.container.onDidClick(async (e) => {
- const dialog = new MigrationStatusDialog(await this.getCurrentMigrations(), AdsMigrationStatus.FAILED);
+ const dialog = new MigrationStatusDialog(this._context, await this.getCurrentMigrations(), AdsMigrationStatus.FAILED);
dialog.initialize();
}));
this._migrationStatusCardsContainer.addItem(
diff --git a/extensions/sql-migration/src/dialog/assessmentResults/assessmentResultsDialog.ts b/extensions/sql-migration/src/dialog/assessmentResults/assessmentResultsDialog.ts
index ad9ea60fca..f2d45dd518 100644
--- a/extensions/sql-migration/src/dialog/assessmentResults/assessmentResultsDialog.ts
+++ b/extensions/sql-migration/src/dialog/assessmentResults/assessmentResultsDialog.ts
@@ -5,7 +5,7 @@
import * as azdata from 'azdata';
import * as vscode from 'vscode';
-import { MigrationStateModel, MigrationTargetType } from '../../models/stateMachine';
+import { MigrationStateModel, MigrationTargetType, Page } from '../../models/stateMachine';
import { SqlDatabaseTree } from './sqlDatabasesTree';
import { SqlMigrationImpactedObjectInfo } from '../../../../mssql/src/mssql';
import { SKURecommendationPage } from '../../wizard/skuRecommendationPage';
@@ -32,7 +32,7 @@ export class AssessmentResultsDialog {
constructor(public ownerUri: string, public model: MigrationStateModel, public title: string, private _skuRecommendationPage: SKURecommendationPage, private _targetType: MigrationTargetType) {
this._model = model;
- if (this._model.resumeAssessment && this._model.savedInfo.closedPage >= 2) {
+ if (this._model.resumeAssessment && this._model.savedInfo.closedPage >= Page.DatabaseBackup) {
this._model._databaseAssessment = this._model.savedInfo.databaseAssessment;
}
this._tree = new SqlDatabaseTree(this._model, this._targetType);
diff --git a/extensions/sql-migration/src/dialog/assessmentResults/sqlDatabasesTree.ts b/extensions/sql-migration/src/dialog/assessmentResults/sqlDatabasesTree.ts
index 5fa1ea1cea..cedbf6ff98 100644
--- a/extensions/sql-migration/src/dialog/assessmentResults/sqlDatabasesTree.ts
+++ b/extensions/sql-migration/src/dialog/assessmentResults/sqlDatabasesTree.ts
@@ -5,7 +5,7 @@
import * as azdata from 'azdata';
import * as vscode from 'vscode';
import { SqlMigrationAssessmentResultItem, SqlMigrationImpactedObjectInfo } from '../../../../mssql/src/mssql';
-import { MigrationStateModel, MigrationTargetType } from '../../models/stateMachine';
+import { MigrationStateModel, MigrationTargetType, Page } from '../../models/stateMachine';
import * as constants from '../../constants/strings';
import { debounce } from '../../api/utils';
import { IconPath, IconPathHelper } from '../../constants/iconPathHelper';
@@ -142,7 +142,7 @@ export class SqlDatabaseTree {
...styles.BOLD_NOTE_CSS,
'margin': '0px 15px 0px 15px'
},
- value: constants.DATABASES(0, this._model._databaseAssessment.length)
+ value: constants.DATABASES(0, this._model._databaseAssessment?.length)
}).component();
return this._databaseCount;
}
@@ -187,10 +187,7 @@ export class SqlDatabaseTree {
).component();
this._disposables.push(this._databaseTable.onDataChanged(async () => {
- await this._databaseCount.updateProperties({
- 'value': constants.DATABASES(this.selectedDbs().length, this._model._databaseAssessment.length)
- });
- this._model._databaseSelection = this._databaseTable.dataValues;
+ await this.updateValuesOnSelection();
}));
this._disposables.push(this._databaseTable.onRowSelected(async (e) => {
@@ -200,7 +197,7 @@ export class SqlDatabaseTree {
this._activeIssues = [];
}
this._dbName.value = this._dbNames[e.row];
- this._recommendationTitle.value = constants.ISSUES_COUNT(this._activeIssues.length);
+ this._recommendationTitle.value = constants.ISSUES_COUNT(this._activeIssues?.length);
this._recommendation.value = constants.ISSUES_DETAILS;
await this._resultComponent.updateCssStyles({
'display': 'block'
@@ -307,7 +304,7 @@ export class SqlDatabaseTree {
'display': 'none'
});
this._recommendation.value = constants.WARNINGS_DETAILS;
- this._recommendationTitle.value = constants.WARNINGS_COUNT(this._activeIssues.length);
+ this._recommendationTitle.value = constants.WARNINGS_COUNT(this._activeIssues?.length);
if (this._targetType === MigrationTargetType.SQLMI) {
await this.refreshResults();
}
@@ -424,7 +421,7 @@ export class SqlDatabaseTree {
}
private handleFailedAssessment(): boolean {
- const failedAssessment: boolean = this._model._assessmentResults.assessmentError !== undefined
+ const failedAssessment: boolean = this._model._assessmentResults?.assessmentError !== undefined
|| (this._model._assessmentResults?.errors?.length || 0) > 0;
if (failedAssessment) {
this._dialog.message = {
@@ -439,12 +436,12 @@ export class SqlDatabaseTree {
private getAssessmentError(): string {
const errors: string[] = [];
- const assessmentError = this._model._assessmentResults.assessmentError;
+ 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(
+ 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}`)!);
}
@@ -791,7 +788,7 @@ export class SqlDatabaseTree {
public async refreshResults(): Promise {
if (this._targetType === MigrationTargetType.SQLMI) {
- if (this._activeIssues.length === 0) {
+ if (this._activeIssues?.length === 0) {
/// show no issues here
await this._assessmentsTable.updateCssStyles({
'display': 'none',
@@ -858,7 +855,7 @@ export class SqlDatabaseTree {
|| [];
await this._assessmentResultsTable.setDataValues(assessmentResults);
- this._assessmentResultsTable.selectedRow = assessmentResults.length > 0 ? 0 : -1;
+ this._assessmentResultsTable.selectedRow = assessmentResults?.length > 0 ? 0 : -1;
}
public async refreshAssessmentDetails(selectedIssue?: SqlMigrationAssessmentResultItem): Promise {
@@ -872,7 +869,7 @@ export class SqlDatabaseTree {
await this._impactedObjectsTable.setDataValues(this._impactedObjects.map(
(object) => [{ value: object.objectType }, { value: object.name }]));
- this._impactedObjectsTable.selectedRow = this._impactedObjects.length > 0 ? 0 : -1;
+ this._impactedObjectsTable.selectedRow = this._impactedObjects?.length > 0 ? 0 : -1;
}
public refreshImpactedObject(impactedObject?: SqlMigrationImpactedObjectInfo): void {
@@ -927,17 +924,17 @@ export class SqlDatabaseTree {
style: styleLeft
},
{
- value: this._model._assessmentResults.issues.length,
+ value: this._model._assessmentResults?.issues?.length,
style: styleRight
}
]
];
- this._model._assessmentResults.databaseAssessments.sort((db1, db2) => {
- return db2.issues.length - db1.issues.length;
+ this._model._assessmentResults?.databaseAssessments.sort((db1, db2) => {
+ return db2.issues?.length - db1.issues?.length;
});
// Reset the dbName list so that it is in sync with the table
- this._dbNames = this._model._assessmentResults.databaseAssessments.map(da => da.name);
- this._model._assessmentResults.databaseAssessments.forEach((db) => {
+ this._dbNames = this._model._assessmentResults?.databaseAssessments.map(da => da.name);
+ this._model._assessmentResults?.databaseAssessments.forEach((db) => {
let selectable = true;
if (db.issues.find(item => item.databaseRestoreFails)) {
selectable = false;
@@ -954,7 +951,7 @@ export class SqlDatabaseTree {
style: styleLeft
},
{
- value: db.issues.length,
+ value: db.issues?.length,
style: styleRight
}
]
@@ -962,13 +959,27 @@ export class SqlDatabaseTree {
});
}
await this._instanceTable.setDataValues(instanceTableValues);
- if (this._model.resumeAssessment && this._model.savedInfo.closedPage >= 2) {
+ if (this._model.resumeAssessment && this._model.savedInfo.closedPage >= Page.SKURecommendation && this._targetType === this._model.savedInfo.migrationTargetType) {
await this._databaseTable.setDataValues(this._model.savedInfo.migrationDatabases);
} else {
+ if (this._model.retryMigration && this._targetType === this._model.savedInfo.migrationTargetType) {
+ const sourceDatabaseName = this._model.savedInfo.databaseList[0];
+ const sourceDatabaseIndex = this._dbNames.indexOf(sourceDatabaseName);
+ this._databaseTableValues[sourceDatabaseIndex][0].value = true;
+ }
+
await this._databaseTable.setDataValues(this._databaseTableValues);
+ await this.updateValuesOnSelection();
}
}
+ private async updateValuesOnSelection() {
+ await this._databaseCount.updateProperties({
+ 'value': constants.DATABASES(this.selectedDbs()?.length, this._model._databaseAssessment?.length)
+ });
+ this._model._databaseSelection = this._databaseTable.dataValues;
+ }
+
// undo when bug #16445 is fixed
private createIconTextCell(icon: IconPath, text: string): string {
return text;
diff --git a/extensions/sql-migration/src/dialog/migrationCutover/migrationCutoverDialog.ts b/extensions/sql-migration/src/dialog/migrationCutover/migrationCutoverDialog.ts
index 5f666edce9..e063040668 100644
--- a/extensions/sql-migration/src/dialog/migrationCutover/migrationCutoverDialog.ts
+++ b/extensions/sql-migration/src/dialog/migrationCutover/migrationCutoverDialog.ts
@@ -12,15 +12,19 @@ import * as loc from '../../constants/strings';
import { convertByteSizeToReadableUnit, convertIsoTimeToLocalTime, getSqlServerName, getMigrationStatusImage, SupportedAutoRefreshIntervals, clearDialogMessage, displayDialogErrorMessage } from '../../api/utils';
import { EOL } from 'os';
import { ConfirmCutoverDialog } from './confirmCutoverDialog';
+import { RetryMigrationDialog } from '../retryMigration/retryMigrationDialog';
import * as styles from '../../constants/styles';
+import { canRetryMigration } from '../../constants/helper';
const refreshFrequency: SupportedAutoRefreshIntervals = 30000;
const statusImageSize: number = 14;
export class MigrationCutoverDialog {
+ private _context: vscode.ExtensionContext;
private _dialogObject!: azdata.window.Dialog;
private _view!: azdata.ModelView;
private _model: MigrationCutoverDialogModel;
+ private _migration: MigrationContext;
private _databaseTitleName!: azdata.TextComponent;
private _cutoverButton!: azdata.ButtonComponent;
@@ -29,6 +33,7 @@ export class MigrationCutoverDialog {
private _refreshLoader!: azdata.LoadingComponent;
private _copyDatabaseMigrationDetails!: azdata.ButtonComponent;
private _newSupportRequest!: azdata.ButtonComponent;
+ private _retryButton!: azdata.ButtonComponent;
private _sourceDatabaseInfoField!: InfoFieldSchema;
private _sourceDetailsInfoField!: InfoFieldSchema;
@@ -53,7 +58,9 @@ export class MigrationCutoverDialog {
readonly _infoFieldWidth: string = '250px';
- constructor(migration: MigrationContext) {
+ constructor(context: vscode.ExtensionContext, migration: MigrationContext) {
+ this._context = context;
+ this._migration = migration;
this._model = new MigrationCutoverDialogModel(migration);
this._dialogObject = azdata.window.createModelViewDialog('', 'MigrationCutoverDialog', 'wide');
}
@@ -301,11 +308,11 @@ export class MigrationCutoverDialog {
iconWidth: '16px',
label: loc.COMPLETE_CUTOVER,
height: '20px',
- width: '150px',
+ width: '140px',
enabled: false,
CSSStyles: {
...styles.BODY_CSS,
- 'display': this._isOnlineMigration() ? 'inline' : 'none'
+ 'display': this._isOnlineMigration() ? 'block' : 'none'
}
}).component();
@@ -330,7 +337,7 @@ export class MigrationCutoverDialog {
iconWidth: '16px',
label: loc.CANCEL_MIGRATION,
height: '20px',
- width: '150px',
+ width: '140px',
enabled: false,
CSSStyles: {
...styles.BODY_CSS,
@@ -353,6 +360,28 @@ export class MigrationCutoverDialog {
flex: '0'
});
+ this._retryButton = this._view.modelBuilder.button().withProps({
+ label: loc.RETRY_MIGRATION,
+ iconPath: IconPathHelper.retry,
+ enabled: false,
+ iconHeight: '16px',
+ iconWidth: '16px',
+ height: '20px',
+ width: '120px',
+ CSSStyles: {
+ ...styles.BODY_CSS,
+ }
+ }).component();
+ this._disposables.push(this._retryButton.onDidClick(
+ async (e) => {
+ await this.refreshStatus();
+ let retryMigrationDialog = new RetryMigrationDialog(this._context, this._migration);
+ await retryMigrationDialog.openDialog();
+ }
+ ));
+ headerActions.addItem(this._retryButton, {
+ flex: '0',
+ });
this._refreshButton = this._view.modelBuilder.button().withProps({
iconPath: IconPathHelper.refresh,
@@ -360,7 +389,7 @@ export class MigrationCutoverDialog {
iconWidth: '16px',
label: 'Refresh',
height: '20px',
- width: '100px',
+ width: '80px',
CSSStyles: {
...styles.BODY_CSS,
}
@@ -379,7 +408,7 @@ export class MigrationCutoverDialog {
iconWidth: '16px',
label: loc.COPY_MIGRATION_DETAILS,
height: '20px',
- width: '200px',
+ width: '160px',
CSSStyles: {
...styles.BODY_CSS,
}
@@ -406,7 +435,7 @@ export class MigrationCutoverDialog {
iconHeight: '16px',
iconWidth: '16px',
height: '20px',
- width: '180px',
+ width: '160px',
CSSStyles: {
...styles.BODY_CSS,
}
@@ -567,7 +596,7 @@ export class MigrationCutoverDialog {
if (this._isOnlineMigration()) {
await this._cutoverButton.updateCssStyles({
- 'display': 'inline'
+ 'display': 'block'
});
}
@@ -720,6 +749,9 @@ export class MigrationCutoverDialog {
this._cancelButton.enabled =
migrationStatusTextValue === MigrationStatus.Creating ||
migrationStatusTextValue === MigrationStatus.InProgress;
+
+ this._retryButton.enabled = canRetryMigration(migrationStatusTextValue);
+
} catch (e) {
displayDialogErrorMessage(this._dialogObject, loc.MIGRATION_STATUS_REFRESH_ERROR, e);
console.log(e);
diff --git a/extensions/sql-migration/src/dialog/migrationStatus/migrationStatusDialog.ts b/extensions/sql-migration/src/dialog/migrationStatus/migrationStatusDialog.ts
index e62a930ebe..e4eb74540b 100644
--- a/extensions/sql-migration/src/dialog/migrationStatus/migrationStatusDialog.ts
+++ b/extensions/sql-migration/src/dialog/migrationStatus/migrationStatusDialog.ts
@@ -14,7 +14,8 @@ import { clearDialogMessage, convertTimeDifferenceToDuration, displayDialogError
import { SqlMigrationServiceDetailsDialog } from '../sqlMigrationService/sqlMigrationServiceDetailsDialog';
import { ConfirmCutoverDialog } from '../migrationCutover/confirmCutoverDialog';
import { MigrationCutoverDialogModel } from '../migrationCutover/migrationCutoverDialogModel';
-import { getMigrationTargetType, getMigrationMode } from '../../constants/helper';
+import { getMigrationTargetType, getMigrationMode, canRetryMigration } from '../../constants/helper';
+import { RetryMigrationDialog } from '../retryMigration/retryMigrationDialog';
const refreshFrequency: SupportedAutoRefreshIntervals = 180000;
@@ -29,9 +30,11 @@ const MenuCommands = {
ViewService: 'sqlmigration.view.service',
CopyMigration: 'sqlmigration.copy.migration',
CancelMigration: 'sqlmigration.cancel.migration',
+ RetryMigration: 'sqlmigration.retry.migration',
};
export class MigrationStatusDialog {
+ private _context: vscode.ExtensionContext;
private _model: MigrationStatusDialogModel;
private _dialogObject!: azdata.window.Dialog;
private _view!: azdata.ModelView;
@@ -45,7 +48,8 @@ export class MigrationStatusDialog {
private isRefreshing = false;
- constructor(migrations: MigrationContext[], private _filter: AdsMigrationStatus) {
+ constructor(context: vscode.ExtensionContext, migrations: MigrationContext[], private _filter: AdsMigrationStatus) {
+ this._context = context;
this._model = new MigrationStatusDialogModel(migrations);
this._dialogObject = azdata.window.createModelViewDialog(loc.MIGRATION_STATUS, 'MigrationControllerDialog', 'wide');
}
@@ -221,7 +225,7 @@ export class MigrationStatusDialog {
async (migrationId: string) => {
try {
const migration = this._model._migrations.find(migration => migration.migrationContext.id === migrationId);
- const dialog = new MigrationCutoverDialog(migration!);
+ const dialog = new MigrationCutoverDialog(this._context, migration!);
await dialog.initialize();
} catch (e) {
console.log(e);
@@ -302,6 +306,25 @@ export class MigrationStatusDialog {
console.log(e);
}
}));
+
+ this._disposables.push(vscode.commands.registerCommand(
+ MenuCommands.RetryMigration,
+ async (migrationId: string) => {
+ try {
+ clearDialogMessage(this._dialogObject);
+ const migration = this._model._migrations.find(migration => migration.migrationContext.id === migrationId);
+ if (canRetryMigration(migration?.migrationContext.properties.migrationStatus)) {
+ let retryMigrationDialog = new RetryMigrationDialog(this._context, migration!);
+ await retryMigrationDialog.openDialog();
+ }
+ else {
+ await vscode.window.showInformationMessage(loc.MIGRATION_CANNOT_RETRY);
+ }
+ } catch (e) {
+ displayDialogErrorMessage(this._dialogObject, loc.MIGRATION_RETRY_ERROR, e);
+ console.log(e);
+ }
+ }));
}
private async populateMigrationTable(): Promise {
@@ -366,7 +389,7 @@ export class MigrationStatusDialog {
}).component();
this._disposables.push(databaseHyperLink.onDidClick(
- async (e) => await (new MigrationCutoverDialog(migration)).initialize()));
+ async (e) => await (new MigrationCutoverDialog(this._context, migration)).initialize()));
return this._view.modelBuilder
.flexContainer()
@@ -416,6 +439,10 @@ export class MigrationStatusDialog {
menuCommands.push(MenuCommands.CancelMigration);
}
+ if (canRetryMigration(migrationStatus)) {
+ menuCommands.push(MenuCommands.RetryMigration);
+ }
+
return menuCommands;
}
diff --git a/extensions/sql-migration/src/dialog/retryMigration/retryMigrationDialog.ts b/extensions/sql-migration/src/dialog/retryMigration/retryMigrationDialog.ts
new file mode 100644
index 0000000000..d3e0c05727
--- /dev/null
+++ b/extensions/sql-migration/src/dialog/retryMigration/retryMigrationDialog.ts
@@ -0,0 +1,154 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import * as azdata from 'azdata';
+import * as vscode from 'vscode';
+import * as mssql from '../../../../mssql';
+import { azureResource } from 'azureResource';
+import { getLocations, getResourceGroupFromId, getBlobContainerId, getFullResourceGroupFromId, getResourceName } from '../../api/azure';
+import { MigrationMode, MigrationStateModel, NetworkContainerType, SavedInfo, Page } from '../../models/stateMachine';
+import { MigrationContext } from '../../models/migrationLocalStorage';
+import { WizardController } from '../../wizard/wizardController';
+import { getMigrationModeEnum, getMigrationTargetTypeEnum } from '../../constants/helper';
+
+export class RetryMigrationDialog {
+ private _context: vscode.ExtensionContext;
+ private _migration: MigrationContext;
+
+ constructor(context: vscode.ExtensionContext, migration: MigrationContext) {
+ this._context = context;
+ this._migration = migration;
+ }
+
+ private createMigrationStateModel(migration: MigrationContext, connectionId: string, serverName: string, api: mssql.IExtension, location: azureResource.AzureLocation): MigrationStateModel {
+ let stateModel = new MigrationStateModel(this._context, connectionId, api.sqlMigration);
+
+ const sourceDatabaseName = migration.migrationContext.properties.sourceDatabaseName;
+ let savedInfo: SavedInfo;
+ savedInfo = {
+ closedPage: Page.AzureAccount,
+
+ // AzureAccount
+ azureAccount: migration.azureAccount,
+ azureTenant: migration.azureAccount.properties.tenants[0],
+
+ // DatabaseSelector
+ selectedDatabases: [],
+
+ // SKURecommendation
+ databaseAssessment: [],
+ databaseList: [sourceDatabaseName],
+ migrationDatabases: [],
+ serverAssessment: null,
+
+ migrationTargetType: getMigrationTargetTypeEnum(migration)!,
+ subscription: migration.subscription,
+ location: location,
+ resourceGroup: {
+ id: getFullResourceGroupFromId(migration.targetManagedInstance.id),
+ name: getResourceGroupFromId(migration.targetManagedInstance.id),
+ subscription: migration.subscription
+ },
+ targetServerInstance: migration.targetManagedInstance,
+
+ // MigrationMode
+ migrationMode: getMigrationModeEnum(migration),
+
+ // DatabaseBackup
+ targetSubscription: migration.subscription,
+ targetDatabaseNames: [migration.migrationContext.name],
+ networkContainerType: null,
+ networkShare: null,
+ blobs: [],
+
+ // Integration Runtime
+ migrationServiceId: migration.migrationContext.properties.migrationService,
+ };
+
+ const getStorageAccountResourceGroup = (storageAccountResourceId: string) => {
+ return {
+ id: getFullResourceGroupFromId(storageAccountResourceId!),
+ name: getResourceGroupFromId(storageAccountResourceId!),
+ subscription: migration.subscription
+ };
+ };
+ const getStorageAccount = (storageAccountResourceId: string) => {
+ const storageAccountName = getResourceName(storageAccountResourceId);
+ return {
+ type: 'microsoft.storage/storageaccounts',
+ id: storageAccountResourceId!,
+ tenantId: savedInfo.azureTenant?.id!,
+ subscriptionId: migration.subscription.id,
+ name: storageAccountName,
+ location: savedInfo.location!.name,
+ };
+ };
+
+ const sourceLocation = migration.migrationContext.properties.backupConfiguration.sourceLocation;
+ if (sourceLocation?.fileShare) {
+ savedInfo.networkContainerType = NetworkContainerType.NETWORK_SHARE;
+ const storageAccountResourceId = migration.migrationContext.properties.backupConfiguration.targetLocation?.storageAccountResourceId!;
+ savedInfo.networkShare = {
+ password: '',
+ networkShareLocation: sourceLocation?.fileShare?.path!,
+ windowsUser: sourceLocation?.fileShare?.username!,
+ storageAccount: getStorageAccount(storageAccountResourceId!),
+ resourceGroup: getStorageAccountResourceGroup(storageAccountResourceId!),
+ storageKey: ''
+ };
+ } else if (sourceLocation?.azureBlob) {
+ savedInfo.networkContainerType = NetworkContainerType.BLOB_CONTAINER;
+ const storageAccountResourceId = sourceLocation?.azureBlob?.storageAccountResourceId!;
+ savedInfo.blobs = [
+ {
+ blobContainer: {
+ id: getBlobContainerId(getFullResourceGroupFromId(storageAccountResourceId!), getResourceName(storageAccountResourceId!), sourceLocation?.azureBlob.blobContainerName),
+ name: sourceLocation?.azureBlob.blobContainerName,
+ subscription: migration.subscription
+ },
+ lastBackupFile: getMigrationModeEnum(migration) === MigrationMode.OFFLINE ? migration.migrationContext.properties.offlineConfiguration.lastBackupName! : undefined,
+ storageAccount: getStorageAccount(storageAccountResourceId!),
+ resourceGroup: getStorageAccountResourceGroup(storageAccountResourceId!),
+ storageKey: ''
+ }
+ ];
+ }
+
+ stateModel.retryMigration = true;
+ stateModel.savedInfo = savedInfo;
+ stateModel.serverName = serverName;
+ return stateModel;
+ }
+
+ public async openDialog(dialogName?: string) {
+ const locations = await getLocations(this._migration.azureAccount, this._migration.subscription);
+ let location: azureResource.AzureLocation;
+ locations.forEach(azureLocation => {
+ if (azureLocation.name === this._migration.targetManagedInstance.location) {
+ location = azureLocation;
+ }
+ });
+
+ let activeConnection = await azdata.connection.getCurrentConnection();
+ let connectionId: string = '';
+ let serverName: string = '';
+ if (!activeConnection) {
+ const connection = await azdata.connection.openConnectionDialog();
+ if (connection) {
+ connectionId = connection.connectionId;
+ serverName = connection.options.server;
+ }
+ } else {
+ connectionId = activeConnection.connectionId;
+ serverName = activeConnection.serverName;
+ }
+
+ const api = (await vscode.extensions.getExtension(mssql.extension.name)?.activate()) as mssql.IExtension;
+ const stateModel = this.createMigrationStateModel(this._migration, connectionId, serverName, api, location!);
+
+ const wizardController = new WizardController(this._context, stateModel);
+ await wizardController.openWizard(stateModel.sourceConnectionId);
+ }
+}
diff --git a/extensions/sql-migration/src/main.ts b/extensions/sql-migration/src/main.ts
index 2ddfffbe48..263ed816f1 100644
--- a/extensions/sql-migration/src/main.ts
+++ b/extensions/sql-migration/src/main.ts
@@ -108,11 +108,7 @@ class SQLMigration {
await wizardController.openWizard(connectionId);
}
}
-
}
-
-
-
}
private checkSavedInfo(serverName: string): SavedInfo | undefined {
@@ -138,7 +134,7 @@ let sqlMigration: SQLMigration;
export async function activate(context: vscode.ExtensionContext) {
sqlMigration = new SQLMigration(context);
await sqlMigration.registerCommands();
- let widget = new DashboardWidget();
+ let widget = new DashboardWidget(context);
widget.register();
}
diff --git a/extensions/sql-migration/src/models/stateMachine.ts b/extensions/sql-migration/src/models/stateMachine.ts
index e1ff2fa30e..ec56c9a81f 100644
--- a/extensions/sql-migration/src/models/stateMachine.ts
+++ b/extensions/sql-migration/src/models/stateMachine.ts
@@ -71,6 +71,7 @@ export enum Page {
export enum WizardEntryPoint {
Default = 'Default',
SaveAndClose = 'SaveAndClose',
+ RetryMigration = 'RetryMigration',
}
export interface DatabaseBackupModel {
@@ -188,6 +189,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
public refreshDatabaseBackupPage!: boolean;
public _databaseSelection!: azdata.DeclarativeTableCellValue[][];
+ public retryMigration!: boolean;
public resumeAssessment!: boolean;
public savedInfo!: SavedInfo;
public closedPage!: number;
@@ -293,7 +295,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
private async generateAssessmentTelemetry(): Promise {
try {
- let serverIssues = this._assessmentResults.issues.map(i => {
+ let serverIssues = this._assessmentResults?.issues.map(i => {
return {
ruleId: i.ruleId,
count: i.impactedObjects.length
@@ -337,10 +339,10 @@ export class MigrationStateModel implements Model, vscode.Disposable {
'serverErrors': JSON.stringify(serverErrors),
},
{
- 'issuesCount': this._assessmentResults.issues.length,
- 'warningsCount': this._assessmentResults.databaseAssessments.reduce((count, d) => count + d.issues.length, 0),
+ 'issuesCount': this._assessmentResults?.issues.length,
+ 'warningsCount': this._assessmentResults?.databaseAssessments.reduce((count, d) => count + d.issues.length, 0),
'durationInMilliseconds': endTime.getTime() - startTime.getTime(),
- 'databaseCount': this._assessmentResults.databaseAssessments.length,
+ 'databaseCount': this._assessmentResults?.databaseAssessments.length,
'serverHostCpuCount': this._assessmentApiResponse?.assessmentResult?.cpuCoreCount,
'serverHostPhysicalMemoryInBytes': this._assessmentApiResponse?.assessmentResult?.physicalServerMemory,
'serverDatabases': this._assessmentApiResponse?.assessmentResult?.numberOfUserDatabases,
@@ -626,12 +628,12 @@ export class MigrationStateModel implements Model, vscode.Disposable {
public async getManagedInstanceValues(subscription: azureResource.AzureResourceSubscription, location: azureResource.AzureLocation, resourceGroup: azureResource.AzureResourceResourceGroup): Promise {
let managedInstanceValues: azdata.CategoryValue[] = [];
- if (!this._azureAccount) {
+ if (!this._azureAccount || !subscription) {
return managedInstanceValues;
}
try {
this._targetManagedInstances = (await getAvailableManagedInstanceProducts(this._azureAccount, subscription)).filter((mi) => {
- if (mi.location.toLowerCase() === location.name.toLowerCase() && mi.resourceGroup?.toLowerCase() === resourceGroup?.name.toLowerCase()) {
+ if (mi.location.toLowerCase() === location?.name.toLowerCase() && mi.resourceGroup?.toLowerCase() === resourceGroup?.name.toLowerCase()) {
return true;
}
return false;
@@ -678,7 +680,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
try {
if (this._azureAccount && subscription && resourceGroup) {
this._targetSqlVirtualMachines = (await getAvailableSqlVMs(this._azureAccount, subscription, resourceGroup)).filter((virtualMachine) => {
- if (virtualMachine.location === location.name) {
+ if (virtualMachine?.location?.toLowerCase() === location?.name?.toLowerCase()) {
if (virtualMachine.properties.sqlImageOffer) {
return virtualMachine.properties.sqlImageOffer.toLowerCase().includes('-ws'); //filtering out all non windows sql vms.
}
@@ -996,6 +998,8 @@ export class MigrationStateModel implements Model, vscode.Disposable {
let wizardEntryPoint = WizardEntryPoint.Default;
if (this.resumeAssessment) {
wizardEntryPoint = WizardEntryPoint.SaveAndClose;
+ } else if (this.retryMigration) {
+ wizardEntryPoint = WizardEntryPoint.RetryMigration;
}
if (response.status === 201 || response.status === 200) {
sendSqlMigrationActionEvent(
diff --git a/extensions/sql-migration/src/wizard/accountsSelectionPage.ts b/extensions/sql-migration/src/wizard/accountsSelectionPage.ts
index 6bd4a92e7f..e1d98d7c24 100644
--- a/extensions/sql-migration/src/wizard/accountsSelectionPage.ts
+++ b/extensions/sql-migration/src/wizard/accountsSelectionPage.ts
@@ -6,7 +6,7 @@
import * as azdata from 'azdata';
import * as vscode from 'vscode';
import { MigrationWizardPage } from '../models/migrationWizardPage';
-import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
+import { MigrationStateModel, Page, StateChangeEvent } from '../models/stateMachine';
import * as constants from '../constants/strings';
import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
import { deepClone, findDropDownItemIndex, selectDropDownIndex } from '../api/utils';
@@ -111,9 +111,9 @@ export class AccountsSelectionPage extends MigrationWizardPage {
await this._accountTenantFlexContainer.updateCssStyles({
'display': 'none'
});
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= 0) {
+ if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.AzureAccount)) {
(this._azureAccountsDropdown.values)?.forEach((account, index) => {
- if (account.name === this.migrationStateModel.savedInfo.azureAccount?.displayInfo.userId) {
+ if (account.name.toLowerCase() === this.migrationStateModel.savedInfo.azureAccount?.displayInfo.userId.toLowerCase()) {
selectDropDownIndex(this._azureAccountsDropdown, index);
}
});
diff --git a/extensions/sql-migration/src/wizard/databaseBackupPage.ts b/extensions/sql-migration/src/wizard/databaseBackupPage.ts
index 291d54094b..258b9fcc08 100644
--- a/extensions/sql-migration/src/wizard/databaseBackupPage.ts
+++ b/extensions/sql-migration/src/wizard/databaseBackupPage.ts
@@ -283,7 +283,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
}
return true;
}).component();
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.MigrationMode) {
+ if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.MigrationMode)) {
this._networkSharePath.value = this.migrationStateModel.savedInfo.networkShare?.networkShareLocation;
}
this._disposables.push(this._networkSharePath.onTextChanged(async (value) => {
@@ -331,7 +331,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
}
return true;
}).component();
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup) {
+ if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup)) {
this._windowsUserAccountText.value = this.migrationStateModel.savedInfo.networkShare?.windowsUser;
}
this._disposables.push(this._windowsUserAccountText.onTextChanged((value) => {
@@ -458,7 +458,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
return flexContainer;
}
-
private createTargetDatabaseContainer(): azdata.FlexContainer {
const headerCssStyles: azdata.CssStyles = {
...styles.LABEL_CSS,
@@ -755,253 +754,265 @@ export class DatabaseBackupPage extends MigrationWizardPage {
return container;
}
-
public async onPageEnter(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise {
if (this.migrationStateModel.refreshDatabaseBackupPage) {
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup) {
- this.migrationStateModel._migrationDbs = this.migrationStateModel.savedInfo.databaseList;
- }
- const isOfflineMigration = this.migrationStateModel._databaseBackup?.migrationMode === MigrationMode.OFFLINE;
- const lastBackupFileColumnIndex = this._blobContainerTargetDatabaseNamesTable.columns.length - 1;
- this._blobContainerTargetDatabaseNamesTable.columns[lastBackupFileColumnIndex].hidden = !isOfflineMigration;
- this._blobContainerTargetDatabaseNamesTable.columns.forEach(column => {
- column.width = isOfflineMigration ? WIZARD_TABLE_COLUMN_WIDTH_SMALL : WIZARD_TABLE_COLUMN_WIDTH;
- });
+ try {
+ if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup)) {
+ this.migrationStateModel._migrationDbs = this.migrationStateModel.savedInfo.databaseList;
+ }
+ const isOfflineMigration = this.migrationStateModel._databaseBackup?.migrationMode === MigrationMode.OFFLINE;
+ const lastBackupFileColumnIndex = this._blobContainerTargetDatabaseNamesTable.columns.length - 1;
+ this._blobContainerTargetDatabaseNamesTable.columns[lastBackupFileColumnIndex].hidden = !isOfflineMigration;
+ this._blobContainerTargetDatabaseNamesTable.columns.forEach(column => {
+ column.width = isOfflineMigration ? WIZARD_TABLE_COLUMN_WIDTH_SMALL : WIZARD_TABLE_COLUMN_WIDTH;
+ });
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.MigrationMode) {
- if (this.migrationStateModel.savedInfo.networkContainerType === NetworkContainerType.NETWORK_SHARE) {
- this._networkShareButton.checked = true;
+ if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.MigrationMode)) {
+ if (this.migrationStateModel.savedInfo.networkContainerType === NetworkContainerType.NETWORK_SHARE) {
+ this._networkShareButton.checked = true;
+ } else {
+ this._networkShareButton.checked = false;
+ this._networkTableContainer.display = 'none';
+ await this._networkShareContainer.updateCssStyles({ 'display': 'none' });
+ }
} else {
this._networkShareButton.checked = false;
this._networkTableContainer.display = 'none';
await this._networkShareContainer.updateCssStyles({ 'display': 'none' });
}
- } else {
- this._networkShareButton.checked = false;
- this._networkTableContainer.display = 'none';
- await this._networkShareContainer.updateCssStyles({ 'display': 'none' });
- }
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.MigrationMode) {
- if (this.migrationStateModel.savedInfo.networkContainerType === NetworkContainerType.BLOB_CONTAINER) {
- this._blobContainerButton.checked = true;
+ if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.MigrationMode)) {
+ if (this.migrationStateModel.savedInfo.networkContainerType === NetworkContainerType.BLOB_CONTAINER) {
+ this._blobContainerButton.checked = true;
+ } else {
+ this._blobContainerButton.checked = false;
+ this._blobTableContainer.display = 'none';
+ await this._blobContainer.updateCssStyles({ 'display': 'none' });
+ }
} else {
this._blobContainerButton.checked = false;
this._blobTableContainer.display = 'none';
await this._blobContainer.updateCssStyles({ 'display': 'none' });
}
- } else {
- this._blobContainerButton.checked = false;
- this._blobTableContainer.display = 'none';
- await this._blobContainer.updateCssStyles({ 'display': 'none' });
- }
- await this._targetDatabaseContainer.updateCssStyles({ 'display': 'none' });
- await this._networkShareStorageAccountDetails.updateCssStyles({ 'display': 'none' });
- const connectionProfile = await this.migrationStateModel.getSourceConnectionProfile();
- const queryProvider = azdata.dataprotocol.getProvider((await this.migrationStateModel.getSourceConnectionProfile()).providerId, azdata.DataProviderType.QueryProvider);
- const query = 'select SUSER_NAME()';
- const results = await queryProvider.runQueryAndReturn(await (azdata.connection.getUriForConnection(this.migrationStateModel.sourceConnectionId)), query);
- const username = results.rows[0][0].displayValue;
- this.migrationStateModel._authenticationType = connectionProfile.authenticationType === 'SqlLogin' ? MigrationSourceAuthenticationType.Sql : connectionProfile.authenticationType === 'Integrated' ? MigrationSourceAuthenticationType.Integrated : undefined!;
- this._sourceHelpText.value = constants.SQL_SOURCE_DETAILS(this.migrationStateModel._authenticationType, connectionProfile.serverName);
- this._sqlSourceUsernameInput.value = username;
- this._sqlSourcePassword.value = (await azdata.connection.getCredentials(this.migrationStateModel.sourceConnectionId)).password;
+ await this._targetDatabaseContainer.updateCssStyles({ 'display': 'none' });
+ await this._networkShareStorageAccountDetails.updateCssStyles({ 'display': 'none' });
+ const connectionProfile = await this.migrationStateModel.getSourceConnectionProfile();
+ const queryProvider = azdata.dataprotocol.getProvider((await this.migrationStateModel.getSourceConnectionProfile()).providerId, azdata.DataProviderType.QueryProvider);
+ const query = 'select SUSER_NAME()';
+ const results = await queryProvider.runQueryAndReturn(await (azdata.connection.getUriForConnection(this.migrationStateModel.sourceConnectionId)), query);
+ const username = results.rows[0][0].displayValue;
+ this.migrationStateModel._authenticationType = connectionProfile.authenticationType === 'SqlLogin' ? MigrationSourceAuthenticationType.Sql : connectionProfile.authenticationType === 'Integrated' ? MigrationSourceAuthenticationType.Integrated : undefined!;
+ this._sourceHelpText.value = constants.SQL_SOURCE_DETAILS(this.migrationStateModel._authenticationType, connectionProfile.serverName);
+ this._sqlSourceUsernameInput.value = username;
+ this._sqlSourcePassword.value = (await azdata.connection.getCredentials(this.migrationStateModel.sourceConnectionId)).password;
- this._networkShareTargetDatabaseNames = [];
- this._blobContainerTargetDatabaseNames = [];
- this._blobContainerResourceGroupDropdowns = [];
- this._blobContainerStorageAccountDropdowns = [];
- this._blobContainerDropdowns = [];
- this._blobContainerLastBackupFileDropdowns = [];
+ this._networkShareTargetDatabaseNames = [];
+ this._blobContainerTargetDatabaseNames = [];
+ this._blobContainerResourceGroupDropdowns = [];
+ this._blobContainerStorageAccountDropdowns = [];
+ this._blobContainerDropdowns = [];
+ this._blobContainerLastBackupFileDropdowns = [];
- if (this.migrationStateModel._targetType === MigrationTargetType.SQLMI) {
- this._existingDatabases = await this.migrationStateModel.getManagedDatabases();
- }
- this.migrationStateModel._targetDatabaseNames = [];
- this.migrationStateModel._databaseBackup.blobs = [];
- this.migrationStateModel._migrationDbs.forEach((db, index) => {
- this.migrationStateModel._targetDatabaseNames.push('');
- this.migrationStateModel._databaseBackup.blobs.push({});
- const targetDatabaseInput = this._view.modelBuilder.inputBox().withProps({
- required: true,
- value: db,
- width: WIZARD_TABLE_COLUMN_WIDTH
- }).withValidation(c => {
- if (this._networkShareTargetDatabaseNames.filter(t => t.value === c.value).length > 1) { //Making sure no databases have duplicate values.
- c.validationErrorMessage = constants.DUPLICATE_NAME_ERROR;
- return false;
- }
- if (this.migrationStateModel._targetType === MigrationTargetType.SQLMI && this._existingDatabases.includes(c.value!)) { // Making sure if database with same name is not present on the target Azure SQL
- c.validationErrorMessage = constants.DATABASE_ALREADY_EXISTS_MI(c.value!, this.migrationStateModel._targetServerInstance.name);
- return false;
- }
- if (c.value!.length < 1 || c.value!.length > 128 || !/[^<>*%&:\\\/?]/.test(c.value!)) {
- c.validationErrorMessage = constants.INVALID_TARGET_NAME_ERROR;
- return false;
- }
- return true;
- }).component();
- this._disposables.push(targetDatabaseInput.onTextChanged(async (value) => {
- this.migrationStateModel._targetDatabaseNames[index] = value.trim();
- await this.validateFields();
- }));
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup) {
- targetDatabaseInput.value = this.migrationStateModel.savedInfo.targetDatabaseNames[index];
- } else {
- targetDatabaseInput.value = this.migrationStateModel._targetDatabaseNames[index];
+ if (this.migrationStateModel._targetType === MigrationTargetType.SQLMI) {
+ this._existingDatabases = await this.migrationStateModel.getManagedDatabases();
}
- this._networkShareTargetDatabaseNames.push(targetDatabaseInput);
-
- const blobTargetDatabaseInput = this._view.modelBuilder.inputBox().withProps({
- required: true,
- value: db,
- }).withValidation(c => {
- if (this._blobContainerTargetDatabaseNames.filter(t => t.value === c.value).length > 1) { //Making sure no databases have duplicate values.
- c.validationErrorMessage = constants.DUPLICATE_NAME_ERROR;
- return false;
- }
- if (this.migrationStateModel._targetType === MigrationTargetType.SQLMI && this._existingDatabases.includes(c.value!)) { // Making sure if database with same name is not present on the target Azure SQL
- c.validationErrorMessage = constants.DATABASE_ALREADY_EXISTS_MI(c.value!, this.migrationStateModel._targetServerInstance.name);
- return false;
- }
- if (c.value!.length < 1 || c.value!.length > 128 || !/[^<>*%&:\\\/?]/.test(c.value!)) {
- c.validationErrorMessage = constants.INVALID_TARGET_NAME_ERROR;
- return false;
- }
- return true;
- }).component();
- this._disposables.push(blobTargetDatabaseInput.onTextChanged((value) => {
- this.migrationStateModel._targetDatabaseNames[index] = value.trim();
- }));
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup) {
- blobTargetDatabaseInput.value = this.migrationStateModel.savedInfo.targetDatabaseNames[index];
- } else {
- targetDatabaseInput.value = this.migrationStateModel._targetDatabaseNames[index];
- }
- this._blobContainerTargetDatabaseNames.push(blobTargetDatabaseInput);
-
- const blobContainerResourceDropdown = this._view.modelBuilder.dropDown().withProps({
- ariaLabel: constants.BLOB_CONTAINER_RESOURCE_GROUP,
- editable: true,
- fireOnTextChange: true,
- required: true,
- }).component();
-
- const blobContainerStorageAccountDropdown = this._view.modelBuilder.dropDown().withProps({
- ariaLabel: constants.BLOB_CONTAINER_STORAGE_ACCOUNT,
- editable: true,
- fireOnTextChange: true,
- required: true,
- enabled: false,
- }).component();
-
- const blobContainerDropdown = this._view.modelBuilder.dropDown().withProps({
- ariaLabel: constants.BLOB_CONTAINER,
- editable: true,
- fireOnTextChange: true,
- required: true,
- enabled: false,
- }).component();
-
- const blobContainerLastBackupFileDropdown = this._view.modelBuilder.dropDown().withProps({
- ariaLabel: constants.BLOB_CONTAINER_LAST_BACKUP_FILE,
- editable: true,
- fireOnTextChange: true,
- required: true,
- enabled: false,
- }).component();
-
- this._disposables.push(blobContainerResourceDropdown.onValueChanged(async (value) => {
- const selectedIndex = findDropDownItemIndex(blobContainerResourceDropdown, value);
- if (selectedIndex > -1 && !blobResourceGroupErrorStrings.includes(value)) {
- this.migrationStateModel._databaseBackup.blobs[index].resourceGroup = this.migrationStateModel.getAzureResourceGroup(selectedIndex);
- await this.loadBlobStorageDropdown(index);
- await blobContainerStorageAccountDropdown.updateProperties({ enabled: true });
- } else {
- await this.disableBlobTableDropdowns(index, constants.RESOURCE_GROUP);
- }
- }));
- this._blobContainerResourceGroupDropdowns.push(blobContainerResourceDropdown);
-
- this._disposables.push(blobContainerStorageAccountDropdown.onValueChanged(async (value) => {
- const selectedIndex = findDropDownItemIndex(blobContainerStorageAccountDropdown, value);
- if (selectedIndex > -1 && !blobStorageAccountErrorStrings.includes(value)) {
- this.migrationStateModel._databaseBackup.blobs[index].storageAccount = this.migrationStateModel.getStorageAccount(selectedIndex);
- await this.loadBlobContainerDropdown(index);
- await blobContainerDropdown.updateProperties({ enabled: true });
- } else {
- await this.disableBlobTableDropdowns(index, constants.STORAGE_ACCOUNT);
- }
- }));
- this._blobContainerStorageAccountDropdowns.push(blobContainerStorageAccountDropdown);
-
- this._disposables.push(blobContainerDropdown.onValueChanged(async (value) => {
- const selectedIndex = findDropDownItemIndex(blobContainerDropdown, value);
- if (selectedIndex > -1 && !blobContainerErrorStrings.includes(value)) {
- this.migrationStateModel._databaseBackup.blobs[index].blobContainer = this.migrationStateModel.getBlobContainer(selectedIndex);
- if (this.migrationStateModel._databaseBackup.migrationMode === MigrationMode.OFFLINE) {
- await this.loadBlobLastBackupFileDropdown(index);
- await blobContainerLastBackupFileDropdown.updateProperties({ enabled: true });
+ this.migrationStateModel._targetDatabaseNames = [];
+ this.migrationStateModel._databaseBackup.blobs = [];
+ this.migrationStateModel._migrationDbs.forEach((db, index) => {
+ this.migrationStateModel._targetDatabaseNames.push('');
+ this.migrationStateModel._databaseBackup.blobs.push({});
+ const targetDatabaseInput = this._view.modelBuilder.inputBox().withProps({
+ required: true,
+ value: db,
+ width: WIZARD_TABLE_COLUMN_WIDTH
+ }).withValidation(c => {
+ if (this._networkShareTargetDatabaseNames.filter(t => t.value === c.value).length > 1) { //Making sure no databases have duplicate values.
+ c.validationErrorMessage = constants.DUPLICATE_NAME_ERROR;
+ return false;
}
+ if (this.migrationStateModel._targetType === MigrationTargetType.SQLMI && this._existingDatabases.includes(c.value!)) { // Making sure if database with same name is not present on the target Azure SQL
+ c.validationErrorMessage = constants.DATABASE_ALREADY_EXISTS_MI(c.value!, this.migrationStateModel._targetServerInstance.name);
+ return false;
+ }
+ if (c.value!.length < 1 || c.value!.length > 128 || !/[^<>*%&:\\\/?]/.test(c.value!)) {
+ c.validationErrorMessage = constants.INVALID_TARGET_NAME_ERROR;
+ return false;
+ }
+ return true;
+ }).component();
+ this._disposables.push(targetDatabaseInput.onTextChanged(async (value) => {
+ this.migrationStateModel._targetDatabaseNames[index] = value.trim();
+ await this.validateFields();
+ }));
+ if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup)) {
+ targetDatabaseInput.value = this.migrationStateModel.savedInfo.targetDatabaseNames[index];
} else {
- await this.disableBlobTableDropdowns(index, constants.BLOB_CONTAINER);
+ targetDatabaseInput.value = this.migrationStateModel._targetDatabaseNames[index];
}
- }));
- this._blobContainerDropdowns.push(blobContainerDropdown);
+ this._networkShareTargetDatabaseNames.push(targetDatabaseInput);
- if (this.migrationStateModel._databaseBackup.migrationMode === MigrationMode.OFFLINE) {
- this._disposables.push(blobContainerLastBackupFileDropdown.onValueChanged(value => {
- const selectedIndex = findDropDownItemIndex(blobContainerLastBackupFileDropdown, value);
- if (selectedIndex > -1 && !blobFileErrorStrings.includes(value)) {
- this.migrationStateModel._databaseBackup.blobs[index].lastBackupFile = this.migrationStateModel.getBlobLastBackupFileName(selectedIndex);
+ const blobTargetDatabaseInput = this._view.modelBuilder.inputBox().withProps({
+ required: true,
+ value: db,
+ }).withValidation(c => {
+ if (this._blobContainerTargetDatabaseNames.filter(t => t.value === c.value).length > 1) { //Making sure no databases have duplicate values.
+ c.validationErrorMessage = constants.DUPLICATE_NAME_ERROR;
+ return false;
+ }
+ if (this.migrationStateModel._targetType === MigrationTargetType.SQLMI && this._existingDatabases.includes(c.value!)) { // Making sure if database with same name is not present on the target Azure SQL
+ c.validationErrorMessage = constants.DATABASE_ALREADY_EXISTS_MI(c.value!, this.migrationStateModel._targetServerInstance.name);
+ return false;
+ }
+ if (c.value!.length < 1 || c.value!.length > 128 || !/[^<>*%&:\\\/?]/.test(c.value!)) {
+ c.validationErrorMessage = constants.INVALID_TARGET_NAME_ERROR;
+ return false;
+ }
+ return true;
+ }).component();
+ this._disposables.push(blobTargetDatabaseInput.onTextChanged((value) => {
+ this.migrationStateModel._targetDatabaseNames[index] = value.trim();
+ }));
+ if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup)) {
+ blobTargetDatabaseInput.value = this.migrationStateModel.savedInfo.targetDatabaseNames[index];
+ } else {
+ targetDatabaseInput.value = this.migrationStateModel._targetDatabaseNames[index];
+ }
+ this._blobContainerTargetDatabaseNames.push(blobTargetDatabaseInput);
+
+ const blobContainerResourceDropdown = this._view.modelBuilder.dropDown().withProps({
+ ariaLabel: constants.BLOB_CONTAINER_RESOURCE_GROUP,
+ editable: true,
+ fireOnTextChange: true,
+ required: true,
+ }).component();
+
+ const blobContainerStorageAccountDropdown = this._view.modelBuilder.dropDown().withProps({
+ ariaLabel: constants.BLOB_CONTAINER_STORAGE_ACCOUNT,
+ editable: true,
+ fireOnTextChange: true,
+ required: true,
+ enabled: false,
+ }).component();
+
+ const blobContainerDropdown = this._view.modelBuilder.dropDown().withProps({
+ ariaLabel: constants.BLOB_CONTAINER,
+ editable: true,
+ fireOnTextChange: true,
+ required: true,
+ enabled: false,
+ }).component();
+
+ const blobContainerLastBackupFileDropdown = this._view.modelBuilder.dropDown().withProps({
+ ariaLabel: constants.BLOB_CONTAINER_LAST_BACKUP_FILE,
+ editable: true,
+ fireOnTextChange: true,
+ required: true,
+ enabled: false,
+ }).component();
+
+ this._disposables.push(blobContainerResourceDropdown.onValueChanged(async (value) => {
+ const selectedIndex = findDropDownItemIndex(blobContainerResourceDropdown, value);
+ if (selectedIndex > -1 && !blobResourceGroupErrorStrings.includes(value)) {
+ this.migrationStateModel._databaseBackup.blobs[index].resourceGroup = this.migrationStateModel.getAzureResourceGroup(selectedIndex);
+ await this.loadBlobStorageDropdown(index);
+ await blobContainerStorageAccountDropdown.updateProperties({ enabled: true });
+ } else {
+ await this.disableBlobTableDropdowns(index, constants.RESOURCE_GROUP);
}
}));
- this._blobContainerLastBackupFileDropdowns.push(blobContainerLastBackupFileDropdown);
+ this._blobContainerResourceGroupDropdowns.push(blobContainerResourceDropdown);
+
+ this._disposables.push(blobContainerStorageAccountDropdown.onValueChanged(async (value) => {
+ const selectedIndex = findDropDownItemIndex(blobContainerStorageAccountDropdown, value);
+ if (selectedIndex > -1 && !blobStorageAccountErrorStrings.includes(value)) {
+ this.migrationStateModel._databaseBackup.blobs[index].storageAccount = this.migrationStateModel.getStorageAccount(selectedIndex);
+ await this.loadBlobContainerDropdown(index);
+ await blobContainerDropdown.updateProperties({ enabled: true });
+ } else {
+ await this.disableBlobTableDropdowns(index, constants.STORAGE_ACCOUNT);
+ }
+ }));
+ this._blobContainerStorageAccountDropdowns.push(blobContainerStorageAccountDropdown);
+
+ this._disposables.push(blobContainerDropdown.onValueChanged(async (value) => {
+ const selectedIndex = findDropDownItemIndex(blobContainerDropdown, value);
+ if (selectedIndex > -1 && !blobContainerErrorStrings.includes(value)) {
+ this.migrationStateModel._databaseBackup.blobs[index].blobContainer = this.migrationStateModel.getBlobContainer(selectedIndex);
+ if (this.migrationStateModel._databaseBackup.migrationMode === MigrationMode.OFFLINE) {
+ await this.loadBlobLastBackupFileDropdown(index);
+ await blobContainerLastBackupFileDropdown.updateProperties({ enabled: true });
+ }
+ } else {
+ await this.disableBlobTableDropdowns(index, constants.BLOB_CONTAINER);
+ }
+ }));
+ this._blobContainerDropdowns.push(blobContainerDropdown);
+
+ if (this.migrationStateModel._databaseBackup.migrationMode === MigrationMode.OFFLINE) {
+ this._disposables.push(blobContainerLastBackupFileDropdown.onValueChanged(value => {
+ const selectedIndex = findDropDownItemIndex(blobContainerLastBackupFileDropdown, value);
+ if (selectedIndex > -1 && !blobFileErrorStrings.includes(value)) {
+ this.migrationStateModel._databaseBackup.blobs[index].lastBackupFile = this.migrationStateModel.getBlobLastBackupFileName(selectedIndex);
+ }
+ }));
+ this._blobContainerLastBackupFileDropdowns.push(blobContainerLastBackupFileDropdown);
+ }
+ });
+
+
+ let data: azdata.DeclarativeTableCellValue[][] = [];
+ this.migrationStateModel._migrationDbs.forEach((db, index) => {
+ const targetRow: azdata.DeclarativeTableCellValue[] = [];
+ targetRow.push({
+ value: db
+ });
+ targetRow.push({
+ value: this._networkShareTargetDatabaseNames[index]
+ });
+ data.push(targetRow);
+ });
+ this._networkShareTargetDatabaseNamesTable.dataValues = data;
+
+ data = [];
+ this.migrationStateModel._migrationDbs.forEach((db, index) => {
+ const targetRow: azdata.DeclarativeTableCellValue[] = [];
+ targetRow.push({
+ value: db
+ });
+ targetRow.push({
+ value: this._blobContainerTargetDatabaseNames[index]
+ });
+ targetRow.push({
+ value: this._blobContainerResourceGroupDropdowns[index]
+ });
+ targetRow.push({
+ value: this._blobContainerStorageAccountDropdowns[index]
+ });
+ targetRow.push({
+ value: this._blobContainerDropdowns[index]
+ });
+ targetRow.push({
+ value: this._blobContainerLastBackupFileDropdowns[index]
+ });
+ data.push(targetRow);
+ });
+ await this._blobContainerTargetDatabaseNamesTable.setDataValues(data);
+
+ await this.getSubscriptionValues();
+ this.migrationStateModel.refreshDatabaseBackupPage = false;
+ } catch (error) {
+ console.log(error);
+ let errorText = error?.message;
+ if (errorText === constants.INVALID_OWNER_URI) {
+ errorText = constants.DATABASE_BACKUP_PAGE_LOAD_ERROR;
}
- });
-
-
- let data: azdata.DeclarativeTableCellValue[][] = [];
- this.migrationStateModel._migrationDbs.forEach((db, index) => {
- const targetRow: azdata.DeclarativeTableCellValue[] = [];
- targetRow.push({
- value: db
- });
- targetRow.push({
- value: this._networkShareTargetDatabaseNames[index]
- });
- data.push(targetRow);
- });
- this._networkShareTargetDatabaseNamesTable.dataValues = data;
-
- data = [];
- this.migrationStateModel._migrationDbs.forEach((db, index) => {
- const targetRow: azdata.DeclarativeTableCellValue[] = [];
- targetRow.push({
- value: db
- });
- targetRow.push({
- value: this._blobContainerTargetDatabaseNames[index]
- });
- targetRow.push({
- value: this._blobContainerResourceGroupDropdowns[index]
- });
- targetRow.push({
- value: this._blobContainerStorageAccountDropdowns[index]
- });
- targetRow.push({
- value: this._blobContainerDropdowns[index]
- });
- targetRow.push({
- value: this._blobContainerLastBackupFileDropdowns[index]
- });
- data.push(targetRow);
- });
- await this._blobContainerTargetDatabaseNamesTable.setDataValues(data);
-
- await this.getSubscriptionValues();
- this.migrationStateModel.refreshDatabaseBackupPage = false;
+ this.wizard.message = {
+ text: errorText,
+ description: error?.stack,
+ level: azdata.window.MessageLevel.Error
+ };
+ }
}
this.wizard.registerNavigationValidator((pageChangeInfo) => {
@@ -1131,7 +1142,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
this._blobTableContainer.display = (containerType === NetworkContainerType.BLOB_CONTAINER) ? 'inline' : 'none';
//Preserving the database Names between the 2 tables.
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup) {
+ if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup)) {
this.migrationStateModel._targetDatabaseNames = this.migrationStateModel.savedInfo.targetDatabaseNames;
}
@@ -1150,7 +1161,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
await this.validateFields();
}
-
private async validateFields(): Promise {
await this._sqlSourceUsernameInput.validate();
await this._sqlSourcePassword.validate();
@@ -1175,7 +1185,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
}
private async getSubscriptionValues(): Promise {
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup) {
+ if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup)) {
this.migrationStateModel._targetSubscription = this.migrationStateModel.savedInfo.targetSubscription;
this.migrationStateModel._targetServerInstance = this.migrationStateModel.savedInfo.targetServerInstance;
}
@@ -1196,7 +1206,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
this._networkShareStorageAccountResourceGroupDropdown.loading = true;
try {
this._networkShareStorageAccountResourceGroupDropdown.values = await this.migrationStateModel.getAzureResourceGroupDropdownValues(this.migrationStateModel._databaseBackup.subscription);
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup && this._networkShareStorageAccountResourceGroupDropdown.values) {
+ if (this.hasSavedInfo(NetworkContainerType.NETWORK_SHARE, this._networkShareStorageAccountResourceGroupDropdown.values)) {
this._networkShareStorageAccountResourceGroupDropdown.values.forEach((resource, index) => {
if ((resource).name.toLowerCase() === this.migrationStateModel.savedInfo?.networkShare?.resourceGroup?.id?.toLowerCase()) {
selectDropDownIndex(this._networkShareStorageAccountResourceGroupDropdown, index);
@@ -1231,7 +1241,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
const resourceGroupValues = await this.migrationStateModel.getAzureResourceGroupDropdownValues(this.migrationStateModel._databaseBackup.subscription);
this._blobContainerResourceGroupDropdowns.forEach((dropDown, index) => {
dropDown.values = resourceGroupValues;
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup && dropDown.values) {
+ if (this.hasSavedInfo(NetworkContainerType.BLOB_CONTAINER, dropDown.values)) {
dropDown.values.forEach((resource, resourceIndex) => {
if ((resource).name.toLowerCase() === this.migrationStateModel.savedInfo?.blobs[index]?.resourceGroup?.id?.toLowerCase()) {
selectDropDownIndex(dropDown, resourceIndex);
@@ -1251,8 +1261,8 @@ export class DatabaseBackupPage extends MigrationWizardPage {
private async loadBlobStorageDropdown(index: number): Promise {
this._blobContainerStorageAccountDropdowns[index].loading = true;
try {
- this._blobContainerStorageAccountDropdowns[index].values = await this.migrationStateModel.getStorageAccountValues(this.migrationStateModel._databaseBackup.subscription, this.migrationStateModel._databaseBackup.blobs[index].resourceGroup);
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup && this._blobContainerStorageAccountDropdowns[index].values && this.migrationStateModel.savedInfo.blobs[index].storageAccount) {
+ this._blobContainerStorageAccountDropdowns[index].values = await this.migrationStateModel.getStorageAccountValues(this.migrationStateModel._databaseBackup.subscription, this.migrationStateModel._databaseBackup.blobs[index]?.resourceGroup);
+ if (this.hasSavedInfo(NetworkContainerType.BLOB_CONTAINER, this._blobContainerStorageAccountDropdowns[index].values && this.migrationStateModel.savedInfo.blobs[index]?.storageAccount)) {
this._blobContainerStorageAccountDropdowns[index].values!.forEach((resource, resourceIndex) => {
if ((resource).name.toLowerCase() === this.migrationStateModel.savedInfo?.blobs[index]?.storageAccount?.id?.toLowerCase()) {
selectDropDownIndex(this._blobContainerStorageAccountDropdowns[index], resourceIndex);
@@ -1271,9 +1281,9 @@ export class DatabaseBackupPage extends MigrationWizardPage {
private async loadBlobContainerDropdown(index: number): Promise {
this._blobContainerDropdowns[index].loading = true;
try {
- const blobContainerValues = await this.migrationStateModel.getBlobContainerValues(this.migrationStateModel._databaseBackup.subscription, this.migrationStateModel._databaseBackup.blobs[index].storageAccount);
+ const blobContainerValues = await this.migrationStateModel.getBlobContainerValues(this.migrationStateModel._databaseBackup.subscription, this.migrationStateModel._databaseBackup.blobs[index]?.storageAccount);
this._blobContainerDropdowns[index].values = blobContainerValues;
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup && this._blobContainerDropdowns[index].values && this.migrationStateModel.savedInfo.blobs[index].blobContainer) {
+ if (this.hasSavedInfo(NetworkContainerType.BLOB_CONTAINER, this._blobContainerDropdowns[index].values && this.migrationStateModel.savedInfo.blobs[index]?.blobContainer)) {
this._blobContainerDropdowns[index].values!.forEach((resource, resourceIndex) => {
if ((resource).name.toLowerCase() === this.migrationStateModel.savedInfo?.blobs[index]?.blobContainer?.id?.toLowerCase()) {
selectDropDownIndex(this._blobContainerDropdowns[index], resourceIndex);
@@ -1292,9 +1302,9 @@ export class DatabaseBackupPage extends MigrationWizardPage {
private async loadBlobLastBackupFileDropdown(index: number): Promise {
this._blobContainerLastBackupFileDropdowns[index].loading = true;
try {
- const blobLastBackupFileValues = await this.migrationStateModel.getBlobLastBackupFileNameValues(this.migrationStateModel._databaseBackup.subscription, this.migrationStateModel._databaseBackup.blobs[index].storageAccount, this.migrationStateModel._databaseBackup.blobs[index].blobContainer);
+ const blobLastBackupFileValues = await this.migrationStateModel.getBlobLastBackupFileNameValues(this.migrationStateModel._databaseBackup.subscription, this.migrationStateModel._databaseBackup.blobs[index]?.storageAccount, this.migrationStateModel._databaseBackup.blobs[index]?.blobContainer);
this._blobContainerLastBackupFileDropdowns[index].values = blobLastBackupFileValues;
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup && this._blobContainerLastBackupFileDropdowns[index].values && this.migrationStateModel.savedInfo.blobs[index].lastBackupFile) {
+ if (this.hasSavedInfo(NetworkContainerType.BLOB_CONTAINER, this._blobContainerLastBackupFileDropdowns[index].values && this.migrationStateModel.savedInfo.blobs[index]?.lastBackupFile)) {
this._blobContainerLastBackupFileDropdowns[index].values!.forEach((resource, resourceIndex) => {
if ((resource).name.toLowerCase() === this.migrationStateModel.savedInfo?.blobs[index]?.lastBackupFile!.toLowerCase()) {
selectDropDownIndex(this._blobContainerLastBackupFileDropdowns[index], resourceIndex);
@@ -1334,4 +1344,12 @@ export class DatabaseBackupPage extends MigrationWizardPage {
selectDropDownIndex(this._blobContainerStorageAccountDropdowns[rowIndex], 0);
await this._blobContainerStorageAccountDropdowns[rowIndex].updateProperties(dropdownProps);
}
+
+ private hasSavedInfo(networkContainerType: NetworkContainerType, values: any): boolean {
+ if (this.migrationStateModel._databaseBackup.networkContainerType === networkContainerType &&
+ (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup) && values)) {
+ return true;
+ }
+ return false;
+ }
}
diff --git a/extensions/sql-migration/src/wizard/databaseSelectorPage.ts b/extensions/sql-migration/src/wizard/databaseSelectorPage.ts
index b3f4b2f355..424225fd18 100644
--- a/extensions/sql-migration/src/wizard/databaseSelectorPage.ts
+++ b/extensions/sql-migration/src/wizard/databaseSelectorPage.ts
@@ -6,7 +6,7 @@
import * as azdata from 'azdata';
import * as vscode from 'vscode';
import { MigrationWizardPage } from '../models/migrationWizardPage';
-import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
+import { MigrationStateModel, Page, StateChangeEvent } from '../models/stateMachine';
import * as constants from '../constants/strings';
import { IconPath, IconPathHelper } from '../constants/iconPathHelper';
import { debounce } from '../api/utils';
@@ -275,17 +275,26 @@ export class DatabaseSelectorPage extends MigrationWizardPage {
}
).component();
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= 1) {
+ if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseSelector) {
await this._databaseSelectorTable.setDataValues(this.migrationStateModel.savedInfo.selectedDatabases);
} else {
+ if (this.migrationStateModel.retryMigration) {
+ const sourceDatabaseName = this.migrationStateModel.savedInfo.databaseList[0];
+ this._databaseTableValues.forEach((row, index) => {
+ const dbName = row[1].value as string;
+ if (dbName?.toLowerCase() === sourceDatabaseName?.toLowerCase()) {
+ row[0].value = true;
+ } else {
+ row[0].enabled = false;
+ }
+ });
+ }
await this._databaseSelectorTable.setDataValues(this._databaseTableValues);
+ await this.updateValuesOnSelection();
}
+
this._disposables.push(this._databaseSelectorTable.onDataChanged(async () => {
- await this._dbCount.updateProperties({
- 'value': constants.DATABASES_SELECTED(this.selectedDbs().length, this._databaseTableValues.length)
- });
- this.migrationStateModel._databaseAssessment = this.selectedDbs();
- this.migrationStateModel.databaseSelectorTableValues = this._databaseSelectorTable.dataValues;
+ await this.updateValuesOnSelection();
}));
const flex = view.modelBuilder.flexContainer().withLayout({
flexFlow: 'column',
@@ -314,6 +323,14 @@ export class DatabaseSelectorPage extends MigrationWizardPage {
return result;
}
+ private async updateValuesOnSelection() {
+ await this._dbCount.updateProperties({
+ 'value': constants.DATABASES_SELECTED(this.selectedDbs().length, this._databaseTableValues.length)
+ });
+ this.migrationStateModel._databaseAssessment = this.selectedDbs();
+ this.migrationStateModel.databaseSelectorTableValues = this._databaseSelectorTable.dataValues;
+ }
+
// undo when bug #16445 is fixed
private createIconTextCell(icon: IconPath, text: string): string {
return text;
diff --git a/extensions/sql-migration/src/wizard/integrationRuntimePage.ts b/extensions/sql-migration/src/wizard/integrationRuntimePage.ts
index 6c3b652928..5318e86f1c 100644
--- a/extensions/sql-migration/src/wizard/integrationRuntimePage.ts
+++ b/extensions/sql-migration/src/wizard/integrationRuntimePage.ts
@@ -86,7 +86,7 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
}
public async onPageEnter(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise {
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.IntegrationRuntime) {
+ if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.IntegrationRuntime)) {
this.migrationStateModel._targetSubscription = this.migrationStateModel.savedInfo.targetSubscription;
this.migrationStateModel._targetServerInstance = this.migrationStateModel.savedInfo.targetServerInstance;
}
@@ -391,7 +391,7 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
this._resourceGroupDropdown.loading = true;
try {
this._resourceGroupDropdown.values = await this.migrationStateModel.getAzureResourceGroupDropdownValues(this.migrationStateModel._targetSubscription);
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.IntegrationRuntime && this._resourceGroupDropdown.values) {
+ if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.IntegrationRuntime && this._resourceGroupDropdown.values)) {
this._resourceGroupDropdown.values.forEach((resource, resourceIndex) => {
const resourceId = this.migrationStateModel.savedInfo?.migrationServiceId?.toLowerCase();
if (resourceId && (resource).name.toLowerCase() === getFullResourceGroupFromId(resourceId)) {
@@ -409,8 +409,7 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
try {
this._dmsDropdown.values = await this.migrationStateModel.getSqlMigrationServiceValues(this.migrationStateModel._targetSubscription, this.migrationStateModel._targetServerInstance, resourceGroupName);
const selectedSqlMigrationService = this._dmsDropdown.values.find(v => v.displayName.toLowerCase() === this.migrationStateModel._sqlMigrationService?.name?.toLowerCase());
-
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.IntegrationRuntime && this._dmsDropdown.values) {
+ if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.IntegrationRuntime && this._dmsDropdown.values)) {
this._dmsDropdown.values.forEach((resource, resourceIndex) => {
if ((resource).name.toLowerCase() === this.migrationStateModel.savedInfo?.migrationServiceId?.toLowerCase()) {
selectDropDownIndex(this._dmsDropdown, resourceIndex);
diff --git a/extensions/sql-migration/src/wizard/migrationModePage.ts b/extensions/sql-migration/src/wizard/migrationModePage.ts
index 0d46126476..fca2167ea8 100644
--- a/extensions/sql-migration/src/wizard/migrationModePage.ts
+++ b/extensions/sql-migration/src/wizard/migrationModePage.ts
@@ -6,7 +6,7 @@
import * as azdata from 'azdata';
import * as vscode from 'vscode';
import { MigrationWizardPage } from '../models/migrationWizardPage';
-import { MigrationMode, MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
+import { MigrationMode, MigrationStateModel, Page, StateChangeEvent } from '../models/stateMachine';
import * as constants from '../constants/strings';
import * as styles from '../constants/styles';
@@ -113,7 +113,7 @@ export class MigrationModePage extends MigrationWizardPage {
}
}).component();
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= 3) {
+ if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.MigrationMode)) {
if (this.migrationStateModel.savedInfo.migrationMode === MigrationMode.ONLINE) {
onlineButton.checked = true;
offlineButton.checked = false;
diff --git a/extensions/sql-migration/src/wizard/skuRecommendationPage.ts b/extensions/sql-migration/src/wizard/skuRecommendationPage.ts
index d615ef456c..56669964bd 100644
--- a/extensions/sql-migration/src/wizard/skuRecommendationPage.ts
+++ b/extensions/sql-migration/src/wizard/skuRecommendationPage.ts
@@ -197,6 +197,14 @@ export class SKURecommendationPage extends MigrationWizardPage {
}));
await this._view.initializeModel(this._rootContainer);
+
+ if (this.hasSavedInfo()) {
+ if (this.migrationStateModel.savedInfo.migrationTargetType === MigrationTargetType.SQLMI) {
+ this.migrationStateModel._miDbs = this.migrationStateModel.savedInfo.databaseList;
+ } else {
+ this.migrationStateModel._vmDbs = this.migrationStateModel.savedInfo.databaseList;
+ }
+ }
}
private createStatusComponent(view: azdata.ModelView): azdata.TextComponent {
@@ -300,7 +308,7 @@ export class SKURecommendationPage extends MigrationWizardPage {
}).component();
let serverName = '';
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.serverName) {
+ if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.serverName)) {
serverName = this.migrationStateModel.serverName;
} else {
serverName = (await this.migrationStateModel.getSourceConnectionProfile()).serverName;
@@ -505,7 +513,7 @@ export class SKURecommendationPage extends MigrationWizardPage {
this.migrationStateModel._migrationDbs = miDbs;
} else {
this._viewAssessmentsHelperText.value = constants.SKU_RECOMMENDATION_VIEW_ASSESSMENT_VM;
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.SKURecommendation) {
+ if ((this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.SKURecommendation)) {
this._databaseSelectedHelperText.value = constants.TOTAL_DATABASES_SELECTED(this.migrationStateModel.savedInfo.databaseList.length, this.migrationStateModel._databaseAssessment.length);
} else {
this._databaseSelectedHelperText.value = constants.TOTAL_DATABASES_SELECTED(vmDbs.length, this.migrationStateModel._databaseAssessment.length);
@@ -540,12 +548,12 @@ export class SKURecommendationPage extends MigrationWizardPage {
await this.migrationStateModel.getDatabaseAssessments(MigrationTargetType.SQLMI);
}
- const assessmentError = this.migrationStateModel._assessmentResults.assessmentError;
+ const assessmentError = this.migrationStateModel._assessmentResults?.assessmentError;
if (assessmentError) {
errors.push(`message: ${assessmentError.message}${EOL}stack: ${assessmentError.stack}`);
}
if (this.migrationStateModel?._assessmentResults?.errors?.length! > 0) {
- errors.push(...this.migrationStateModel._assessmentResults.errors?.map(
+ errors.push(...this.migrationStateModel._assessmentResults?.errors?.map(
e => `message: ${e.message}${EOL}errorSummary: ${e.errorSummary}${EOL}possibleCauses: ${e.possibleCauses}${EOL}guidance: ${e.guidance}${EOL}errorId: ${e.errorId}`)!);
}
@@ -566,11 +574,11 @@ export class SKURecommendationPage extends MigrationWizardPage {
} 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._detailsComponent.value = constants.SKU_RECOMMENDATION_ALL_SUCCESSFUL(this.migrationStateModel._assessmentResults?.databaseAssessments?.length);
}
}
- if ((this.migrationStateModel.resumeAssessment) && this.migrationStateModel.savedInfo.closedPage >= Page.SKURecommendation) {
+ if (this.hasSavedInfo()) {
if (this.migrationStateModel.savedInfo.migrationTargetType) {
this._rbg.selectedCardId = this.migrationStateModel.savedInfo.migrationTargetType;
await this.refreshCardText();
@@ -602,9 +610,9 @@ export class SKURecommendationPage extends MigrationWizardPage {
await this.assessmentGroupContainer.updateCssStyles({ 'display': display });
this.assessmentGroupContainer.display = display;
- display = this._rbg.selectedCardId
+ display = (this._rbg.selectedCardId
&& (!failedAssessment || this._skipAssessmentCheckbox.checked)
- && this.migrationStateModel._migrationDbs.length > 0
+ && this.migrationStateModel._migrationDbs.length > 0)
? 'inline'
: 'none';
await this._targetContainer.updateCssStyles({ 'display': display });
@@ -614,7 +622,7 @@ export class SKURecommendationPage extends MigrationWizardPage {
}
private async populateSubscriptionDropdown(): Promise {
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.SKURecommendation) {
+ if (this.hasSavedInfo()) {
this.migrationStateModel._azureAccount = this.migrationStateModel.savedInfo.azureAccount;
}
if (!this.migrationStateModel._targetSubscription) {
@@ -628,9 +636,9 @@ export class SKURecommendationPage extends MigrationWizardPage {
this._managedInstanceSubscriptionDropdown.loading = false;
this._resourceDropdown.loading = false;
}
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= 2 && this._managedInstanceSubscriptionDropdown.values) {
- this._managedInstanceSubscriptionDropdown.values.forEach((subscription, index) => {
- if ((subscription).name === this.migrationStateModel.savedInfo?.subscription?.id) {
+ if (this.hasSavedInfo() && this._managedInstanceSubscriptionDropdown.values) {
+ this._managedInstanceSubscriptionDropdown.values!.forEach((subscription, index) => {
+ if ((subscription).name.toLowerCase() === this.migrationStateModel.savedInfo?.subscription?.id.toLowerCase()) {
selectDropDownIndex(this._managedInstanceSubscriptionDropdown, index);
}
});
@@ -645,9 +653,9 @@ export class SKURecommendationPage extends MigrationWizardPage {
this._azureLocationDropdown.loading = true;
try {
this._azureResourceGroupDropdown.values = await this.migrationStateModel.getAzureResourceGroupDropdownValues(this.migrationStateModel._targetSubscription);
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= 2 && this._azureResourceGroupDropdown.values) {
+ if (this.hasSavedInfo() && this._azureResourceGroupDropdown.values) {
this._azureResourceGroupDropdown.values.forEach((resourceGroup, index) => {
- if (resourceGroup.name === this.migrationStateModel.savedInfo?.resourceGroup?.id) {
+ if (resourceGroup.name.toLowerCase() === this.migrationStateModel.savedInfo?.resourceGroup?.id.toLowerCase()) {
selectDropDownIndex(this._azureResourceGroupDropdown, index);
}
});
@@ -655,7 +663,7 @@ export class SKURecommendationPage extends MigrationWizardPage {
selectDropDownIndex(this._azureResourceGroupDropdown, 0);
}
this._azureLocationDropdown.values = await this.migrationStateModel.getAzureLocationDropdownValues(this.migrationStateModel._targetSubscription);
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= 2 && this._azureLocationDropdown.values) {
+ if (this.hasSavedInfo() && this._azureLocationDropdown.values) {
this._azureLocationDropdown.values.forEach((location, index) => {
if (location.displayName === this.migrationStateModel.savedInfo?.location?.displayName) {
selectDropDownIndex(this._azureLocationDropdown, index);
@@ -690,9 +698,9 @@ export class SKURecommendationPage extends MigrationWizardPage {
this.migrationStateModel._location,
this.migrationStateModel._resourceGroup);
}
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= 2 && this._resourceDropdown.values) {
+ if (this.hasSavedInfo() && this._resourceDropdown.values) {
this._resourceDropdown.values.forEach((resource, index) => {
- if (resource.displayName === this.migrationStateModel.savedInfo?.targetServerInstance?.name) {
+ if (resource.displayName.toLowerCase() === this.migrationStateModel.savedInfo?.targetServerInstance?.name.toLowerCase()) {
selectDropDownIndex(this._resourceDropdown, index);
}
});
@@ -708,9 +716,6 @@ export class SKURecommendationPage extends MigrationWizardPage {
public async onPageEnter(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise {
this.wizard.registerNavigationValidator((pageChangeInfo) => {
- if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.SKURecommendation) {
- this.migrationStateModel._migrationDbs = this.migrationStateModel.savedInfo.databaseList;
- }
const errors: string[] = [];
this.wizard.message = {
text: '',
@@ -785,8 +790,8 @@ export class SKURecommendationPage extends MigrationWizardPage {
this._targetContainer.display = (this.migrationStateModel._migrationDbs.length === 0) ? 'none' : 'inline';
if (this.migrationStateModel._assessmentResults) {
- const dbCount = this.migrationStateModel._assessmentResults.databaseAssessments?.length;
- const dbWithoutIssuesCount = this.migrationStateModel._assessmentResults.databaseAssessments?.filter(db => db.issues?.length === 0).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);
@@ -837,6 +842,10 @@ export class SKURecommendationPage extends MigrationWizardPage {
}).component();
return this._assessmentInfo;
}
+
+ private hasSavedInfo(): boolean {
+ return this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.SKURecommendation);
+ }
}
diff --git a/extensions/sql-migration/src/wizard/summaryPage.ts b/extensions/sql-migration/src/wizard/summaryPage.ts
index d5f2aebd96..9adbe93f91 100644
--- a/extensions/sql-migration/src/wizard/summaryPage.ts
+++ b/extensions/sql-migration/src/wizard/summaryPage.ts
@@ -112,7 +112,7 @@ export class SummaryPage extends MigrationWizardPage {
]
);
- if (this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE && this.migrationStateModel._nodeNames.length > 0) {
+ if (this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE && this.migrationStateModel._nodeNames?.length > 0) {
this._flexContainer.addItem(createInformationRow(this._view, constants.SHIR, this.migrationStateModel._nodeNames.join(', ')));
}
}
diff --git a/extensions/sql-migration/src/wizard/wizardController.ts b/extensions/sql-migration/src/wizard/wizardController.ts
index 482af4d8f0..bb9646c4da 100644
--- a/extensions/sql-migration/src/wizard/wizardController.ts
+++ b/extensions/sql-migration/src/wizard/wizardController.ts
@@ -64,7 +64,7 @@ export class WizardController {
const wizardSetupPromises: Thenable[] = [];
wizardSetupPromises.push(...pages.map(p => p.registerWizardContent()));
wizardSetupPromises.push(this._wizardObject.open());
- if (this._model.resumeAssessment) {
+ if (this._model.retryMigration || this._model.resumeAssessment) {
if (this._model.savedInfo.closedPage >= Page.MigrationMode) {
this._model.refreshDatabaseBackupPage = true;
}