mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-06 01:25:38 -05:00
Migraiton enhancements v: 0.1.1 (#15570)
* adding filters and cards for failed migrations * Added card and filter for completing cutover * Fixing blob container support and some ux enhancements * Enabling eastus2 and canada central regions * Increasing height of container to accomodate newer cards and cleaning up database backup page * vbump migration * Removing unused code
This commit is contained in:
@@ -32,7 +32,12 @@ export async function getLocations(account: azdata.Account, subscription: Subscr
|
||||
throw new Error(response.errors.toString());
|
||||
}
|
||||
sortResourceArrayByName(response.locations);
|
||||
const supportedLocations = ['eastus2', 'eastus2euap'];
|
||||
const supportedLocations = [
|
||||
'eastus2',
|
||||
'eastus2euap',
|
||||
'eastus',
|
||||
'canadacentral'
|
||||
];
|
||||
const filteredLocations = response.locations.filter(loc => {
|
||||
return supportedLocations.includes(loc.name);
|
||||
});
|
||||
@@ -377,8 +382,8 @@ export interface DatabaseMigration {
|
||||
}
|
||||
export interface DatabaseMigrationProperties {
|
||||
scope: string;
|
||||
provisioningState: string;
|
||||
migrationStatus: string;
|
||||
provisioningState: 'Succeeded' | 'Failed' | 'Creating';
|
||||
migrationStatus: 'InProgress' | 'Failed' | 'Succeeded' | 'Creating' | 'Completing' | 'Cancelling';
|
||||
migrationStatusDetails?: MigrationStatusDetails;
|
||||
startedOn: string;
|
||||
endedOn: string;
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { DAYS, HRS, MINUTE, SEC } from '../constants/strings';
|
||||
import { AdsMigrationStatus } from '../dialog/migrationStatus/migrationStatusDialogModel';
|
||||
import { MigrationContext } from '../models/migrationLocalStorage';
|
||||
|
||||
export function deepClone<T>(obj: T): T {
|
||||
if (!obj || typeof obj !== 'object') {
|
||||
@@ -83,3 +85,38 @@ export function convertTimeDifferenceToDuration(startTime: Date, endTime: Date):
|
||||
return DAYS(parseFloat(days));
|
||||
}
|
||||
}
|
||||
|
||||
export function filterMigrations(databaseMigrations: MigrationContext[], statusFilter: string, databaseNameFilter?: string): MigrationContext[] {
|
||||
let filteredMigration: MigrationContext[] = [];
|
||||
if (statusFilter === AdsMigrationStatus.ALL) {
|
||||
filteredMigration = databaseMigrations;
|
||||
} else if (statusFilter === AdsMigrationStatus.ONGOING) {
|
||||
filteredMigration = databaseMigrations.filter((value) => {
|
||||
const status = value.migrationContext.properties.migrationStatus;
|
||||
const provisioning = value.migrationContext.properties.provisioningState;
|
||||
return status === 'InProgress' || status === 'Creating' || provisioning === 'Creating';
|
||||
});
|
||||
} else if (statusFilter === AdsMigrationStatus.SUCCEEDED) {
|
||||
filteredMigration = databaseMigrations.filter((value) => {
|
||||
const status = value.migrationContext.properties.migrationStatus;
|
||||
return status === 'Succeeded';
|
||||
});
|
||||
} else if (statusFilter === AdsMigrationStatus.FAILED) {
|
||||
filteredMigration = databaseMigrations.filter((value) => {
|
||||
const status = value.migrationContext.properties.migrationStatus;
|
||||
const provisioning = value.migrationContext.properties.provisioningState;
|
||||
return status === 'Failed' || provisioning === 'Failed';
|
||||
});
|
||||
} else if (statusFilter === AdsMigrationStatus.COMPLETING) {
|
||||
filteredMigration = databaseMigrations.filter((value) => {
|
||||
const status = value.migrationContext.properties.migrationStatus;
|
||||
return status === 'Completing';
|
||||
});
|
||||
}
|
||||
if (databaseNameFilter) {
|
||||
filteredMigration = filteredMigration.filter((value) => {
|
||||
return value.migrationContext.name.toLowerCase().includes(databaseNameFilter.toLowerCase());
|
||||
});
|
||||
}
|
||||
return filteredMigration;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ export class IconPathHelper {
|
||||
public static cancel: IconPath;
|
||||
public static warning: IconPath;
|
||||
public static info: IconPath;
|
||||
public static error: IconPath;
|
||||
public static completingCutover: IconPath;
|
||||
|
||||
public static setExtensionContext(context: vscode.ExtensionContext) {
|
||||
IconPathHelper.copy = {
|
||||
@@ -108,5 +110,13 @@ export class IconPathHelper {
|
||||
light: context.asAbsolutePath('images/info.svg'),
|
||||
dark: context.asAbsolutePath('images/infoBox.svg')
|
||||
};
|
||||
IconPathHelper.error = {
|
||||
light: context.asAbsolutePath('images/error.svg'),
|
||||
dark: context.asAbsolutePath('images/error.svg')
|
||||
};
|
||||
IconPathHelper.completingCutover = {
|
||||
light: context.asAbsolutePath('images/completingCutover.svg'),
|
||||
dark: context.asAbsolutePath('images/completingCutover.svg')
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import { AzureAccount } from 'azurecore';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { MigrationSourceAuthenticationType } from '../models/stateMachine';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
|
||||
@@ -144,6 +145,15 @@ export const ENTER_NETWORK_SHARE_INFORMATION = localize('sql.migration.enter.net
|
||||
export const ENTER_BLOB_CONTAINER_INFORMATION = localize('sql.migration.blob.container.information', "Enter the target name and select the blob container location for selected databases");
|
||||
export const ENTER_FILE_SHARE_INFORMATION = localize('sql.migration.enter.file.share.information', "Enter the target name and select the file share location of selected databases");
|
||||
export const INVALID_TARGET_NAME_ERROR = localize('sql.migration.invalid.target.name.error', "Please enter a valid name for the target database.");
|
||||
export const PROVIDE_UNIQUE_CONTAINERS = localize('sql.migration.provide.unique.containers', "Please provide unique containers for target databases. Databases affected: ");
|
||||
export function SQL_SOURCE_DETAILS(authMethod: MigrationSourceAuthenticationType, serverName: string): string {
|
||||
switch (authMethod) {
|
||||
case MigrationSourceAuthenticationType.Integrated:
|
||||
return localize('sql.migration.source.details.windowAuth', "Enter the Windows Authentication credential used for connecting to SQL Server Instance {0}. This credential will be used to for connecting to SQL Server instance and identifying valid backup file(s)", serverName);
|
||||
case MigrationSourceAuthenticationType.Sql:
|
||||
return localize('sql.migration.source.details.sqlAuth', "Enter the SQL Authentication credential used for connecting to SQL Server Instance {0}. This credential will be used to for connecting to SQL Server instance and identifying valid backup file(s)", serverName);
|
||||
}
|
||||
}
|
||||
|
||||
// integration runtime page
|
||||
export const IR_PAGE_TITLE = localize('sql.migration.ir.page.title', "Azure Database Migration Service");
|
||||
@@ -261,8 +271,10 @@ export const PRE_REQ_1 = localize('sql.migration.pre.req.1', "Azure account deta
|
||||
export const PRE_REQ_2 = localize('sql.migration.pre.req.2', "Azure SQL Managed Instance or SQL Server on Azure Virtual Machine");
|
||||
export const PRE_REQ_3 = localize('sql.migration.pre.req.3', "Backup location details");
|
||||
export const MIGRATION_IN_PROGRESS = localize('sql.migration.migration.in.progress', "Database migration in progress");
|
||||
export const MIGRATION_FAILED = localize('sql.migration.failed', "Migration failed");
|
||||
export const LOG_SHIPPING_IN_PROGRESS = localize('sql.migration.log.shipping.in.progress', "Log shipping in progress");
|
||||
export const MIGRATION_COMPLETED = localize('sql.migration.migration.completed', "Database migration completed");
|
||||
export const MIGRATION_COMPLETED = localize('sql.migration.migration.completed', "Migration completed");
|
||||
export const MIGRATION_CUTOVER_CARD = localize('sql.migration.cutover.card', "Completing cutover");
|
||||
export const SUCCESSFULLY_MIGRATED_TO_AZURE_SQL = localize('sql.migration.successfully.migrated.to.azure.sql', "Successfully migrated to Azure SQL");
|
||||
export const MIGRATION_NOT_STARTED = localize('sql.migration.migration.not.started', "Migration not started");
|
||||
export const CHOOSE_TO_MIGRATE_TO_AZURE_SQL = localize('sql.migration.choose.to.migrate.to.azure.sql', "Choose to migrate to Azure SQL");
|
||||
|
||||
@@ -9,7 +9,8 @@ import { MigrationContext, MigrationLocalStorage } from '../models/migrationLoca
|
||||
import * as loc from '../constants/strings';
|
||||
import { IconPath, IconPathHelper } from '../constants/iconPathHelper';
|
||||
import { MigrationStatusDialog } from '../dialog/migrationStatus/migrationStatusDialog';
|
||||
import { MigrationCategory } from '../dialog/migrationStatus/migrationStatusDialogModel';
|
||||
import { AdsMigrationStatus } from '../dialog/migrationStatus/migrationStatusDialogModel';
|
||||
import { filterMigrations } from '../api/utils';
|
||||
|
||||
interface IActionMetadata {
|
||||
title?: string,
|
||||
@@ -39,6 +40,8 @@ export class DashboardWidget {
|
||||
private _inProgressMigrationButton!: StatusCard;
|
||||
private _inProgressWarningMigrationButton!: StatusCard;
|
||||
private _successfulMigrationButton!: StatusCard;
|
||||
private _failedMigrationButton!: StatusCard;
|
||||
private _completingMigrationButton!: StatusCard;
|
||||
private _notStartedMigrationCard!: StatusCard;
|
||||
private _migrationStatusMap: Map<string, MigrationContext[]> = new Map();
|
||||
private _viewAllMigrationsButton!: azdata.ButtonComponent;
|
||||
@@ -233,15 +236,9 @@ export class DashboardWidget {
|
||||
this._migrationStatusCardLoadingContainer.loading = true;
|
||||
try {
|
||||
this.setCurrentMigrations(await this.getMigrations());
|
||||
const migrationStatus = await this.getCurrentMigrations();
|
||||
const inProgressMigrations = migrationStatus.filter((value) => {
|
||||
const status = value.migrationContext.properties.migrationStatus;
|
||||
const provisioning = value.migrationContext.properties.provisioningState;
|
||||
return status === 'InProgress' || status === 'Creating' || status === 'Completing' || provisioning === 'Creating';
|
||||
});
|
||||
|
||||
const migrations = await this.getCurrentMigrations();
|
||||
const inProgressMigrations = filterMigrations(migrations, AdsMigrationStatus.ONGOING);
|
||||
let warningCount = 0;
|
||||
|
||||
for (let i = 0; i < inProgressMigrations.length; i++) {
|
||||
if (
|
||||
inProgressMigrations[i].asyncOperationResult?.error?.message ||
|
||||
@@ -252,7 +249,6 @@ export class DashboardWidget {
|
||||
warningCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (warningCount > 0) {
|
||||
this._inProgressWarningMigrationButton.warningText!.value = loc.MIGRATION_INPROGRESS_WARNING(warningCount);
|
||||
this._inProgressMigrationButton.container.display = 'none';
|
||||
@@ -261,22 +257,32 @@ export class DashboardWidget {
|
||||
this._inProgressMigrationButton.container.display = 'inline';
|
||||
this._inProgressWarningMigrationButton.container.display = 'none';
|
||||
}
|
||||
|
||||
this._inProgressMigrationButton.count.value = inProgressMigrations.length.toString();
|
||||
this._inProgressWarningMigrationButton.count.value = inProgressMigrations.length.toString();
|
||||
|
||||
const successfulMigration = migrationStatus.filter((value) => {
|
||||
const status = value.migrationContext.properties.migrationStatus;
|
||||
return status === 'Succeeded';
|
||||
});
|
||||
const successfulMigration = filterMigrations(migrations, AdsMigrationStatus.SUCCEEDED);
|
||||
|
||||
this._successfulMigrationButton.count.value = successfulMigration.length.toString();
|
||||
const currentConnection = (await azdata.connection.getCurrentConnection());
|
||||
const migrationDatabases = new Set(
|
||||
migrationStatus.map((value) => {
|
||||
return value.migrationContext.properties.sourceDatabaseName;
|
||||
}));
|
||||
const serverDatabases = await azdata.connection.listDatabases(currentConnection.connectionId);
|
||||
this._notStartedMigrationCard.count.value = (serverDatabases.length - migrationDatabases.size).toString();
|
||||
|
||||
const failedMigrations = filterMigrations(migrations, AdsMigrationStatus.FAILED);
|
||||
const failedCount = failedMigrations.length;
|
||||
if (failedCount > 0) {
|
||||
this._failedMigrationButton.container.display = 'inline';
|
||||
this._failedMigrationButton.count.value = failedMigrations.length.toString();
|
||||
} else {
|
||||
this._failedMigrationButton.container.display = 'none';
|
||||
}
|
||||
|
||||
const completingCutoverMigrations = filterMigrations(migrations, AdsMigrationStatus.COMPLETING);
|
||||
const cutoverCount = completingCutoverMigrations.length;
|
||||
if (cutoverCount > 0) {
|
||||
this._completingMigrationButton.container.display = 'inline';
|
||||
this._completingMigrationButton.count.value = cutoverCount.toString();
|
||||
} else {
|
||||
this._completingMigrationButton.container.display = 'none';
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
@@ -498,7 +504,7 @@ export class DashboardWidget {
|
||||
const statusContainer = view.modelBuilder.flexContainer().withLayout({
|
||||
flexFlow: 'column',
|
||||
width: '400px',
|
||||
height: '280px',
|
||||
height: '350px',
|
||||
justifyContent: 'flex-start',
|
||||
}).withProps({
|
||||
CSSStyles: {
|
||||
@@ -527,7 +533,7 @@ export class DashboardWidget {
|
||||
|
||||
this._viewAllMigrationsButton.onDidClick(async (e) => {
|
||||
const migrationStatus = await this.getCurrentMigrations();
|
||||
new MigrationStatusDialog(migrationStatus ? migrationStatus : await this.getMigrations(), MigrationCategory.ALL).initialize();
|
||||
new MigrationStatusDialog(migrationStatus ? migrationStatus : await this.getMigrations(), AdsMigrationStatus.ALL).initialize();
|
||||
});
|
||||
|
||||
const refreshButton = view.modelBuilder.hyperlink().withProps({
|
||||
@@ -581,7 +587,7 @@ export class DashboardWidget {
|
||||
loc.MIGRATION_IN_PROGRESS
|
||||
);
|
||||
this._inProgressMigrationButton.container.onDidClick(async (e) => {
|
||||
const dialog = new MigrationStatusDialog(await this.getCurrentMigrations(), MigrationCategory.ONGOING);
|
||||
const dialog = new MigrationStatusDialog(await this.getCurrentMigrations(), AdsMigrationStatus.ONGOING);
|
||||
dialog.initialize();
|
||||
});
|
||||
|
||||
@@ -595,7 +601,7 @@ export class DashboardWidget {
|
||||
''
|
||||
);
|
||||
this._inProgressWarningMigrationButton.container.onDidClick(async (e) => {
|
||||
const dialog = new MigrationStatusDialog(await this.getCurrentMigrations(), MigrationCategory.ONGOING);
|
||||
const dialog = new MigrationStatusDialog(await this.getCurrentMigrations(), AdsMigrationStatus.ONGOING);
|
||||
dialog.initialize();
|
||||
});
|
||||
|
||||
@@ -608,13 +614,38 @@ export class DashboardWidget {
|
||||
loc.MIGRATION_COMPLETED
|
||||
);
|
||||
this._successfulMigrationButton.container.onDidClick(async (e) => {
|
||||
const dialog = new MigrationStatusDialog(await this.getCurrentMigrations(), MigrationCategory.SUCCEEDED);
|
||||
const dialog = new MigrationStatusDialog(await this.getCurrentMigrations(), AdsMigrationStatus.SUCCEEDED);
|
||||
dialog.initialize();
|
||||
});
|
||||
this._migrationStatusCardsContainer.addItem(
|
||||
this._successfulMigrationButton.container
|
||||
);
|
||||
|
||||
|
||||
this._completingMigrationButton = this.createStatusCard(
|
||||
IconPathHelper.completingCutover,
|
||||
loc.MIGRATION_CUTOVER_CARD
|
||||
);
|
||||
this._completingMigrationButton.container.onDidClick(async (e) => {
|
||||
const dialog = new MigrationStatusDialog(await this.getCurrentMigrations(), AdsMigrationStatus.COMPLETING);
|
||||
dialog.initialize();
|
||||
});
|
||||
this._migrationStatusCardsContainer.addItem(
|
||||
this._completingMigrationButton.container
|
||||
);
|
||||
|
||||
this._failedMigrationButton = this.createStatusCard(
|
||||
IconPathHelper.error,
|
||||
loc.MIGRATION_FAILED
|
||||
);
|
||||
this._failedMigrationButton.container.onDidClick(async (e) => {
|
||||
const dialog = new MigrationStatusDialog(await this.getCurrentMigrations(), AdsMigrationStatus.FAILED);
|
||||
dialog.initialize();
|
||||
});
|
||||
this._migrationStatusCardsContainer.addItem(
|
||||
this._failedMigrationButton.container
|
||||
);
|
||||
|
||||
this._notStartedMigrationCard = this.createStatusCard(
|
||||
IconPathHelper.notStartedMigration,
|
||||
loc.MIGRATION_NOT_STARTED
|
||||
@@ -650,7 +681,7 @@ export class DashboardWidget {
|
||||
const linksContainer = view.modelBuilder.flexContainer().withLayout({
|
||||
flexFlow: 'column',
|
||||
width: '400px',
|
||||
height: '280px',
|
||||
height: '350px',
|
||||
justifyContent: 'flex-start',
|
||||
}).withProps({
|
||||
CSSStyles: {
|
||||
|
||||
@@ -72,7 +72,8 @@ export class ConfirmCutoverDialog {
|
||||
}
|
||||
}).component();
|
||||
|
||||
const pendingBackupCount = this.migrationCutoverModel.migrationStatus.properties.migrationStatusDetails?.activeBackupSets.filter(f => f.listOfBackupFiles[0].status !== 'Restored' && f.listOfBackupFiles[0].status !== 'Ignored').length;
|
||||
|
||||
const pendingBackupCount = this.migrationCutoverModel.migrationStatus.properties.migrationStatusDetails?.activeBackupSets?.filter(f => f.listOfBackupFiles[0].status !== 'Restored' && f.listOfBackupFiles[0].status !== 'Ignored').length ?? 0;
|
||||
const pendingText = this._view.modelBuilder.text().withProps({
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
|
||||
@@ -517,9 +517,14 @@ export class MigrationCutoverDialog {
|
||||
this._fullBackupFile.value = fullBackupFileName! ?? '-';
|
||||
let backupLocation;
|
||||
|
||||
const isBlobMigration = this._model._migration.migrationContext.properties.backupConfiguration.sourceLocation?.azureBlob !== undefined;
|
||||
// Displaying storage accounts and blob container for azure blob backups.
|
||||
if (this._model._migration.migrationContext.properties.backupConfiguration.sourceLocation?.azureBlob) {
|
||||
backupLocation = `${this._model._migration.migrationContext.properties.backupConfiguration.sourceLocation.azureBlob.storageAccountResourceId.split('/').pop()} - ${this._model._migration.migrationContext.properties.backupConfiguration.sourceLocation.azureBlob.blobContainerName}`;
|
||||
if (isBlobMigration) {
|
||||
backupLocation = `${this._model._migration.migrationContext.properties.backupConfiguration.sourceLocation?.azureBlob?.storageAccountResourceId.split('/').pop()} - ${this._model._migration.migrationContext.properties.backupConfiguration.sourceLocation?.azureBlob?.blobContainerName}`;
|
||||
this._fileCount.display = 'none';
|
||||
this.fileTable.updateCssStyles({
|
||||
'display': 'none'
|
||||
});
|
||||
} else {
|
||||
backupLocation = this._model._migration.migrationContext.properties.backupConfiguration?.sourceLocation?.fileShare?.path! ?? '-';
|
||||
}
|
||||
@@ -547,7 +552,7 @@ export class MigrationCutoverDialog {
|
||||
|
||||
if (migrationStatusTextValue === MigrationStatus.InProgress) {
|
||||
const restoredCount = (this._model.migrationStatus.properties.migrationStatusDetails?.activeBackupSets?.filter(a => a.listOfBackupFiles[0].status === 'Restored'))?.length ?? 0;
|
||||
if (restoredCount > 0) {
|
||||
if (restoredCount > 0 || isBlobMigration) {
|
||||
this._cutoverButton.enabled = true;
|
||||
}
|
||||
this._cancelButton.enabled = true;
|
||||
|
||||
@@ -8,9 +8,9 @@ import * as vscode from 'vscode';
|
||||
import { IconPathHelper } from '../../constants/iconPathHelper';
|
||||
import { MigrationContext, MigrationLocalStorage } from '../../models/migrationLocalStorage';
|
||||
import { MigrationCutoverDialog } from '../migrationCutover/migrationCutoverDialog';
|
||||
import { MigrationCategory, MigrationStatusDialogModel } from './migrationStatusDialogModel';
|
||||
import { AdsMigrationStatus, MigrationStatusDialogModel } from './migrationStatusDialogModel';
|
||||
import * as loc from '../../constants/strings';
|
||||
import { convertTimeDifferenceToDuration } from '../../api/utils';
|
||||
import { convertTimeDifferenceToDuration, filterMigrations } from '../../api/utils';
|
||||
export class MigrationStatusDialog {
|
||||
private _model: MigrationStatusDialogModel;
|
||||
private _dialogObject!: azdata.window.Dialog;
|
||||
@@ -21,7 +21,7 @@ export class MigrationStatusDialog {
|
||||
private _statusTable!: azdata.DeclarativeTableComponent;
|
||||
private _refreshLoader!: azdata.LoadingComponent;
|
||||
|
||||
constructor(migrations: MigrationContext[], private _filter: MigrationCategory) {
|
||||
constructor(migrations: MigrationContext[], private _filter: AdsMigrationStatus) {
|
||||
this._model = new MigrationStatusDialogModel(migrations);
|
||||
this._dialogObject = azdata.window.createModelViewDialog(loc.MIGRATION_STATUS, 'MigrationControllerDialog', 'wide');
|
||||
}
|
||||
@@ -40,7 +40,11 @@ export class MigrationStatusDialog {
|
||||
this.populateMigrationTable();
|
||||
});
|
||||
|
||||
this._statusDropdown.value = this._statusDropdown.values![this._filter];
|
||||
if (this._filter) {
|
||||
this._statusDropdown.value = (<azdata.CategoryValue[]>this._statusDropdown.values).find((value) => {
|
||||
return value.name === this._filter;
|
||||
});
|
||||
}
|
||||
|
||||
const formBuilder = view.modelBuilder.formContainer().withFormItems(
|
||||
[
|
||||
@@ -124,10 +128,7 @@ export class MigrationStatusDialog {
|
||||
|
||||
private populateMigrationTable(): void {
|
||||
try {
|
||||
const migrations = this._model.filterMigration(
|
||||
this._searchBox.value!,
|
||||
(<azdata.CategoryValue>this._statusDropdown.value).name
|
||||
);
|
||||
const migrations = filterMigrations(this._model._migrations, (<azdata.CategoryValue>this._statusDropdown.value).name, this._searchBox.value!);
|
||||
|
||||
const data: azdata.DeclarativeTableCellValue[][] = [];
|
||||
|
||||
|
||||
@@ -10,47 +10,34 @@ export class MigrationStatusDialogModel {
|
||||
public statusDropdownValues: azdata.CategoryValue[] = [
|
||||
{
|
||||
displayName: 'Status: All',
|
||||
name: 'All',
|
||||
name: AdsMigrationStatus.ALL,
|
||||
}, {
|
||||
displayName: 'Status: Ongoing',
|
||||
name: 'Ongoing',
|
||||
name: AdsMigrationStatus.ONGOING,
|
||||
}, {
|
||||
displayName: 'Status: Completing',
|
||||
name: AdsMigrationStatus.COMPLETING
|
||||
}, {
|
||||
displayName: 'Status: Succeeded',
|
||||
name: 'Succeeded',
|
||||
name: AdsMigrationStatus.SUCCEEDED,
|
||||
}, {
|
||||
displayName: 'Status: Failed',
|
||||
name: AdsMigrationStatus.FAILED
|
||||
}
|
||||
];
|
||||
|
||||
constructor(public _migrations: MigrationContext[]) {
|
||||
}
|
||||
|
||||
public filterMigration(databaseName: string, category: string): MigrationContext[] {
|
||||
let filteredMigration: MigrationContext[] = [];
|
||||
if (category === 'All') {
|
||||
filteredMigration = this._migrations;
|
||||
} else if (category === 'Ongoing') {
|
||||
filteredMigration = this._migrations.filter((value) => {
|
||||
const status = value.migrationContext.properties.migrationStatus;
|
||||
const provisioning = value.migrationContext.properties.provisioningState;
|
||||
return status === 'InProgress' || status === 'Creating' || status === 'Completing' || provisioning === 'Creating';
|
||||
});
|
||||
} else if (category === 'Succeeded') {
|
||||
filteredMigration = this._migrations.filter((value) => {
|
||||
const status = value.migrationContext.properties.migrationStatus;
|
||||
return status === 'Succeeded';
|
||||
});
|
||||
}
|
||||
if (databaseName) {
|
||||
filteredMigration = filteredMigration.filter((value) => {
|
||||
return value.migrationContext.name.toLowerCase().includes(databaseName.toLowerCase());
|
||||
});
|
||||
}
|
||||
|
||||
return filteredMigration;
|
||||
}
|
||||
}
|
||||
|
||||
export enum MigrationCategory {
|
||||
ALL,
|
||||
ONGOING,
|
||||
SUCCEEDED
|
||||
/**
|
||||
* This enum is used to categorize migrations internally in ADS. A migration has 2 statuses: Provisioning Status and Migration Status. The values from both the statuses are mapped to different values in this enum
|
||||
*/
|
||||
export enum AdsMigrationStatus {
|
||||
ALL = 'all',
|
||||
ONGOING = 'ongoing',
|
||||
SUCCEEDED = 'succeeded',
|
||||
FAILED = 'failed',
|
||||
COMPLETING = 'completing'
|
||||
}
|
||||
|
||||
@@ -58,10 +58,9 @@ export enum NetworkContainerType {
|
||||
export interface DatabaseBackupModel {
|
||||
migrationMode: MigrationMode;
|
||||
networkContainerType: NetworkContainerType;
|
||||
storageKey: string;
|
||||
networkShare: NetworkShare;
|
||||
subscription: azureResource.AzureResourceSubscription;
|
||||
blob: Blob;
|
||||
blobs: Blob[];
|
||||
}
|
||||
|
||||
export interface NetworkShare {
|
||||
@@ -70,12 +69,14 @@ export interface NetworkShare {
|
||||
password: string;
|
||||
resourceGroup: azureResource.AzureResourceResourceGroup;
|
||||
storageAccount: StorageAccount;
|
||||
storageKey: string;
|
||||
}
|
||||
|
||||
export interface Blob {
|
||||
resourceGroup: azureResource.AzureResourceResourceGroup;
|
||||
storageAccount: StorageAccount;
|
||||
blobContainer: azureResource.BlobContainer;
|
||||
storageKey: string;
|
||||
}
|
||||
|
||||
export interface Model {
|
||||
@@ -144,7 +145,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
this._currentState = State.INIT;
|
||||
this._databaseBackup = {} as DatabaseBackupModel;
|
||||
this._databaseBackup.networkShare = {} as NetworkShare;
|
||||
this._databaseBackup.blob = {} as Blob;
|
||||
this._databaseBackup.blobs = [];
|
||||
}
|
||||
|
||||
public get sourceConnectionId(): string {
|
||||
@@ -667,38 +668,38 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
scope: this._targetServerInstance.id
|
||||
}
|
||||
};
|
||||
switch (this._databaseBackup.networkContainerType) {
|
||||
case NetworkContainerType.BLOB_CONTAINER:
|
||||
requestBody.properties.backupConfiguration = {
|
||||
targetLocation: undefined!,
|
||||
sourceLocation: {
|
||||
azureBlob: {
|
||||
storageAccountResourceId: this._databaseBackup.blob.storageAccount.id,
|
||||
accountKey: this._databaseBackup.storageKey,
|
||||
blobContainerName: this._databaseBackup.blob.blobContainer.name
|
||||
}
|
||||
}
|
||||
};
|
||||
break;
|
||||
case NetworkContainerType.NETWORK_SHARE:
|
||||
requestBody.properties.backupConfiguration = {
|
||||
targetLocation: {
|
||||
storageAccountResourceId: this._databaseBackup.networkShare.storageAccount.id,
|
||||
accountKey: this._databaseBackup.storageKey,
|
||||
},
|
||||
sourceLocation: {
|
||||
fileShare: {
|
||||
path: this._databaseBackup.networkShare.networkShareLocation,
|
||||
username: this._databaseBackup.networkShare.windowsUser,
|
||||
password: this._databaseBackup.networkShare.password,
|
||||
}
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._migrationDbs.length; i++) {
|
||||
try {
|
||||
switch (this._databaseBackup.networkContainerType) {
|
||||
case NetworkContainerType.BLOB_CONTAINER:
|
||||
requestBody.properties.backupConfiguration = {
|
||||
targetLocation: undefined!,
|
||||
sourceLocation: {
|
||||
azureBlob: {
|
||||
storageAccountResourceId: this._databaseBackup.blobs[i].storageAccount.id,
|
||||
accountKey: this._databaseBackup.blobs[i].storageKey,
|
||||
blobContainerName: this._databaseBackup.blobs[i].blobContainer.name
|
||||
}
|
||||
}
|
||||
};
|
||||
break;
|
||||
case NetworkContainerType.NETWORK_SHARE:
|
||||
requestBody.properties.backupConfiguration = {
|
||||
targetLocation: {
|
||||
storageAccountResourceId: this._databaseBackup.networkShare.storageAccount.id,
|
||||
accountKey: this._databaseBackup.networkShare.storageKey,
|
||||
},
|
||||
sourceLocation: {
|
||||
fileShare: {
|
||||
path: this._databaseBackup.networkShare.networkShareLocation,
|
||||
username: this._databaseBackup.networkShare.windowsUser,
|
||||
password: this._databaseBackup.networkShare.password,
|
||||
}
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
requestBody.properties.sourceDatabaseName = this._migrationDbs[i];
|
||||
const response = await startDatabaseMigration(
|
||||
this._azureAccount,
|
||||
|
||||
@@ -7,9 +7,8 @@ import * as azdata from 'azdata';
|
||||
import { EOL } from 'os';
|
||||
import { getStorageAccountAccessKeys } from '../api/azure';
|
||||
import { MigrationWizardPage } from '../models/migrationWizardPage';
|
||||
import { MigrationStateModel, MigrationTargetType, NetworkContainerType, StateChangeEvent } from '../models/stateMachine';
|
||||
import { Blob, MigrationSourceAuthenticationType, MigrationStateModel, MigrationTargetType, NetworkContainerType, StateChangeEvent } from '../models/stateMachine';
|
||||
import * as constants from '../constants/strings';
|
||||
import * as vscode from 'vscode';
|
||||
import { IconPathHelper } from '../constants/iconPathHelper';
|
||||
import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
|
||||
export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
@@ -19,17 +18,16 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
private _windowsUserAccountText!: azdata.InputBoxComponent;
|
||||
private _passwordText!: azdata.InputBoxComponent;
|
||||
private _networkSharePath!: azdata.InputBoxComponent;
|
||||
private _sourceHelpText!: azdata.TextComponent;
|
||||
private _sqlSourceUsernameInput!: azdata.InputBoxComponent;
|
||||
private _sqlSourcepassword!: azdata.InputBoxComponent;
|
||||
|
||||
private _blobContainer!: azdata.FlexContainer;
|
||||
private _blobContainerSubscription!: azdata.InputBoxComponent;
|
||||
private _blobContainerLocation!: azdata.InputBoxComponent;
|
||||
private _blobContainerResourceGroup!: azdata.DropDownComponent;
|
||||
private _blobContainerStorageAccountDropdown!: azdata.DropDownComponent;
|
||||
private _blobContainerDropdown!: azdata.DropDownComponent;
|
||||
|
||||
private _fileShareContainer!: azdata.FlexContainer;
|
||||
private _fileShareSubscription!: azdata.InputBoxComponent;
|
||||
private _fileShareStorageAccountDropdown!: azdata.DropDownComponent;
|
||||
private _blobContainerResourceGroupDropdowns!: azdata.DropDownComponent[];
|
||||
private _blobContainerStorageAccountDropdowns!: azdata.DropDownComponent[];
|
||||
private _blobContainerDropdowns!: azdata.DropDownComponent[];
|
||||
|
||||
private _networkShareStorageAccountDetails!: azdata.FlexContainer;
|
||||
private _networkShareContainerSubscription!: azdata.InputBoxComponent;
|
||||
@@ -39,8 +37,12 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
private _networkShareContainerStorageAccountRefreshButton!: azdata.ButtonComponent;
|
||||
|
||||
private _targetDatabaseContainer!: azdata.FlexContainer;
|
||||
private _targetDatabaseNamesTable!: azdata.DeclarativeTableComponent;
|
||||
private _targetDatabaseNames: azdata.InputBoxComponent[] = [];
|
||||
private _newtworkShareTargetDatabaseNamesTable!: azdata.DeclarativeTableComponent;
|
||||
private _blobContainerTargetDatabaseNamesTable!: azdata.DeclarativeTableComponent;
|
||||
private _networkTableContainer!: azdata.FlexContainer;
|
||||
private _blobTableContainer!: azdata.FlexContainer;
|
||||
private _networkShareTargetDatabaseNames: azdata.InputBoxComponent[] = [];
|
||||
private _blobContainerTargetDatabaseNames: azdata.InputBoxComponent[] = [];
|
||||
|
||||
|
||||
private _existingDatabases: string[] = [];
|
||||
@@ -107,7 +109,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
|
||||
networkShareButton.onDidChangeCheckedState((e) => {
|
||||
if (e) {
|
||||
this.toggleNetworkContainerFields(NetworkContainerType.NETWORK_SHARE);
|
||||
this.switchNetworkContainerFields(NetworkContainerType.NETWORK_SHARE);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -122,25 +124,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
|
||||
blobContainerButton.onDidChangeCheckedState((e) => {
|
||||
if (e) {
|
||||
this.toggleNetworkContainerFields(NetworkContainerType.BLOB_CONTAINER);
|
||||
}
|
||||
});
|
||||
|
||||
const fileShareButton = this._view.modelBuilder.radioButton()
|
||||
.withProps({
|
||||
name: buttonGroup,
|
||||
label: constants.DATABASE_BACKUP_NC_FILE_SHARE_RADIO_LABEL,
|
||||
enabled: false,
|
||||
CSSStyles: {
|
||||
'font-size': '13px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
fileShareButton.onDidChangeCheckedState((e) => {
|
||||
if (e) {
|
||||
vscode.window.showInformationMessage('Feature coming soon');
|
||||
networkShareButton.checked = true;
|
||||
//this.toggleNetworkContainerFields(NetworkContainerType.FILE_SHARE);
|
||||
this.switchNetworkContainerFields(NetworkContainerType.BLOB_CONTAINER);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -148,8 +132,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
[
|
||||
selectLocationText,
|
||||
networkShareButton,
|
||||
blobContainerButton,
|
||||
fileShareButton
|
||||
blobContainerButton
|
||||
]
|
||||
).withLayout({
|
||||
flexFlow: 'column'
|
||||
@@ -161,19 +144,69 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
private createNetworkDetailsContainer(): azdata.FlexContainer {
|
||||
this._networkShareContainer = this.createNetworkShareContainer();
|
||||
this._blobContainer = this.createBlobContainer();
|
||||
this._fileShareContainer = this.createFileShareContainer();
|
||||
|
||||
const networkContainer = this._view.modelBuilder.flexContainer().withLayout({
|
||||
flexFlow: 'column'
|
||||
}).withItems([
|
||||
this._networkShareContainer,
|
||||
this._blobContainer,
|
||||
this._fileShareContainer
|
||||
]).component();
|
||||
return networkContainer;
|
||||
}
|
||||
|
||||
private createNetworkShareContainer(): azdata.FlexContainer {
|
||||
|
||||
const sqlSourceHeader = this._view.modelBuilder.text().withProps({
|
||||
value: constants.SOURCE_CREDENTIALS,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
CSSStyles: {
|
||||
'font-size': '14px',
|
||||
'font-weight': 'bold'
|
||||
}
|
||||
}).component();
|
||||
|
||||
this._sourceHelpText = this._view.modelBuilder.text().withProps({
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
}
|
||||
}).component();
|
||||
|
||||
const usernameLable = this._view.modelBuilder.text().withProps({
|
||||
value: constants.USERNAME,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold',
|
||||
}
|
||||
}).component();
|
||||
this._sqlSourceUsernameInput = this._view.modelBuilder.inputBox().withProps({
|
||||
required: true,
|
||||
enabled: false,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||
}).component();
|
||||
this._sqlSourceUsernameInput.onTextChanged(value => {
|
||||
this.migrationStateModel._sqlServerUsername = value;
|
||||
});
|
||||
|
||||
const sqlPasswordLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.DATABASE_BACKUP_NETWORK_SHARE_PASSWORD_LABEL,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold',
|
||||
}
|
||||
}).component();
|
||||
this._sqlSourcepassword = this._view.modelBuilder.inputBox().withProps({
|
||||
required: true,
|
||||
inputType: 'password',
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||
}).component();
|
||||
this._sqlSourcepassword.onTextChanged(value => {
|
||||
this.migrationStateModel._sqlServerPassword = value;
|
||||
});
|
||||
|
||||
|
||||
const networkShareHeading = this._view.modelBuilder.text().withProps({
|
||||
value: constants.DATABASE_BACKUP_NETWORK_SHARE_HEADER_TEXT,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
@@ -282,8 +315,17 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
this.migrationStateModel._databaseBackup.networkShare.password = value;
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
const flexContainer = this._view.modelBuilder.flexContainer().withItems(
|
||||
[
|
||||
sqlSourceHeader,
|
||||
this._sourceHelpText,
|
||||
usernameLable,
|
||||
this._sqlSourceUsernameInput,
|
||||
sqlPasswordLabel,
|
||||
this._sqlSourcepassword,
|
||||
networkShareHeading,
|
||||
networkShareHelpText,
|
||||
networkLocationInputBoxLabel,
|
||||
@@ -303,39 +345,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
return flexContainer;
|
||||
}
|
||||
|
||||
private createFileShareContainer(): azdata.FlexContainer {
|
||||
|
||||
const subscriptionLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.DATABASE_BACKUP_FILE_SHARE_SUBSCRIPTION_LABEL,
|
||||
requiredIndicator: true,
|
||||
}).component();
|
||||
this._fileShareSubscription = this._view.modelBuilder.inputBox().withProps({
|
||||
enabled: false
|
||||
}).component();
|
||||
|
||||
const storageAccountLabel = this._view.modelBuilder.text()
|
||||
.withProps({
|
||||
value: constants.DATABASE_BACKUP_FILE_SHARE_STORAGE_ACCOUNT_LABEL,
|
||||
}).component();
|
||||
this._fileShareStorageAccountDropdown = this._view.modelBuilder.dropDown().component();
|
||||
|
||||
const flexContainer = this._view.modelBuilder.flexContainer()
|
||||
.withItems(
|
||||
[
|
||||
subscriptionLabel,
|
||||
this._fileShareSubscription,
|
||||
storageAccountLabel,
|
||||
this._fileShareStorageAccountDropdown
|
||||
]
|
||||
).withLayout({
|
||||
flexFlow: 'column'
|
||||
}).withProps({
|
||||
display: 'none'
|
||||
}).component();
|
||||
|
||||
return flexContainer;
|
||||
}
|
||||
|
||||
private createBlobContainer(): azdata.FlexContainer {
|
||||
|
||||
const subscriptionLabel = this._view.modelBuilder.text()
|
||||
@@ -364,66 +373,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
enabled: false
|
||||
}).component();
|
||||
|
||||
const resourceGroupLabel = this._view.modelBuilder.text()
|
||||
.withProps({
|
||||
value: constants.RESOURCE_GROUP,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold'
|
||||
}
|
||||
}).component();
|
||||
this._blobContainerResourceGroup = this._view.modelBuilder.dropDown().withProps({
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||
}).component();
|
||||
this._blobContainerResourceGroup.onValueChanged(e => {
|
||||
if (e.selected && e.selected !== constants.RESOURCE_GROUP_NOT_FOUND) {
|
||||
this.migrationStateModel._databaseBackup.blob.resourceGroup = this.migrationStateModel.getAzureResourceGroup(e.index);
|
||||
}
|
||||
this.loadblobStorageDropdown();
|
||||
});
|
||||
|
||||
const storageAccountLabel = this._view.modelBuilder.text()
|
||||
.withProps({
|
||||
value: constants.STORAGE_ACCOUNT,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold'
|
||||
}
|
||||
}).component();
|
||||
this._blobContainerStorageAccountDropdown = this._view.modelBuilder.dropDown()
|
||||
.withProps({
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||
}).component();
|
||||
this._blobContainerStorageAccountDropdown.onValueChanged(async (value) => {
|
||||
if (value.selected && value.selected !== constants.NO_STORAGE_ACCOUNT_FOUND) {
|
||||
this.migrationStateModel._databaseBackup.blob.storageAccount = this.migrationStateModel.getStorageAccount(value.index);
|
||||
}
|
||||
await this.loadBlobContainerDropdown();
|
||||
});
|
||||
|
||||
|
||||
const blobContainerLabel = this._view.modelBuilder.text()
|
||||
.withProps({
|
||||
value: constants.BLOB_CONTAINER,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold'
|
||||
}
|
||||
}).component();
|
||||
this._blobContainerDropdown = this._view.modelBuilder.dropDown()
|
||||
.withProps({
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||
}).component();
|
||||
this._blobContainerDropdown.onValueChanged(async (value) => {
|
||||
if (value.selected && value.selected !== constants.NO_BLOBCONTAINERS_FOUND) {
|
||||
this.migrationStateModel._databaseBackup.blob.blobContainer = this.migrationStateModel.getBlobContainer(value.index);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
const flexContainer = this._view.modelBuilder.flexContainer()
|
||||
.withItems(
|
||||
[
|
||||
@@ -431,12 +380,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
this._blobContainerSubscription,
|
||||
locationLabel,
|
||||
this._blobContainerLocation,
|
||||
resourceGroupLabel,
|
||||
this._blobContainerResourceGroup,
|
||||
storageAccountLabel,
|
||||
this._blobContainerStorageAccountDropdown,
|
||||
blobContainerLabel,
|
||||
this._blobContainerDropdown,
|
||||
]
|
||||
).withLayout({
|
||||
flexFlow: 'column'
|
||||
@@ -449,12 +392,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
|
||||
|
||||
private createTargetDatabaseContainer(): azdata.FlexContainer {
|
||||
const rowCssStyle: azdata.CssStyles = {
|
||||
'border': 'none',
|
||||
'font-size': '13px',
|
||||
'border-bottom': '1px solid',
|
||||
};
|
||||
|
||||
const headerCssStyles: azdata.CssStyles = {
|
||||
'border': 'none',
|
||||
'font-size': '13px',
|
||||
@@ -462,8 +399,13 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
'text-align': 'left',
|
||||
'border-bottom': '1px solid',
|
||||
};
|
||||
const rowCssStyle: azdata.CssStyles = {
|
||||
'border': 'none',
|
||||
'font-size': '13px',
|
||||
'border-bottom': '1px solid',
|
||||
};
|
||||
|
||||
this._targetDatabaseNamesTable = this._view.modelBuilder.declarativeTable().withProps({
|
||||
this._newtworkShareTargetDatabaseNamesTable = this._view.modelBuilder.declarativeTable().withProps({
|
||||
columns: [
|
||||
{
|
||||
displayName: constants.SOURCE_DATABASE,
|
||||
@@ -483,11 +425,64 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
}
|
||||
]
|
||||
}).component();
|
||||
this._blobContainerTargetDatabaseNamesTable = this._view.modelBuilder.declarativeTable().withProps({
|
||||
columns: [
|
||||
{
|
||||
displayName: constants.SOURCE_DATABASE,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
rowCssStyles: rowCssStyle,
|
||||
headerCssStyles: headerCssStyles,
|
||||
isReadOnly: true,
|
||||
width: '200px'
|
||||
},
|
||||
{
|
||||
displayName: constants.TARGET_DATABASE_NAME,
|
||||
valueType: azdata.DeclarativeDataType.component,
|
||||
rowCssStyles: rowCssStyle,
|
||||
headerCssStyles: headerCssStyles,
|
||||
isReadOnly: true,
|
||||
width: '200px'
|
||||
},
|
||||
{
|
||||
displayName: constants.RESOURCE_GROUP,
|
||||
valueType: azdata.DeclarativeDataType.component,
|
||||
rowCssStyles: rowCssStyle,
|
||||
headerCssStyles: headerCssStyles,
|
||||
isReadOnly: true,
|
||||
width: '200px'
|
||||
},
|
||||
{
|
||||
displayName: constants.STORAGE_ACCOUNT,
|
||||
valueType: azdata.DeclarativeDataType.component,
|
||||
rowCssStyles: rowCssStyle,
|
||||
headerCssStyles: headerCssStyles,
|
||||
isReadOnly: true,
|
||||
width: '200px'
|
||||
},
|
||||
{
|
||||
displayName: constants.BLOB_CONTAINER,
|
||||
valueType: azdata.DeclarativeDataType.component,
|
||||
rowCssStyles: rowCssStyle,
|
||||
headerCssStyles: headerCssStyles,
|
||||
isReadOnly: true,
|
||||
width: '200px'
|
||||
}
|
||||
]
|
||||
}).component();
|
||||
|
||||
this._networkTableContainer = this._view.modelBuilder.flexContainer().withItems([
|
||||
this._newtworkShareTargetDatabaseNamesTable
|
||||
]).component();
|
||||
|
||||
this._blobTableContainer = this._view.modelBuilder.flexContainer().withItems([
|
||||
this._blobContainerTargetDatabaseNamesTable
|
||||
]).component();
|
||||
|
||||
const container = this._view.modelBuilder.flexContainer().withLayout({
|
||||
flexFlow: 'column'
|
||||
}).withItems([
|
||||
this._targetDatabaseNamesTable
|
||||
this._networkTableContainer,
|
||||
this._blobTableContainer
|
||||
]).withProps({
|
||||
display: 'none'
|
||||
}).component();
|
||||
@@ -631,23 +626,39 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
|
||||
|
||||
public async onPageEnter(): Promise<void> {
|
||||
|
||||
if (this.migrationStateModel.refreshDatabaseBackupPage) {
|
||||
this._targetDatabaseNames = [];
|
||||
const connectionProfile = await this.migrationStateModel.getSourceConnectionProfile();
|
||||
const queryProvider = azdata.dataprotocol.getProvider<azdata.QueryProvider>((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 = [];
|
||||
|
||||
if (this.migrationStateModel._targetType === MigrationTargetType.SQLMI) {
|
||||
this._existingDatabases = await this.migrationStateModel.getManagedDatabases();
|
||||
}
|
||||
this.migrationStateModel._targetDatabaseNames = [];
|
||||
|
||||
const tableRows: azdata.DeclarativeTableCellValue[][] = [];
|
||||
this.migrationStateModel._databaseBackup.blobs = [];
|
||||
this.migrationStateModel._migrationDbs.forEach((db, index) => {
|
||||
const targetRow: azdata.DeclarativeTableCellValue[] = [];
|
||||
|
||||
this.migrationStateModel._targetDatabaseNames.push('');
|
||||
this.migrationStateModel._databaseBackup.blobs.push(<Blob>{});
|
||||
const targetDatabaseInput = this._view.modelBuilder.inputBox().withProps({
|
||||
required: true,
|
||||
value: db,
|
||||
width: '280px'
|
||||
width: '200px'
|
||||
}).withValidation(c => {
|
||||
if (this._targetDatabaseNames.filter(t => t.value === c.value).length > 1) { //Making sure no databases have duplicate values.
|
||||
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;
|
||||
}
|
||||
@@ -664,18 +675,105 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
targetDatabaseInput.onTextChanged((value) => {
|
||||
this.migrationStateModel._targetDatabaseNames[index] = value.trim();
|
||||
});
|
||||
this._targetDatabaseNames.push(targetDatabaseInput);
|
||||
this._networkShareTargetDatabaseNames.push(targetDatabaseInput);
|
||||
|
||||
const blobtargetDatabaseInput = this._view.modelBuilder.inputBox().withProps({
|
||||
required: true,
|
||||
value: db,
|
||||
width: '200px'
|
||||
}).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();
|
||||
blobtargetDatabaseInput.onTextChanged((value) => {
|
||||
this.migrationStateModel._targetDatabaseNames[index] = value.trim();
|
||||
});
|
||||
this._blobContainerTargetDatabaseNames.push(blobtargetDatabaseInput);
|
||||
|
||||
const blobContainerResourceDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||
width: '200px'
|
||||
}).component();
|
||||
blobContainerResourceDropdown.onValueChanged(e => {
|
||||
if (e.selected && e.selected !== constants.RESOURCE_GROUP_NOT_FOUND) {
|
||||
this.migrationStateModel._databaseBackup.blobs[index].resourceGroup = this.migrationStateModel.getAzureResourceGroup(e.index);
|
||||
}
|
||||
this.loadblobStorageDropdown(index);
|
||||
});
|
||||
this._blobContainerResourceGroupDropdowns.push(blobContainerResourceDropdown);
|
||||
|
||||
const blobContainerStorageAccountDropdown = this._view.modelBuilder.dropDown()
|
||||
.withProps({
|
||||
width: '200px'
|
||||
}).component();
|
||||
|
||||
blobContainerStorageAccountDropdown.onValueChanged(async (value) => {
|
||||
if (value.selected && value.selected !== constants.NO_STORAGE_ACCOUNT_FOUND) {
|
||||
this.migrationStateModel._databaseBackup.blobs[index].storageAccount = this.migrationStateModel.getStorageAccount(value.index);
|
||||
}
|
||||
await this.loadBlobContainerDropdown(index);
|
||||
});
|
||||
this._blobContainerStorageAccountDropdowns.push(blobContainerStorageAccountDropdown);
|
||||
|
||||
const blobContainerDropdown = this._view.modelBuilder.dropDown()
|
||||
.withProps({
|
||||
width: '200px'
|
||||
}).component();
|
||||
blobContainerDropdown.onValueChanged(async (value) => {
|
||||
if (value.selected && value.selected !== constants.NO_BLOBCONTAINERS_FOUND) {
|
||||
this.migrationStateModel._databaseBackup.blobs[index].blobContainer = this.migrationStateModel.getBlobContainer(value.index);
|
||||
}
|
||||
});
|
||||
this._blobContainerDropdowns.push(blobContainerDropdown);
|
||||
});
|
||||
|
||||
|
||||
let data: azdata.DeclarativeTableCellValue[][] = [];
|
||||
this.migrationStateModel._migrationDbs.forEach((db, index) => {
|
||||
const targetRow: azdata.DeclarativeTableCellValue[] = [];
|
||||
targetRow.push({
|
||||
value: db
|
||||
});
|
||||
targetRow.push({
|
||||
value: targetDatabaseInput
|
||||
value: this._networkShareTargetDatabaseNames[index]
|
||||
});
|
||||
tableRows.push(targetRow);
|
||||
data.push(targetRow);
|
||||
});
|
||||
this._newtworkShareTargetDatabaseNamesTable.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]
|
||||
});
|
||||
data.push(targetRow);
|
||||
});
|
||||
this._blobContainerTargetDatabaseNamesTable.dataValues = data;
|
||||
|
||||
this._targetDatabaseNamesTable.dataValues = tableRows;
|
||||
this.migrationStateModel.refreshDatabaseBackupPage = false;
|
||||
}
|
||||
await this.getSubscriptionValues();
|
||||
@@ -696,15 +794,39 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
}
|
||||
break;
|
||||
case NetworkContainerType.BLOB_CONTAINER:
|
||||
if ((<azdata.CategoryValue>this._blobContainerResourceGroup.value).displayName === constants.RESOURCE_GROUP_NOT_FOUND) {
|
||||
errors.push(constants.INVALID_RESOURCE_GROUP_ERROR);
|
||||
}
|
||||
if ((<azdata.CategoryValue>this._blobContainerStorageAccountDropdown.value).displayName === constants.NO_STORAGE_ACCOUNT_FOUND) {
|
||||
errors.push(constants.INVALID_STORAGE_ACCOUNT_ERROR);
|
||||
}
|
||||
if ((<azdata.CategoryValue>this._blobContainerDropdown.value).displayName === constants.NO_BLOBCONTAINERS_FOUND) {
|
||||
errors.push(constants.INVALID_BLOBCONTAINER_ERROR);
|
||||
this._blobContainerResourceGroupDropdowns.forEach(v => {
|
||||
if ((<azdata.CategoryValue>v.value).displayName === constants.RESOURCE_GROUP_NOT_FOUND) {
|
||||
errors.push(constants.INVALID_RESOURCE_GROUP_ERROR);
|
||||
}
|
||||
});
|
||||
this._blobContainerStorageAccountDropdowns.forEach(v => {
|
||||
if ((<azdata.CategoryValue>v.value).displayName === constants.NO_STORAGE_ACCOUNT_FOUND) {
|
||||
errors.push(constants.INVALID_STORAGE_ACCOUNT_ERROR);
|
||||
}
|
||||
});
|
||||
this._blobContainerDropdowns.forEach(v => {
|
||||
if ((<azdata.CategoryValue>v.value).displayName === constants.NO_BLOBCONTAINERS_FOUND) {
|
||||
errors.push(constants.INVALID_BLOBCONTAINER_ERROR);
|
||||
}
|
||||
});
|
||||
|
||||
const duplicates: Map<string, number[]> = new Map();
|
||||
for (let i = 0; i < this.migrationStateModel._targetDatabaseNames.length; i++) {
|
||||
const blobContainerId = this.migrationStateModel._databaseBackup.blobs[i].blobContainer.id;
|
||||
if (duplicates.has(blobContainerId)) {
|
||||
duplicates.get(blobContainerId)?.push(i);
|
||||
} else {
|
||||
duplicates.set(blobContainerId, [i]);
|
||||
}
|
||||
}
|
||||
|
||||
duplicates.forEach((d) => {
|
||||
if (d.length > 1) {
|
||||
const dupString = `${d.map(index => this.migrationStateModel._migrationDbs[index]).join(', ')}`;
|
||||
errors.push(constants.PROVIDE_UNIQUE_CONTAINERS + dupString);
|
||||
}
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -727,13 +849,25 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
|
||||
public async onPageLeave(): Promise<void> {
|
||||
try {
|
||||
const storageAccount = (this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.BLOB_CONTAINER) ?
|
||||
this.migrationStateModel._databaseBackup.blob.storageAccount : this.migrationStateModel._databaseBackup.networkShare.storageAccount;
|
||||
switch (this.migrationStateModel._databaseBackup.networkContainerType) {
|
||||
case NetworkContainerType.BLOB_CONTAINER:
|
||||
for (let i = 0; i < this.migrationStateModel._databaseBackup.blobs.length; i++) {
|
||||
const storageAccount = this.migrationStateModel._databaseBackup.blobs[i].storageAccount;
|
||||
this.migrationStateModel._databaseBackup.blobs[i].storageKey = (await getStorageAccountAccessKeys(
|
||||
this.migrationStateModel._azureAccount,
|
||||
this.migrationStateModel._databaseBackup.subscription,
|
||||
storageAccount)).keyName1;
|
||||
}
|
||||
break;
|
||||
case NetworkContainerType.NETWORK_SHARE:
|
||||
const storageAccount = this.migrationStateModel._databaseBackup.networkShare.storageAccount;
|
||||
|
||||
this.migrationStateModel._databaseBackup.storageKey = (await getStorageAccountAccessKeys(
|
||||
this.migrationStateModel._azureAccount,
|
||||
this.migrationStateModel._databaseBackup.subscription,
|
||||
storageAccount)).keyName1;
|
||||
this.migrationStateModel._databaseBackup.networkShare.storageKey = (await getStorageAccountAccessKeys(
|
||||
this.migrationStateModel._azureAccount,
|
||||
this.migrationStateModel._databaseBackup.subscription,
|
||||
storageAccount)).keyName1;
|
||||
break;
|
||||
}
|
||||
} finally {
|
||||
this.wizard.registerNavigationValidator((pageChangeInfo) => {
|
||||
return true;
|
||||
@@ -744,7 +878,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
protected async handleStateChange(e: StateChangeEvent): Promise<void> {
|
||||
}
|
||||
|
||||
private toggleNetworkContainerFields(containerType: NetworkContainerType): void {
|
||||
private switchNetworkContainerFields(containerType: NetworkContainerType): void {
|
||||
this.wizard.message = {
|
||||
text: '',
|
||||
level: azdata.window.MessageLevel.Error
|
||||
@@ -752,11 +886,18 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
|
||||
this.wizard.nextButton.enabled = true;
|
||||
this.migrationStateModel._databaseBackup.networkContainerType = containerType;
|
||||
this._fileShareContainer.updateCssStyles({ 'display': (containerType === NetworkContainerType.FILE_SHARE) ? 'inline' : 'none' });
|
||||
this._blobContainer.updateCssStyles({ 'display': (containerType === NetworkContainerType.BLOB_CONTAINER) ? 'inline' : 'none' });
|
||||
this._networkShareContainer.updateCssStyles({ 'display': (containerType === NetworkContainerType.NETWORK_SHARE) ? 'inline' : 'none' });
|
||||
this._networkShareStorageAccountDetails.updateCssStyles({ 'display': (containerType === NetworkContainerType.NETWORK_SHARE) ? 'inline' : 'none' });
|
||||
this._targetDatabaseContainer.updateCssStyles({ 'display': 'inline' });
|
||||
this._networkTableContainer.display = (containerType === NetworkContainerType.NETWORK_SHARE) ? 'inline' : 'none';
|
||||
this._blobTableContainer.display = (containerType === NetworkContainerType.BLOB_CONTAINER) ? 'inline' : 'none';
|
||||
|
||||
//Preserving the database Names between the 2 tables.
|
||||
this.migrationStateModel._targetDatabaseNames.forEach((v, index) => {
|
||||
this._networkShareTargetDatabaseNames[index].value = v;
|
||||
this._blobContainerTargetDatabaseNames[index].value = v;
|
||||
});
|
||||
|
||||
this._windowsUserAccountText.updateProperties({
|
||||
required: containerType === NetworkContainerType.NETWORK_SHARE
|
||||
@@ -764,11 +905,19 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
this._passwordText.updateProperties({
|
||||
required: containerType === NetworkContainerType.NETWORK_SHARE
|
||||
});
|
||||
this._sqlSourceUsernameInput.updateProperties({
|
||||
required: containerType === NetworkContainerType.NETWORK_SHARE
|
||||
});
|
||||
this._sqlSourcepassword.updateProperties({
|
||||
required: containerType === NetworkContainerType.NETWORK_SHARE
|
||||
});
|
||||
this.validateFields();
|
||||
}
|
||||
|
||||
|
||||
private async validateFields(): Promise<void> {
|
||||
await this._sqlSourceUsernameInput.validate();
|
||||
await this._sqlSourcepassword.validate();
|
||||
await this._networkSharePath.validate();
|
||||
await this._windowsUserAccountText.validate();
|
||||
await this._passwordText.validate();
|
||||
@@ -776,12 +925,13 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
await this._networkShareStorageAccountResourceGroupDropdown.validate();
|
||||
await this._networkShareContainerStorageAccountDropdown.validate();
|
||||
await this._blobContainerSubscription.validate();
|
||||
await this._blobContainerResourceGroup.validate();
|
||||
await this._blobContainerStorageAccountDropdown.validate();
|
||||
await this._blobContainerDropdown.validate();
|
||||
await this._targetDatabaseNames.forEach((inputBox) => {
|
||||
inputBox.validate();
|
||||
});
|
||||
for (let i = 0; i < this._networkShareTargetDatabaseNames.length; i++) {
|
||||
await this._networkShareTargetDatabaseNames[i].validate();
|
||||
await this._blobContainerTargetDatabaseNames[i].validate();
|
||||
await this._blobContainerResourceGroupDropdowns[i].validate();
|
||||
await this._blobContainerStorageAccountDropdowns[i].validate();
|
||||
await this._blobContainerDropdowns[i].validate();
|
||||
}
|
||||
}
|
||||
|
||||
private async getSubscriptionValues(): Promise<void> {
|
||||
@@ -823,36 +973,37 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
}
|
||||
|
||||
private async loadblobResourceGroup(): Promise<void> {
|
||||
this._blobContainerResourceGroup.loading = true;
|
||||
this._blobContainerResourceGroupDropdowns.forEach(v => v.loading = true);
|
||||
try {
|
||||
this._blobContainerResourceGroup.values = await this.migrationStateModel.getAzureResourceGroupDropdownValues(this.migrationStateModel._databaseBackup.subscription);
|
||||
const resourceGroupValues = await this.migrationStateModel.getAzureResourceGroupDropdownValues(this.migrationStateModel._databaseBackup.subscription);
|
||||
this._blobContainerResourceGroupDropdowns.forEach(v => v.values = resourceGroupValues);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
this._blobContainerResourceGroup.loading = false;
|
||||
this._blobContainerResourceGroupDropdowns.forEach(v => v.loading = false);
|
||||
}
|
||||
}
|
||||
|
||||
private async loadblobStorageDropdown(): Promise<void> {
|
||||
this._blobContainerStorageAccountDropdown.loading = true;
|
||||
private async loadblobStorageDropdown(index: number): Promise<void> {
|
||||
this._blobContainerStorageAccountDropdowns[index].loading = true;
|
||||
try {
|
||||
this._blobContainerStorageAccountDropdown.values = await this.migrationStateModel.getStorageAccountValues(this.migrationStateModel._databaseBackup.subscription, this.migrationStateModel._databaseBackup.blob.resourceGroup);
|
||||
this._blobContainerStorageAccountDropdowns[index].values = await this.migrationStateModel.getStorageAccountValues(this.migrationStateModel._databaseBackup.subscription, this.migrationStateModel._databaseBackup.blobs[index].resourceGroup);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
this._blobContainerStorageAccountDropdown.loading = false;
|
||||
this._blobContainerStorageAccountDropdowns[index].loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async loadBlobContainerDropdown(): Promise<void> {
|
||||
this._blobContainerDropdown.loading = true;
|
||||
private async loadBlobContainerDropdown(index: number): Promise<void> {
|
||||
this._blobContainerDropdowns[index].loading = true;
|
||||
try {
|
||||
const blobContainerValues = await this.migrationStateModel.getBlobContainerValues(this.migrationStateModel._databaseBackup.subscription, this.migrationStateModel._databaseBackup.blob.storageAccount);
|
||||
this._blobContainerDropdown.values = blobContainerValues;
|
||||
const blobContainerValues = await this.migrationStateModel.getBlobContainerValues(this.migrationStateModel._databaseBackup.subscription, this.migrationStateModel._databaseBackup.blobs[index].storageAccount);
|
||||
this._blobContainerDropdowns[index].values = blobContainerValues;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
this._blobContainerDropdown.loading = false;
|
||||
this._blobContainerDropdowns[index].loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -96,29 +96,25 @@ export class SummaryPage extends MigrationWizardPage {
|
||||
]
|
||||
);
|
||||
break;
|
||||
case NetworkContainerType.FILE_SHARE:
|
||||
flexContainer.addItems(
|
||||
[
|
||||
createInformationRow(this._view, constants.TYPE, constants.FILE_SHARE),
|
||||
createInformationRow(this._view, constants.SUMMARY_AZURE_STORAGE_SUBSCRIPTION, this.migrationStateModel._databaseBackup.subscription.name),
|
||||
]
|
||||
);
|
||||
break;
|
||||
case NetworkContainerType.BLOB_CONTAINER:
|
||||
flexContainer.addItems(
|
||||
[
|
||||
createInformationRow(this._view, constants.TYPE, constants.BLOB_CONTAINER),
|
||||
createInformationRow(this._view, constants.SUMMARY_AZURE_STORAGE_SUBSCRIPTION, this.migrationStateModel._databaseBackup.subscription.name),
|
||||
createInformationRow(this._view, constants.LOCATION, this.migrationStateModel._databaseBackup.blob.storageAccount.location),
|
||||
createInformationRow(this._view, constants.RESOURCE_GROUP, this.migrationStateModel._databaseBackup.blob.storageAccount.resourceGroup!),
|
||||
createInformationRow(this._view, constants.SUMMARY_AZURE_STORAGE, this.migrationStateModel._databaseBackup.blob.storageAccount.name),
|
||||
createInformationRow(this._view, constants.BLOB_CONTAINER, this.migrationStateModel._databaseBackup.blob.blobContainer.name)
|
||||
createInformationRow(this._view, constants.SUMMARY_AZURE_STORAGE_SUBSCRIPTION, this.migrationStateModel._databaseBackup.subscription.name)
|
||||
]
|
||||
);
|
||||
}
|
||||
flexContainer.addItem(createHeadingTextComponent(this._view, constants.TARGET_NAME));
|
||||
this.migrationStateModel._migrationDbs.forEach((db, index) => {
|
||||
flexContainer.addItem(createInformationRow(this._view, db, this.migrationStateModel._targetDatabaseNames[index]));
|
||||
if (this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.BLOB_CONTAINER) {
|
||||
flexContainer.addItems([
|
||||
createInformationRow(this._view, constants.LOCATION, this.migrationStateModel._databaseBackup.blobs[index].storageAccount.location),
|
||||
createInformationRow(this._view, constants.RESOURCE_GROUP, this.migrationStateModel._databaseBackup.blobs[index].storageAccount.resourceGroup!),
|
||||
createInformationRow(this._view, constants.SUMMARY_AZURE_STORAGE, this.migrationStateModel._databaseBackup.blobs[index].storageAccount.name),
|
||||
createInformationRow(this._view, constants.BLOB_CONTAINER, this.migrationStateModel._databaseBackup.blobs[index].blobContainer.name)
|
||||
]);
|
||||
}
|
||||
});
|
||||
return flexContainer;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import { AccountsSelectionPage } from './accountsSelectionPage';
|
||||
import { IntergrationRuntimePage } from './integrationRuntimePage';
|
||||
import { SummaryPage } from './summaryPage';
|
||||
import { MigrationModePage } from './migrationModePage';
|
||||
import { SqlSourceConfigurationPage } from './sqlSourceConfigurationPage';
|
||||
|
||||
export const WIZARD_INPUT_COMPONENT_WIDTH = '600px';
|
||||
export class WizardController {
|
||||
@@ -40,14 +39,12 @@ export class WizardController {
|
||||
const skuRecommendationPage = new SKURecommendationPage(wizard, stateModel);
|
||||
const migrationModePage = new MigrationModePage(wizard, stateModel);
|
||||
const azureAccountsPage = new AccountsSelectionPage(wizard, stateModel);
|
||||
const sourceConfigurationPage = new SqlSourceConfigurationPage(wizard, stateModel);
|
||||
const databaseBackupPage = new DatabaseBackupPage(wizard, stateModel);
|
||||
const integrationRuntimePage = new IntergrationRuntimePage(wizard, stateModel);
|
||||
const summaryPage = new SummaryPage(wizard, stateModel);
|
||||
|
||||
const pages: MigrationWizardPage[] = [
|
||||
azureAccountsPage,
|
||||
sourceConfigurationPage,
|
||||
skuRecommendationPage,
|
||||
migrationModePage,
|
||||
databaseBackupPage,
|
||||
|
||||
Reference in New Issue
Block a user