mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Adding database specific settings, cancel migration and other dark UI fixes (#14626)
* Made dashboard dark ui compat * foundations for sql vm * WIP * Added cancel migration Added refresh migraiton table Added multi db config * disabling cancel migration button if the migration is not in progress. * Addressing some PR based coments - Removing (s) from loc strings - Adding return type to cancel migration - removing _ from public vars - localizing strings - Adding name to dialogs for telemetry * Adding todo comment for offline mode
This commit is contained in:
@@ -16,21 +16,7 @@
|
|||||||
<stop offset="0.82" stop-color="#559CEC"/>
|
<stop offset="0.82" stop-color="#559CEC"/>
|
||||||
<stop offset="1" stop-color="#5EA0EF"/>
|
<stop offset="1" stop-color="#5EA0EF"/>
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
<linearGradient id="paint1_linear" x1="538.219" y1="282.481" x2="720.628" y2="282.481" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#32BEDD"/>
|
|
||||||
<stop offset="0.06" stop-color="#37C5E3"/>
|
|
||||||
<stop offset="0.3" stop-color="#49DDF7"/>
|
|
||||||
<stop offset="0.45" stop-color="#50E6FF"/>
|
|
||||||
<stop offset="0.55" stop-color="#50E6FF"/>
|
|
||||||
<stop offset="0.7" stop-color="#49DDF7"/>
|
|
||||||
<stop offset="0.94" stop-color="#37C5E3"/>
|
|
||||||
<stop offset="1" stop-color="#32BEDD"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="paint2_linear" x1="509" y1="403" x2="509" y2="0.000435431" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#F0F0F0"/>
|
|
||||||
<stop offset="1" stop-color="#F2F2F2" stop-opacity="0"/>
|
|
||||||
<stop offset="1" stop-color="#F2F2F2" stop-opacity="0.0572917"/>
|
|
||||||
</linearGradient>
|
|
||||||
<clipPath id="clip0">
|
<clipPath id="clip0">
|
||||||
<rect width="371" height="329" fill="white" transform="translate(443 35)"/>
|
<rect width="371" height="329" fill="white" transform="translate(443 35)"/>
|
||||||
</clipPath>
|
</clipPath>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 3.4 KiB |
3
extensions/sql-migration/images/discard.svg
Normal file
3
extensions/sql-migration/images/discard.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M8.7 7.9L15.8 15L15 15.8L7.9 8.7L0.8 15.8L0 15L7.1 7.9L0 0.8L0.8 0L7.9 7.1L15 0L15.8 0.8L8.7 7.9Z" fill="#0078D4"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 228 B |
@@ -220,6 +220,16 @@ export async function startMigrationCutover(account: azdata.Account, subscriptio
|
|||||||
return response.response.data.value;
|
return response.response.data.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function stopMigration(account: azdata.Account, subscription: Subscription, migrationStatus: DatabaseMigration): Promise<void> {
|
||||||
|
const api = await getAzureCoreAPI();
|
||||||
|
const host = `https://eastus2euap.management.azure.com`;
|
||||||
|
const path = `${migrationStatus.id}/operations/${migrationStatus.properties.migrationOperationId}/cancel?api-version=2020-09-01-preview`;
|
||||||
|
const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.POST, undefined, true, host);
|
||||||
|
if (response.errors.length > 0) {
|
||||||
|
throw new Error(response.errors.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For now only east us euap is supported. Actual API calls will be added in the public release.
|
* For now only east us euap is supported. Actual API calls will be added in the public release.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export interface IconPath {
|
|||||||
|
|
||||||
export class IconPathHelper {
|
export class IconPathHelper {
|
||||||
public static copy: IconPath;
|
public static copy: IconPath;
|
||||||
|
public static discard: IconPath;
|
||||||
public static refresh: IconPath;
|
public static refresh: IconPath;
|
||||||
public static cutover: IconPath;
|
public static cutover: IconPath;
|
||||||
public static sqlMigrationLogo: IconPath;
|
public static sqlMigrationLogo: IconPath;
|
||||||
@@ -27,6 +28,10 @@ export class IconPathHelper {
|
|||||||
light: context.asAbsolutePath('images/copy.svg'),
|
light: context.asAbsolutePath('images/copy.svg'),
|
||||||
dark: context.asAbsolutePath('images/copy.svg')
|
dark: context.asAbsolutePath('images/copy.svg')
|
||||||
};
|
};
|
||||||
|
IconPathHelper.discard = {
|
||||||
|
light: context.asAbsolutePath('images/discard.svg'),
|
||||||
|
dark: context.asAbsolutePath('images/discard.svg')
|
||||||
|
};
|
||||||
IconPathHelper.refresh = {
|
IconPathHelper.refresh = {
|
||||||
light: context.asAbsolutePath('images/refresh.svg'),
|
light: context.asAbsolutePath('images/refresh.svg'),
|
||||||
dark: context.asAbsolutePath('images/refresh.svg')
|
dark: context.asAbsolutePath('images/refresh.svg')
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export const DATABASE_BACKUP_NETWORK_SHARE_LOCATION_LABEL = localize('sql.migrat
|
|||||||
export const DATABASE_BACKUP_NETWORK_SHARE_WINDOWS_USER_LABEL = localize('sql.migration.network.share.windows.user.label', "Windows user account with read access to the network share location.");
|
export const DATABASE_BACKUP_NETWORK_SHARE_WINDOWS_USER_LABEL = localize('sql.migration.network.share.windows.user.label', "Windows user account with read access to the network share location.");
|
||||||
export const DATABASE_BACKUP_NETWORK_SHARE_PASSWORD_LABEL = localize('sql.migration.network.share.password.label', "Password");
|
export const DATABASE_BACKUP_NETWORK_SHARE_PASSWORD_LABEL = localize('sql.migration.network.share.password.label', "Password");
|
||||||
export const DATABASE_BACKUP_NETWORK_SHARE_PASSWORD_PLACEHOLDER = localize('sql.migration.network.share.password.placeholder', "Enter password");
|
export const DATABASE_BACKUP_NETWORK_SHARE_PASSWORD_PLACEHOLDER = localize('sql.migration.network.share.password.placeholder', "Enter password");
|
||||||
export const DATABASE_BACKUP_NETWORK_SHARE_AZURE_ACCOUNT_HELP = localize('sql.migration.network.share.azure.help', "Enter Azure storage account information where the backup will be copied");
|
export const DATABASE_BACKUP_NETWORK_SHARE_AZURE_ACCOUNT_HELP = localize('sql.migration.network.share.azure.help', "Select the storage account where backup files will be copied to during migration");
|
||||||
export const DATABASE_BACKUP_NETWORK_SHARE_SUBSCRIPTION_LABEL = localize('sql.migration.network.share.subscription.label', "Select the subscription that contains the storage account.");
|
export const DATABASE_BACKUP_NETWORK_SHARE_SUBSCRIPTION_LABEL = localize('sql.migration.network.share.subscription.label', "Select the subscription that contains the storage account.");
|
||||||
export const DATABASE_BACKUP_SUBSCRIPTION_PLACEHOLDER = localize('sql.migration.network.share.subscription.placeholder', "Select subscription");
|
export const DATABASE_BACKUP_SUBSCRIPTION_PLACEHOLDER = localize('sql.migration.network.share.subscription.placeholder', "Select subscription");
|
||||||
export const DATABASE_BACKUP_NETWORK_SHARE_NETWORK_STORAGE_ACCOUNT_LABEL = localize('sql.migration.network.share.storage.account.label', "Select the storage account where backup files will be copied.");
|
export const DATABASE_BACKUP_NETWORK_SHARE_NETWORK_STORAGE_ACCOUNT_LABEL = localize('sql.migration.network.share.storage.account.label', "Select the storage account where backup files will be copied.");
|
||||||
@@ -87,7 +87,21 @@ export const INVALID_FILESHARE_ERROR = localize('sql.migration.invalid.fileShare
|
|||||||
export const INVALID_BLOBCONTAINER_ERROR = localize('sql.migration.invalid.blobContainer.error', "Please select a valid blob container to proceed.");
|
export const INVALID_BLOBCONTAINER_ERROR = localize('sql.migration.invalid.blobContainer.error', "Please select a valid blob container to proceed.");
|
||||||
export const INVALID_NETWORK_SHARE_LOCATION = localize('sql.migration.invalid.network.share.location', "Invalid network share location format. Example: {0}", '\\\\Servername.domainname.com\\Backupfolder');
|
export const INVALID_NETWORK_SHARE_LOCATION = localize('sql.migration.invalid.network.share.location', "Invalid network share location format. Example: {0}", '\\\\Servername.domainname.com\\Backupfolder');
|
||||||
export const INVALID_USER_ACCOUNT = localize('sql.migration.invalid.user.account', "Invalid user account format. Example: {0}", 'Domain\\username');
|
export const INVALID_USER_ACCOUNT = localize('sql.migration.invalid.user.account', "Invalid user account format. Example: {0}", 'Domain\\username');
|
||||||
|
export function TARGET_NAME_FOR_DATABASE(dbName: string): string {
|
||||||
|
return localize('sql.migration.target.name.for.database', 'Target name for database ‘{0}’', dbName);
|
||||||
|
}
|
||||||
|
export function TARGET_NETWORK_SHARE_LOCATION(dbName: string): string {
|
||||||
|
return localize('sql.migration.network.share.location', "Network share location to read backups for database ‘{0}’", dbName);
|
||||||
|
}
|
||||||
|
export function TARGET_FILE_SHARE(dbName: string): string {
|
||||||
|
return localize('sql.migration.file.share', "Select the file share that contains the backup files for ‘{0}’", dbName);
|
||||||
|
}
|
||||||
|
export function TARGET_BLOB_CONTAINER(dbName: string): string {
|
||||||
|
return localize('sql.migration.blob.container', "Select the container that contains the backup files for ‘{0}’", dbName);
|
||||||
|
}
|
||||||
|
export const ENTER_NETWORK_SHARE_INFORMATION = localize('sql.migration.enter.network.share.information', "Enter network share path information for selected databases");
|
||||||
|
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");
|
||||||
|
|
||||||
// integration runtime page
|
// integration runtime page
|
||||||
export const IR_PAGE_TITLE = localize('sql.migration.ir.page.title', "Migration Controller");
|
export const IR_PAGE_TITLE = localize('sql.migration.ir.page.title', "Migration Controller");
|
||||||
@@ -168,8 +182,8 @@ export const SUMMARY_AZURE_STORAGE_SUBSCRIPTION = localize('sql.migration.summar
|
|||||||
export const SUMMARY_AZURE_STORAGE = localize('sql.migration.summary.azure.storage', "Azure storage");
|
export const SUMMARY_AZURE_STORAGE = localize('sql.migration.summary.azure.storage', "Azure storage");
|
||||||
export const SUMMARY_IR_NODE = localize('sql.migration.ir.node', "Integration Runtime node");
|
export const SUMMARY_IR_NODE = localize('sql.migration.ir.node', "Integration Runtime node");
|
||||||
export const NETWORK_SHARE = localize('sql.migration.network.share', "Network Share");
|
export const NETWORK_SHARE = localize('sql.migration.network.share', "Network Share");
|
||||||
export const BLOB_CONTAINER = localize('sql.migration.blob.container', "Blob Container");
|
export const BLOB_CONTAINER = localize('sql.migration.blob.container.title', "Blob Container");
|
||||||
export const FILE_SHARE = localize('sql.migration.file.share', "File Share");
|
export const FILE_SHARE = localize('sql.migration.file.share.title', "File Share");
|
||||||
export const MIGRATION_STARTED = localize('sql.migration.started.notification', "Migration in progress");
|
export const MIGRATION_STARTED = localize('sql.migration.started.notification', "Migration in progress");
|
||||||
|
|
||||||
// Open notebook quick pick string
|
// Open notebook quick pick string
|
||||||
@@ -184,8 +198,8 @@ export const DASHBOARD_DESCRIPTION = localize('sql.migration.dashboard.descripti
|
|||||||
export const DASHBOARD_MIGRATE_TASK_BUTTON_TITLE = localize('sql.migration.dashboard.migrate.task.button', "Migrate to Azure SQL");
|
export const DASHBOARD_MIGRATE_TASK_BUTTON_TITLE = localize('sql.migration.dashboard.migrate.task.button', "Migrate to Azure SQL");
|
||||||
export const DASHBOARD_MIGRATE_TASK_BUTTON_DESCRIPTION = localize('sql.migration.dashboard.migrate.task.button.description', "Migrate SQL Server instance to Azure SQL.");
|
export const DASHBOARD_MIGRATE_TASK_BUTTON_DESCRIPTION = localize('sql.migration.dashboard.migrate.task.button.description', "Migrate SQL Server instance to Azure SQL.");
|
||||||
export const DATABASE_MIGRATION_STATUS = localize('sql.migration.database.migration.status', "Database Migration Status");
|
export const DATABASE_MIGRATION_STATUS = localize('sql.migration.database.migration.status', "Database Migration Status");
|
||||||
export const HELP_VIDEO1_TITLE = localize('sql.migration.dashboard.video1.title', "Migrate to SQL Server to SQL Managed Instance");
|
export const HELP_VIDEO1_TITLE = localize('sql.migration.dashboard.video1.title', "Migrate SQL Server to SQL Managed Instance");
|
||||||
export const HELP_VIDEO2_TITLE = localize('sql.migration.dashboard.video2.title', "Migrate to SQL Server to SQL Virtual Machine");
|
export const HELP_VIDEO2_TITLE = localize('sql.migration.dashboard.video2.title', "Migrate SQL Server to SQL Virtual Machine");
|
||||||
export const HELP_LINK1_TITLE = localize('sql.migration.dashboard.link1.title', "Migrating your SQL Server to cloud");
|
export const HELP_LINK1_TITLE = localize('sql.migration.dashboard.link1.title', "Migrating your SQL Server to cloud");
|
||||||
export const HELP_LINK1_DESCRIPTION = localize('sql.migration.dashboard.link1.description', "Lorem ipsum dolor sit amet, consectetur adipi. Lorem ipsum dolor sit amet, consectetur adipi. Lorem ipsum.");
|
export const HELP_LINK1_DESCRIPTION = localize('sql.migration.dashboard.link1.description', "Lorem ipsum dolor sit amet, consectetur adipi. Lorem ipsum dolor sit amet, consectetur adipi. Lorem ipsum.");
|
||||||
export const HELP_TITLE = localize('sql.migration.dashboard.help.title', "Help Articles and Video Links");
|
export const HELP_TITLE = localize('sql.migration.dashboard.help.title', "Help Articles and Video Links");
|
||||||
@@ -212,11 +226,11 @@ export const SOURCE_VERSION = localize('sql.migration.source.version', "Source v
|
|||||||
export const TARGET_SERVER = localize('sql.migration.target.server', "Target server");
|
export const TARGET_SERVER = localize('sql.migration.target.server', "Target server");
|
||||||
export const TARGET_VERSION = localize('sql.migration.target.version', "Target version");
|
export const TARGET_VERSION = localize('sql.migration.target.version', "Target version");
|
||||||
export const MIGRATION_STATUS = localize('sql.migration.migration.status', "Migration status");
|
export const MIGRATION_STATUS = localize('sql.migration.migration.status', "Migration status");
|
||||||
export const FULL_BACKUP_FILES = localize('sql.migration.full.backup.files', "Full backup files(s)");
|
export const FULL_BACKUP_FILES = localize('sql.migration.full.backup.files', "Full backup files");
|
||||||
export const LAST_APPLIED_LSN = localize('sql.migration.last.applied.lsn', "Last applied LSN");
|
export const LAST_APPLIED_LSN = localize('sql.migration.last.applied.lsn', "Last applied LSN");
|
||||||
export const LAST_APPLIED_BACKUP_FILES = localize('sql.migration.last.applied.backup.files', "Last applied backup file(s)");
|
export const LAST_APPLIED_BACKUP_FILES = localize('sql.migration.last.applied.backup.files', "Last applied backup files");
|
||||||
export const LAST_APPLIED_BACKUP_FILES_TAKEN_ON = localize('sql.migration.last.applied.files.taken.on', "Last applied backup file(s) taken on");
|
export const LAST_APPLIED_BACKUP_FILES_TAKEN_ON = localize('sql.migration.last.applied.files.taken.on', "Last applied backup files taken on");
|
||||||
export const ACTIVE_BACKUP_FILES = localize('sql.migration.active.backup.files', "Active Backup file(s)");
|
export const ACTIVE_BACKUP_FILES = localize('sql.migration.active.backup.files', "Active Backup files");
|
||||||
export const STATUS = localize('sql.migration.status', "Status");
|
export const STATUS = localize('sql.migration.status', "Status");
|
||||||
export const BACKUP_START_TIME = localize('sql.migration.backup.start.time', "Backup start time");
|
export const BACKUP_START_TIME = localize('sql.migration.backup.start.time', "Backup start time");
|
||||||
export const FIRST_LSN = localize('sql.migration.first.lsn', "First LSN");
|
export const FIRST_LSN = localize('sql.migration.first.lsn', "First LSN");
|
||||||
@@ -224,7 +238,7 @@ export const LAST_LSN = localize('sql.migration.last.LSN', "Last LSN");
|
|||||||
export const CANNOT_START_CUTOVER_ERROR = localize('sql.migration.cannot.start.cutover.error', "Cannot start the cutover process until all the migrations are done. Click refresh to fetch the latest file status");
|
export const CANNOT_START_CUTOVER_ERROR = localize('sql.migration.cannot.start.cutover.error', "Cannot start the cutover process until all the migrations are done. Click refresh to fetch the latest file status");
|
||||||
export const AZURE_SQL_DATABASE_MANAGED_INSTANCE = localize('sql.migration.azure.sql.database.managed.instance', "Azure SQL Database Managed Instance");
|
export const AZURE_SQL_DATABASE_MANAGED_INSTANCE = localize('sql.migration.azure.sql.database.managed.instance', "Azure SQL Database Managed Instance");
|
||||||
export const AZURE_SQL_DATABASE_VIRTUAL_MACHINE = localize('sql.migration.azure.sql.database.virtual.machine', "Azure SQL Database Virtual Machine");
|
export const AZURE_SQL_DATABASE_VIRTUAL_MACHINE = localize('sql.migration.azure.sql.database.virtual.machine', "Azure SQL Database Virtual Machine");
|
||||||
|
export const CANCEL_MIGRATION = localize('sql.migration.cancel.migration', "Cancel migration");
|
||||||
export function ACTIVE_BACKUP_FILES_ITEMS(fileCount: number) {
|
export function ACTIVE_BACKUP_FILES_ITEMS(fileCount: number) {
|
||||||
if (fileCount === 1) {
|
if (fileCount === 1) {
|
||||||
return localize('sql.migration.active.backup.files.items', "Active Backup files (1 item)");
|
return localize('sql.migration.active.backup.files.items', "Active Backup files (1 item)");
|
||||||
|
|||||||
@@ -184,6 +184,9 @@ export class DashboardWidget {
|
|||||||
label: taskMetaData.title,
|
label: taskMetaData.title,
|
||||||
title: taskMetaData.title,
|
title: taskMetaData.title,
|
||||||
width: maxWidth,
|
width: maxWidth,
|
||||||
|
CSSStyles: {
|
||||||
|
'border': '1px solid'
|
||||||
|
}
|
||||||
}).component();
|
}).component();
|
||||||
buttonContainer.onDidClick(async () => {
|
buttonContainer.onDidClick(async () => {
|
||||||
if (taskMetaData.command) {
|
if (taskMetaData.command) {
|
||||||
@@ -267,12 +270,17 @@ export class DashboardWidget {
|
|||||||
const localMigrations = MigrationLocalStorage.getMigrationsBySourceConnections(currentConnection);
|
const localMigrations = MigrationLocalStorage.getMigrationsBySourceConnections(currentConnection);
|
||||||
for (let i = 0; i < localMigrations.length; i++) {
|
for (let i = 0; i < localMigrations.length; i++) {
|
||||||
const localMigration = localMigrations[i];
|
const localMigration = localMigrations[i];
|
||||||
localMigration.migrationContext = await getDatabaseMigration(
|
try {
|
||||||
localMigration.azureAccount,
|
localMigration.migrationContext = await getDatabaseMigration(
|
||||||
localMigration.subscription,
|
localMigration.azureAccount,
|
||||||
localMigration.targetManagedInstance.location,
|
localMigration.subscription,
|
||||||
localMigration.migrationContext.id
|
localMigration.targetManagedInstance.location,
|
||||||
);
|
localMigration.migrationContext.id
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
|
||||||
localMigration.sourceConnectionProfile = currentConnection;
|
localMigration.sourceConnectionProfile = currentConnection;
|
||||||
}
|
}
|
||||||
return localMigrations;
|
return localMigrations;
|
||||||
@@ -329,7 +337,7 @@ export class DashboardWidget {
|
|||||||
'width': '400px',
|
'width': '400px',
|
||||||
'height': '50px',
|
'height': '50px',
|
||||||
'margin-top': '10px',
|
'margin-top': '10px',
|
||||||
'box-shadow': '0 1px 2px 0 rgba(0,0,0,0.2)'
|
'border': '1px solid'
|
||||||
}
|
}
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
@@ -389,7 +397,7 @@ export class DashboardWidget {
|
|||||||
justifyContent: 'flex-start',
|
justifyContent: 'flex-start',
|
||||||
}).withProps({
|
}).withProps({
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
'border': '1px solid rgba(0, 0, 0, 0.1)',
|
'border': '1px solid',
|
||||||
'padding': '15px'
|
'padding': '15px'
|
||||||
}
|
}
|
||||||
}).component();
|
}).component();
|
||||||
@@ -436,7 +444,7 @@ export class DashboardWidget {
|
|||||||
buttonContainer.addItem(viewAllButton, {
|
buttonContainer.addItem(viewAllButton, {
|
||||||
flex: 'auto',
|
flex: 'auto',
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
'border-right': '1px solid rgba(0, 0, 0, 0.7)',
|
'border-right': '1px solid ',
|
||||||
'width': '40px',
|
'width': '40px',
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -492,7 +500,7 @@ export class DashboardWidget {
|
|||||||
justifyContent: 'flex-start',
|
justifyContent: 'flex-start',
|
||||||
}).withProps({
|
}).withProps({
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
'border': '1px solid rgba(0, 0, 0, 0.1)',
|
'border': '1px solid',
|
||||||
'padding': '15px'
|
'padding': '15px'
|
||||||
}
|
}
|
||||||
}).component();
|
}).component();
|
||||||
@@ -518,7 +526,7 @@ export class DashboardWidget {
|
|||||||
const links = [{
|
const links = [{
|
||||||
title: loc.HELP_LINK1_TITLE,
|
title: loc.HELP_LINK1_TITLE,
|
||||||
description: loc.HELP_LINK1_DESCRIPTION,
|
description: loc.HELP_LINK1_DESCRIPTION,
|
||||||
link: 'www.microsoft.com' //TODO: add proper link over here.
|
link: 'https://www.microsoft.com' //TODO: add proper link over here.
|
||||||
}];
|
}];
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ export class AssessmentResultsDialog {
|
|||||||
protected async execute() {
|
protected async execute() {
|
||||||
this.model._migrationDbs = this._tree.selectedDbs();
|
this.model._migrationDbs = this._tree.selectedDbs();
|
||||||
this.skuRecommendationPage.refreshDatabaseCount(this._model._migrationDbs.length);
|
this.skuRecommendationPage.refreshDatabaseCount(this._model._migrationDbs.length);
|
||||||
|
this.model.refreshDatabaseBackupPage = true;
|
||||||
this._isOpen = false;
|
this._isOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export class MigrationCutoverDialog {
|
|||||||
private _databaseTitleName!: azdata.TextComponent;
|
private _databaseTitleName!: azdata.TextComponent;
|
||||||
private _databaseCutoverButton!: azdata.ButtonComponent;
|
private _databaseCutoverButton!: azdata.ButtonComponent;
|
||||||
private _refresh!: azdata.ButtonComponent;
|
private _refresh!: azdata.ButtonComponent;
|
||||||
|
private _cancel!: azdata.ButtonComponent;
|
||||||
|
|
||||||
private _serverName!: azdata.TextComponent;
|
private _serverName!: azdata.TextComponent;
|
||||||
private _serverVersion!: azdata.TextComponent;
|
private _serverVersion!: azdata.TextComponent;
|
||||||
@@ -290,6 +291,27 @@ export class MigrationCutoverDialog {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._cancel = this._view.modelBuilder.button().withProps({
|
||||||
|
iconPath: IconPathHelper.discard,
|
||||||
|
iconHeight: '16px',
|
||||||
|
iconWidth: '16px',
|
||||||
|
label: loc.CANCEL_MIGRATION,
|
||||||
|
height: '55px',
|
||||||
|
width: '130px'
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
this._cancel.onDidClick((e) => {
|
||||||
|
this.cancelMigration();
|
||||||
|
});
|
||||||
|
|
||||||
|
header.addItem(this._cancel, {
|
||||||
|
flex: '0',
|
||||||
|
CSSStyles: {
|
||||||
|
'width': '130px'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
this._refresh = this._view.modelBuilder.button().withProps({
|
this._refresh = this._view.modelBuilder.button().withProps({
|
||||||
iconPath: IconPathHelper.refresh,
|
iconPath: IconPathHelper.refresh,
|
||||||
iconHeight: '16px',
|
iconHeight: '16px',
|
||||||
@@ -392,6 +414,7 @@ export class MigrationCutoverDialog {
|
|||||||
this._databaseCutoverButton.enabled = true;
|
this._databaseCutoverButton.enabled = true;
|
||||||
} else {
|
} else {
|
||||||
this._databaseCutoverButton.enabled = false;
|
this._databaseCutoverButton.enabled = false;
|
||||||
|
this._cancel.enabled = false;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
@@ -419,7 +442,10 @@ export class MigrationCutoverDialog {
|
|||||||
value: value,
|
value: value,
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
'margin-top': '5px',
|
'margin-top': '5px',
|
||||||
'margin-bottom': '0'
|
'margin-bottom': '0',
|
||||||
|
'width': '100%',
|
||||||
|
'overflow': 'hidden',
|
||||||
|
'text-overflow': 'ellipses'
|
||||||
}
|
}
|
||||||
}).component();
|
}).component();
|
||||||
flexContainer.addItem(textComponent);
|
flexContainer.addItem(textComponent);
|
||||||
@@ -428,6 +454,11 @@ export class MigrationCutoverDialog {
|
|||||||
text: textComponent
|
text: textComponent
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async cancelMigration(): Promise<void> {
|
||||||
|
await this._model.cancelMigration();
|
||||||
|
await this.refreshStatus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ActiveBackupFileSchema {
|
interface ActiveBackupFileSchema {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { getMigrationStatus, DatabaseMigration, startMigrationCutover } from '../../api/azure';
|
import { getMigrationStatus, DatabaseMigration, startMigrationCutover, stopMigration } from '../../api/azure';
|
||||||
import { MigrationContext } from '../../models/migrationLocalStorage';
|
import { MigrationContext } from '../../models/migrationLocalStorage';
|
||||||
|
|
||||||
|
|
||||||
@@ -36,4 +36,19 @@ export class MigrationCutoverDialogModel {
|
|||||||
}
|
}
|
||||||
return undefined!;
|
return undefined!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async cancelMigration(): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (this.migrationStatus) {
|
||||||
|
await stopMigration(
|
||||||
|
this._migration.azureAccount,
|
||||||
|
this._migration.subscription,
|
||||||
|
this.migrationStatus
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
return undefined!;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { MigrationContext } from '../../models/migrationLocalStorage';
|
|||||||
import { MigrationCutoverDialog } from '../migrationCutover/migrationCutoverDialog';
|
import { MigrationCutoverDialog } from '../migrationCutover/migrationCutoverDialog';
|
||||||
import { MigrationCategory, MigrationStatusDialogModel } from './migrationStatusDialogModel';
|
import { MigrationCategory, MigrationStatusDialogModel } from './migrationStatusDialogModel';
|
||||||
import * as loc from '../../constants/strings';
|
import * as loc from '../../constants/strings';
|
||||||
|
import { getDatabaseMigration } from '../../api/azure';
|
||||||
export class MigrationStatusDialog {
|
export class MigrationStatusDialog {
|
||||||
private _model: MigrationStatusDialogModel;
|
private _model: MigrationStatusDialogModel;
|
||||||
private _dialogObject!: azdata.window.Dialog;
|
private _dialogObject!: azdata.window.Dialog;
|
||||||
@@ -84,6 +85,10 @@ export class MigrationStatusDialog {
|
|||||||
label: 'Refresh',
|
label: 'Refresh',
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
|
this._refresh.onDidClick((e) => {
|
||||||
|
this.refreshTable();
|
||||||
|
});
|
||||||
|
|
||||||
const flexContainer = this._view.modelBuilder.flexContainer().component();
|
const flexContainer = this._view.modelBuilder.flexContainer().component();
|
||||||
|
|
||||||
flexContainer.addItem(this._searchBox, {
|
flexContainer.addItem(this._searchBox, {
|
||||||
@@ -136,7 +141,7 @@ export class MigrationStatusDialog {
|
|||||||
height: '20px'
|
height: '20px'
|
||||||
}).component();
|
}).component();
|
||||||
const sqlMigrationName = this._view.modelBuilder.hyperlink().withProps({
|
const sqlMigrationName = this._view.modelBuilder.hyperlink().withProps({
|
||||||
label: migration.migrationContext.name,
|
label: migration.targetManagedInstance.name,
|
||||||
url: ''
|
url: ''
|
||||||
}).component();
|
}).component();
|
||||||
sqlMigrationName.onDidClick((e) => {
|
sqlMigrationName.onDidClick((e) => {
|
||||||
@@ -184,6 +189,19 @@ export class MigrationStatusDialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private refreshTable(): void {
|
||||||
|
this._model._migrations.forEach(async (migration) => {
|
||||||
|
migration.migrationContext = await getDatabaseMigration(
|
||||||
|
migration.azureAccount,
|
||||||
|
migration.subscription,
|
||||||
|
migration.targetManagedInstance.location,
|
||||||
|
migration.migrationContext.id
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.populateMigrationTable();
|
||||||
|
}
|
||||||
|
|
||||||
private createStatusTable(): azdata.DeclarativeTableComponent {
|
private createStatusTable(): azdata.DeclarativeTableComponent {
|
||||||
this._statusTable = this._view.modelBuilder.declarativeTable().withProps({
|
this._statusTable = this._view.modelBuilder.declarativeTable().withProps({
|
||||||
columns: [
|
columns: [
|
||||||
|
|||||||
@@ -52,15 +52,15 @@ export interface NetworkShare {
|
|||||||
export interface DatabaseBackupModel {
|
export interface DatabaseBackupModel {
|
||||||
migrationCutover: MigrationCutover;
|
migrationCutover: MigrationCutover;
|
||||||
networkContainerType: NetworkContainerType;
|
networkContainerType: NetworkContainerType;
|
||||||
networkShareLocation: string;
|
networkShareLocations: string[];
|
||||||
windowsUser: string;
|
windowsUser: string;
|
||||||
password: string;
|
password: string;
|
||||||
subscription: azureResource.AzureResourceSubscription;
|
subscription: azureResource.AzureResourceSubscription;
|
||||||
storageAccount: StorageAccount;
|
storageAccount: StorageAccount;
|
||||||
storageKey: string;
|
storageKey: string;
|
||||||
azureSecurityToken: string;
|
azureSecurityToken: string;
|
||||||
fileShare: azureResource.FileShare;
|
fileShares: azureResource.FileShare[];
|
||||||
blobContainer: azureResource.BlobContainer;
|
blobContainers: azureResource.BlobContainer[];
|
||||||
}
|
}
|
||||||
export interface Model {
|
export interface Model {
|
||||||
readonly sourceConnectionId: string;
|
readonly sourceConnectionId: string;
|
||||||
@@ -91,6 +91,8 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
public _storageAccounts!: StorageAccount[];
|
public _storageAccounts!: StorageAccount[];
|
||||||
public _fileShares!: azureResource.FileShare[];
|
public _fileShares!: azureResource.FileShare[];
|
||||||
public _blobContainers!: azureResource.BlobContainer[];
|
public _blobContainers!: azureResource.BlobContainer[];
|
||||||
|
public _refreshNetworkShareLocation!: azureResource.BlobContainer[];
|
||||||
|
public _targetDatabaseNames!: string[];
|
||||||
|
|
||||||
public _migrationController!: SqlMigrationController;
|
public _migrationController!: SqlMigrationController;
|
||||||
public _migrationControllers!: SqlMigrationController[];
|
public _migrationControllers!: SqlMigrationController[];
|
||||||
@@ -102,7 +104,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
private _skuRecommendations: SKURecommendations | undefined;
|
private _skuRecommendations: SKURecommendations | undefined;
|
||||||
private _assessmentResults: mssql.SqlMigrationAssessmentResultItem[] | undefined;
|
private _assessmentResults: mssql.SqlMigrationAssessmentResultItem[] | undefined;
|
||||||
|
|
||||||
|
public refreshDatabaseBackupPage!: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _extensionContext: vscode.ExtensionContext,
|
private readonly _extensionContext: vscode.ExtensionContext,
|
||||||
@@ -467,7 +469,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
},
|
},
|
||||||
SourceLocation: {
|
SourceLocation: {
|
||||||
FileShare: {
|
FileShare: {
|
||||||
Path: this._databaseBackup.networkShareLocation,
|
Path: '',
|
||||||
Username: this._databaseBackup.windowsUser,
|
Username: this._databaseBackup.windowsUser,
|
||||||
Password: this._databaseBackup.password,
|
Password: this._databaseBackup.password,
|
||||||
}
|
}
|
||||||
@@ -482,19 +484,19 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this._migrationDbs.forEach(async (db) => {
|
this._migrationDbs.forEach(async (db, index) => {
|
||||||
|
|
||||||
requestBody.properties.SourceDatabaseName = db;
|
requestBody.properties.SourceDatabaseName = db;
|
||||||
try {
|
try {
|
||||||
|
requestBody.properties.BackupConfiguration.SourceLocation.FileShare.Path = this._databaseBackup.networkShareLocations[index];
|
||||||
const response = await startDatabaseMigration(
|
const response = await startDatabaseMigration(
|
||||||
this._azureAccount,
|
this._azureAccount,
|
||||||
this._targetSubscription,
|
this._targetSubscription,
|
||||||
this._migrationController?.properties.location!,
|
this._migrationController?.properties.location!,
|
||||||
this._targetServerInstance,
|
this._targetServerInstance,
|
||||||
db,
|
this._targetDatabaseNames[index],
|
||||||
requestBody
|
requestBody
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.status === 201) {
|
if (response.status === 201) {
|
||||||
MigrationLocalStorage.saveMigration(
|
MigrationLocalStorage.saveMigration(
|
||||||
currentConnection!,
|
currentConnection!,
|
||||||
|
|||||||
@@ -7,27 +7,31 @@ import * as azdata from 'azdata';
|
|||||||
import { EOL } from 'os';
|
import { EOL } from 'os';
|
||||||
import { getStorageAccountAccessKeys } from '../api/azure';
|
import { getStorageAccountAccessKeys } from '../api/azure';
|
||||||
import { MigrationWizardPage } from '../models/migrationWizardPage';
|
import { MigrationWizardPage } from '../models/migrationWizardPage';
|
||||||
import { MigrationCutover, MigrationStateModel, NetworkContainerType, StateChangeEvent } from '../models/stateMachine';
|
import { MigrationStateModel, NetworkContainerType, StateChangeEvent } from '../models/stateMachine';
|
||||||
import * as constants from '../constants/strings';
|
import * as constants from '../constants/strings';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
export class DatabaseBackupPage extends MigrationWizardPage {
|
export class DatabaseBackupPage extends MigrationWizardPage {
|
||||||
|
private _view!: azdata.ModelView;
|
||||||
|
|
||||||
private _networkShareContainer!: azdata.FlexContainer;
|
private _networkShareContainer!: azdata.FlexContainer;
|
||||||
private _networkShareContainerSubscriptionDropdown!: azdata.DropDownComponent;
|
private _networkShareContainerSubscriptionDropdown!: azdata.DropDownComponent;
|
||||||
private _networkShareContainerStorageAccountDropdown!: azdata.DropDownComponent;
|
private _networkShareContainerStorageAccountDropdown!: azdata.DropDownComponent;
|
||||||
private _networkShareLocationText!: azdata.InputBoxComponent;
|
|
||||||
private _windowsUserAccountText!: azdata.InputBoxComponent;
|
private _windowsUserAccountText!: azdata.InputBoxComponent;
|
||||||
private _passwordText!: azdata.InputBoxComponent;
|
private _passwordText!: azdata.InputBoxComponent;
|
||||||
|
private _networkShareDatabaseConfigContainer!: azdata.FlexContainer;
|
||||||
|
private _networkShareLocations!: azdata.InputBoxComponent[];
|
||||||
|
|
||||||
private _blobContainer!: azdata.FlexContainer;
|
private _blobContainer!: azdata.FlexContainer;
|
||||||
private _blobContainerSubscriptionDropdown!: azdata.DropDownComponent;
|
private _blobContainerSubscriptionDropdown!: azdata.DropDownComponent;
|
||||||
private _blobContainerStorageAccountDropdown!: azdata.DropDownComponent;
|
private _blobContainerStorageAccountDropdown!: azdata.DropDownComponent;
|
||||||
private _blobContainerBlobDropdown!: azdata.DropDownComponent;
|
private _blobContainerDatabaseConfigContainer!: azdata.FlexContainer;
|
||||||
|
private _blobContainerDropdowns!: azdata.DropDownComponent[];
|
||||||
|
|
||||||
private _fileShareContainer!: azdata.FlexContainer;
|
private _fileShareContainer!: azdata.FlexContainer;
|
||||||
private _fileShareSubscriptionDropdown!: azdata.DropDownComponent;
|
private _fileShareSubscriptionDropdown!: azdata.DropDownComponent;
|
||||||
private _fileShareStorageAccountDropdown!: azdata.DropDownComponent;
|
private _fileShareStorageAccountDropdown!: azdata.DropDownComponent;
|
||||||
private _fileShareFileShareDropdown!: azdata.DropDownComponent;
|
private _fileShareDatabaseConfigContainer!: azdata.FlexContainer;
|
||||||
|
private _fileShareDropdowns!: azdata.DropDownComponent[];
|
||||||
|
|
||||||
constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) {
|
constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) {
|
||||||
super(wizard, azdata.window.createWizardPage(constants.DATABASE_BACKUP_PAGE_TITLE), migrationStateModel);
|
super(wizard, azdata.window.createWizardPage(constants.DATABASE_BACKUP_PAGE_TITLE), migrationStateModel);
|
||||||
@@ -35,7 +39,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async registerContent(view: azdata.ModelView): Promise<void> {
|
protected async registerContent(view: azdata.ModelView): Promise<void> {
|
||||||
|
this._view = view;
|
||||||
this._networkShareContainer = this.createNetworkShareContainer(view);
|
this._networkShareContainer = this.createNetworkShareContainer(view);
|
||||||
this._blobContainer = this.createBlobContainer(view);
|
this._blobContainer = this.createBlobContainer(view);
|
||||||
this._fileShareContainer = this.createFileShareContainer(view);
|
this._fileShareContainer = this.createFileShareContainer(view);
|
||||||
@@ -56,7 +60,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
title: '',
|
title: '',
|
||||||
component: networkContainer
|
component: networkContainer
|
||||||
},
|
},
|
||||||
this.migrationModeContainer(view),
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
await view.initializeModel(form.component());
|
await view.initializeModel(form.component());
|
||||||
@@ -68,7 +71,8 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
const networkShareButton = view.modelBuilder.radioButton()
|
const networkShareButton = view.modelBuilder.radioButton()
|
||||||
.withProps({
|
.withProps({
|
||||||
name: buttonGroup,
|
name: buttonGroup,
|
||||||
label: constants.DATABASE_BACKUP_NC_NETWORK_SHARE_RADIO_LABEL
|
label: constants.DATABASE_BACKUP_NC_NETWORK_SHARE_RADIO_LABEL,
|
||||||
|
checked: true
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
networkShareButton.onDidChangeCheckedState((e) => {
|
networkShareButton.onDidChangeCheckedState((e) => {
|
||||||
@@ -150,26 +154,19 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
this._fileShareStorageAccountDropdown.onValueChanged(async (value) => {
|
this._fileShareStorageAccountDropdown.onValueChanged(async (value) => {
|
||||||
if (value.selected) {
|
if (value.selected) {
|
||||||
this.migrationStateModel._databaseBackup.storageAccount = this.migrationStateModel.getStorageAccount(value.index);
|
this.migrationStateModel._databaseBackup.storageAccount = this.migrationStateModel.getStorageAccount(value.index);
|
||||||
this.migrationStateModel._databaseBackup.fileShare = undefined!;
|
this.migrationStateModel._databaseBackup.fileShares = undefined!;
|
||||||
await this.loadFileShareDropdown();
|
await this.loadFileShareDropdown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const fileShareLabel = view.modelBuilder.text()
|
|
||||||
.withProps({
|
|
||||||
value: constants.DATABASE_BACKUP_FILE_SHARE_LABEL,
|
|
||||||
requiredIndicator: true,
|
|
||||||
}).component();
|
|
||||||
this._fileShareFileShareDropdown = view.modelBuilder.dropDown()
|
|
||||||
.withProps({
|
|
||||||
required: true
|
|
||||||
}).component();
|
|
||||||
this._fileShareFileShareDropdown.onValueChanged((value) => {
|
|
||||||
if (value.selected) {
|
|
||||||
this.migrationStateModel._databaseBackup.fileShare = this.migrationStateModel.getFileShare(value.index);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
const fileShareDatabaseConfigHeader = view.modelBuilder.text().withProps({
|
||||||
|
value: constants.ENTER_FILE_SHARE_INFORMATION
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
this._fileShareDatabaseConfigContainer = view.modelBuilder.flexContainer().withLayout({
|
||||||
|
flexFlow: 'column'
|
||||||
|
}).component();
|
||||||
|
|
||||||
const flexContainer = view.modelBuilder.flexContainer()
|
const flexContainer = view.modelBuilder.flexContainer()
|
||||||
.withItems(
|
.withItems(
|
||||||
@@ -178,8 +175,8 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
this._fileShareSubscriptionDropdown,
|
this._fileShareSubscriptionDropdown,
|
||||||
storageAccountLabel,
|
storageAccountLabel,
|
||||||
this._fileShareStorageAccountDropdown,
|
this._fileShareStorageAccountDropdown,
|
||||||
fileShareLabel,
|
fileShareDatabaseConfigHeader,
|
||||||
this._fileShareFileShareDropdown
|
this._fileShareDatabaseConfigContainer
|
||||||
]
|
]
|
||||||
).withLayout({
|
).withLayout({
|
||||||
flexFlow: 'column'
|
flexFlow: 'column'
|
||||||
@@ -220,24 +217,19 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
this._blobContainerStorageAccountDropdown.onValueChanged(async (value) => {
|
this._blobContainerStorageAccountDropdown.onValueChanged(async (value) => {
|
||||||
if (value.selected) {
|
if (value.selected) {
|
||||||
this.migrationStateModel._databaseBackup.storageAccount = this.migrationStateModel.getStorageAccount(value.index);
|
this.migrationStateModel._databaseBackup.storageAccount = this.migrationStateModel.getStorageAccount(value.index);
|
||||||
this.migrationStateModel._databaseBackup.blobContainer = undefined!;
|
this.migrationStateModel._databaseBackup.blobContainers = undefined!;
|
||||||
await this.loadBlobContainerDropdown();
|
await this.loadBlobContainerDropdown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const containerLabel = view.modelBuilder.text().withProps({
|
|
||||||
value: constants.DATABASE_BACKUP_BLOB_STORAGE_ACCOUNT_CONTAINER_LABEL,
|
const blobContainerDatabaseConfigHeader = view.modelBuilder.text().withProps({
|
||||||
requiredIndicator: true,
|
value: constants.ENTER_BLOB_CONTAINER_INFORMATION
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
this._blobContainerDatabaseConfigContainer = view.modelBuilder.flexContainer().withLayout({
|
||||||
|
flexFlow: 'column'
|
||||||
}).component();
|
}).component();
|
||||||
this._blobContainerBlobDropdown = view.modelBuilder.dropDown()
|
|
||||||
.withProps({
|
|
||||||
required: true
|
|
||||||
}).component();
|
|
||||||
this._blobContainerBlobDropdown.onValueChanged((value) => {
|
|
||||||
if (value.selected) {
|
|
||||||
this.migrationStateModel._databaseBackup.blobContainer = this.migrationStateModel.getBlobContainer(value.index);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const flexContainer = view.modelBuilder.flexContainer()
|
const flexContainer = view.modelBuilder.flexContainer()
|
||||||
.withItems(
|
.withItems(
|
||||||
@@ -246,8 +238,8 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
this._blobContainerSubscriptionDropdown,
|
this._blobContainerSubscriptionDropdown,
|
||||||
storageAccountLabel,
|
storageAccountLabel,
|
||||||
this._blobContainerStorageAccountDropdown,
|
this._blobContainerStorageAccountDropdown,
|
||||||
containerLabel,
|
blobContainerDatabaseConfigHeader,
|
||||||
this._blobContainerBlobDropdown
|
this._blobContainerDatabaseConfigContainer
|
||||||
]
|
]
|
||||||
).withLayout({
|
).withLayout({
|
||||||
flexFlow: 'column'
|
flexFlow: 'column'
|
||||||
@@ -264,31 +256,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
value: constants.DATABASE_BACKUP_NC_NETWORK_SHARE_HELP_TEXT,
|
value: constants.DATABASE_BACKUP_NC_NETWORK_SHARE_HELP_TEXT,
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const networkShareLocationLabel = view.modelBuilder.text()
|
|
||||||
.withProps({
|
|
||||||
value: constants.DATABASE_BACKUP_NETWORK_SHARE_LOCATION_LABEL,
|
|
||||||
requiredIndicator: true,
|
|
||||||
}).component();
|
|
||||||
this._networkShareLocationText = view.modelBuilder.inputBox()
|
|
||||||
.withProps({
|
|
||||||
placeHolder: '\\\\Servername.domainname.com\\Backupfolder',
|
|
||||||
required: true,
|
|
||||||
validationErrorMessage: constants.INVALID_NETWORK_SHARE_LOCATION
|
|
||||||
})
|
|
||||||
.withValidation((component) => {
|
|
||||||
if (this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE) {
|
|
||||||
if (component.value) {
|
|
||||||
if (!/(?<=\\\\)[^\\]*/.test(component.value)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}).component();
|
|
||||||
this._networkShareLocationText.onTextChanged((value) => {
|
|
||||||
this.migrationStateModel._databaseBackup.networkShareLocation = value;
|
|
||||||
});
|
|
||||||
|
|
||||||
const windowsUserAccountLabel = view.modelBuilder.text()
|
const windowsUserAccountLabel = view.modelBuilder.text()
|
||||||
.withProps({
|
.withProps({
|
||||||
value: constants.DATABASE_BACKUP_NETWORK_SHARE_WINDOWS_USER_LABEL,
|
value: constants.DATABASE_BACKUP_NETWORK_SHARE_WINDOWS_USER_LABEL,
|
||||||
@@ -366,20 +333,30 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const networkShareDatabaseConfigHeader = view.modelBuilder.text().withProps({
|
||||||
|
value: constants.ENTER_NETWORK_SHARE_INFORMATION
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
this._networkShareDatabaseConfigContainer = view.modelBuilder.flexContainer().withLayout({
|
||||||
|
flexFlow: 'column'
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
|
||||||
const flexContainer = view.modelBuilder.flexContainer().withItems(
|
const flexContainer = view.modelBuilder.flexContainer().withItems(
|
||||||
[
|
[
|
||||||
|
azureAccountHelpText,
|
||||||
networkShareHelpText,
|
networkShareHelpText,
|
||||||
networkShareLocationLabel,
|
subscriptionLabel,
|
||||||
this._networkShareLocationText,
|
this._networkShareContainerSubscriptionDropdown,
|
||||||
|
storageAccountLabel,
|
||||||
|
this._networkShareContainerStorageAccountDropdown,
|
||||||
windowsUserAccountLabel,
|
windowsUserAccountLabel,
|
||||||
this._windowsUserAccountText,
|
this._windowsUserAccountText,
|
||||||
passwordLabel,
|
passwordLabel,
|
||||||
this._passwordText,
|
this._passwordText,
|
||||||
azureAccountHelpText,
|
networkShareDatabaseConfigHeader,
|
||||||
subscriptionLabel,
|
this._networkShareDatabaseConfigContainer
|
||||||
this._networkShareContainerSubscriptionDropdown,
|
|
||||||
storageAccountLabel,
|
|
||||||
this._networkShareContainerStorageAccountDropdown
|
|
||||||
]
|
]
|
||||||
).withLayout({
|
).withLayout({
|
||||||
flexFlow: 'column'
|
flexFlow: 'column'
|
||||||
@@ -390,57 +367,130 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
return flexContainer;
|
return flexContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private migrationModeContainer(view: azdata.ModelView): azdata.FormComponent {
|
|
||||||
const description = view.modelBuilder.text().withProps({
|
|
||||||
value: constants.DATABASE_BACKUP_MIGRATION_MODE_DESCRIPTION
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
const buttonGroup = 'cutoverContainer';
|
|
||||||
|
|
||||||
const onlineButton = view.modelBuilder.radioButton().withProps({
|
|
||||||
label: constants.DATABASE_BACKUP_MIGRATION_MODE_ONLINE_LABEL,
|
|
||||||
name: buttonGroup,
|
|
||||||
checked: true
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
this.migrationStateModel._databaseBackup.migrationCutover = MigrationCutover.ONLINE;
|
|
||||||
|
|
||||||
onlineButton.onDidChangeCheckedState((e) => {
|
|
||||||
if (e) {
|
|
||||||
this.migrationStateModel._databaseBackup.migrationCutover = MigrationCutover.ONLINE;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const offlineButton = view.modelBuilder.radioButton().withProps({
|
|
||||||
label: constants.DATABASE_BACKUP_MIGRATION_MODE_OFFLINE_LABEL,
|
|
||||||
name: buttonGroup
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
offlineButton.onDidChangeCheckedState((e) => {
|
|
||||||
if (e) {
|
|
||||||
vscode.window.showInformationMessage('Feature coming soon');
|
|
||||||
onlineButton.checked = true;
|
|
||||||
//this.migrationStateModel._databaseBackup.migrationCutover = MigrationCutover.OFFLINE;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const flexContainer = view.modelBuilder.flexContainer().withItems(
|
|
||||||
[
|
|
||||||
description,
|
|
||||||
onlineButton,
|
|
||||||
offlineButton
|
|
||||||
]
|
|
||||||
).withLayout({
|
|
||||||
flexFlow: 'column'
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
return {
|
|
||||||
title: constants.DATABASE_BACKUP_MIGRATION_MODE_LABEL,
|
|
||||||
component: flexContainer
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public async onPageEnter(): Promise<void> {
|
public async onPageEnter(): Promise<void> {
|
||||||
|
if (this.migrationStateModel.refreshDatabaseBackupPage) {
|
||||||
|
|
||||||
|
this._networkShareLocations = [];
|
||||||
|
this._fileShareDropdowns = [];
|
||||||
|
this._blobContainerDropdowns = [];
|
||||||
|
this.migrationStateModel._targetDatabaseNames = [];
|
||||||
|
this.migrationStateModel._databaseBackup.networkShareLocations = [];
|
||||||
|
this.migrationStateModel._databaseBackup.fileShares = [];
|
||||||
|
this.migrationStateModel._databaseBackup.blobContainers = [];
|
||||||
|
this._networkShareDatabaseConfigContainer.clearItems();
|
||||||
|
this._fileShareDatabaseConfigContainer.clearItems();
|
||||||
|
this._blobContainerDatabaseConfigContainer.clearItems();
|
||||||
|
|
||||||
|
this.migrationStateModel._migrationDbs.forEach((db, index) => {
|
||||||
|
this.migrationStateModel._targetDatabaseNames.push('');
|
||||||
|
const targetNameLabel = constants.TARGET_NAME_FOR_DATABASE(db);
|
||||||
|
const targetNameNetworkInputBoxLabel = this._view.modelBuilder.text().withProps({
|
||||||
|
value: targetNameLabel,
|
||||||
|
requiredIndicator: true
|
||||||
|
}).component();
|
||||||
|
const targetNameNetworkInputBox = this._view.modelBuilder.inputBox().withProps({
|
||||||
|
required: true
|
||||||
|
}).component();
|
||||||
|
targetNameNetworkInputBox.onTextChanged((value) => {
|
||||||
|
this.migrationStateModel._targetDatabaseNames[index] = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
const networkLocationInputBoxLabel = this._view.modelBuilder.text().withProps({
|
||||||
|
value: constants.TARGET_NETWORK_SHARE_LOCATION(db),
|
||||||
|
requiredIndicator: true
|
||||||
|
}).component();
|
||||||
|
const networkLocationInputBox = this._view.modelBuilder.inputBox().withProps({
|
||||||
|
placeHolder: '\\\\Servername.domainname.com\\Backupfolder',
|
||||||
|
required: true,
|
||||||
|
validationErrorMessage: constants.INVALID_NETWORK_SHARE_LOCATION
|
||||||
|
}).withValidation((component) => {
|
||||||
|
if (this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE) {
|
||||||
|
if (component.value) {
|
||||||
|
if (!/(?<=\\\\)[^\\]*/.test(component.value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}).component();
|
||||||
|
networkLocationInputBox.onTextChanged((value) => {
|
||||||
|
this.validateFields();
|
||||||
|
this.migrationStateModel._databaseBackup.networkShareLocations[index] = value;
|
||||||
|
});
|
||||||
|
this.migrationStateModel._databaseBackup.networkShareLocations.push(undefined!);
|
||||||
|
this._networkShareLocations.push(networkLocationInputBox);
|
||||||
|
this._networkShareDatabaseConfigContainer.addItems(
|
||||||
|
[
|
||||||
|
targetNameNetworkInputBoxLabel,
|
||||||
|
targetNameNetworkInputBox,
|
||||||
|
networkLocationInputBoxLabel,
|
||||||
|
networkLocationInputBox
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
const targetNameFileInputBoxLabel = this._view.modelBuilder.text().withProps({
|
||||||
|
value: targetNameLabel
|
||||||
|
}).component();
|
||||||
|
const targetNameFileInputBox = this._view.modelBuilder.inputBox().withProps({
|
||||||
|
}).component();
|
||||||
|
const fileShareLabel = this._view.modelBuilder.text()
|
||||||
|
.withProps({
|
||||||
|
value: constants.TARGET_FILE_SHARE(db),
|
||||||
|
requiredIndicator: true,
|
||||||
|
}).component();
|
||||||
|
const fileShareDropdown = this._view.modelBuilder.dropDown()
|
||||||
|
.withProps({
|
||||||
|
}).component();
|
||||||
|
fileShareDropdown.onValueChanged((value) => {
|
||||||
|
if (value.selected) {
|
||||||
|
this.validateFields();
|
||||||
|
this.migrationStateModel._databaseBackup.fileShares[index] = this.migrationStateModel.getFileShare(value.index);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.migrationStateModel._databaseBackup.fileShares.push(undefined!);
|
||||||
|
this._fileShareDropdowns.push(fileShareDropdown);
|
||||||
|
this._fileShareDatabaseConfigContainer.addItems(
|
||||||
|
[
|
||||||
|
targetNameFileInputBoxLabel,
|
||||||
|
targetNameFileInputBox,
|
||||||
|
fileShareLabel,
|
||||||
|
fileShareDropdown
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
const targetNameBlobInputBoxLabel = this._view.modelBuilder.text().withProps({
|
||||||
|
value: targetNameLabel
|
||||||
|
}).component();
|
||||||
|
const targetNameBlobInputBox = this._view.modelBuilder.inputBox().withProps({
|
||||||
|
}).component();
|
||||||
|
const blobContainerLabel = this._view.modelBuilder.text()
|
||||||
|
.withProps({
|
||||||
|
value: constants.TARGET_BLOB_CONTAINER(db),
|
||||||
|
requiredIndicator: true,
|
||||||
|
}).component();
|
||||||
|
const blobContainerDropdown = this._view.modelBuilder.dropDown()
|
||||||
|
.withProps({
|
||||||
|
}).component();
|
||||||
|
blobContainerDropdown.onValueChanged((value) => {
|
||||||
|
if (value.selected) {
|
||||||
|
this.validateFields();
|
||||||
|
this.migrationStateModel._databaseBackup.blobContainers[index] = this.migrationStateModel.getBlobContainer(value.index);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.migrationStateModel._databaseBackup.fileShares.push(undefined!);
|
||||||
|
this._blobContainerDropdowns.push(blobContainerDropdown);
|
||||||
|
this._blobContainerDatabaseConfigContainer.addItems(
|
||||||
|
[
|
||||||
|
targetNameBlobInputBoxLabel,
|
||||||
|
targetNameBlobInputBox,
|
||||||
|
blobContainerLabel,
|
||||||
|
blobContainerDropdown
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.migrationStateModel.refreshDatabaseBackupPage = false;
|
||||||
|
}
|
||||||
await this.getSubscriptionValues();
|
await this.getSubscriptionValues();
|
||||||
this.wizard.registerNavigationValidator((pageChangeInfo) => {
|
this.wizard.registerNavigationValidator((pageChangeInfo) => {
|
||||||
if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
|
if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
|
||||||
@@ -465,8 +515,11 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
if ((<azdata.CategoryValue>this._blobContainerStorageAccountDropdown.value).displayName === constants.NO_STORAGE_ACCOUNT_FOUND) {
|
if ((<azdata.CategoryValue>this._blobContainerStorageAccountDropdown.value).displayName === constants.NO_STORAGE_ACCOUNT_FOUND) {
|
||||||
errors.push(constants.INVALID_STORAGE_ACCOUNT_ERROR);
|
errors.push(constants.INVALID_STORAGE_ACCOUNT_ERROR);
|
||||||
}
|
}
|
||||||
if ((<azdata.CategoryValue>this._blobContainerBlobDropdown.value).displayName === constants.NO_BLOBCONTAINERS_FOUND) {
|
for (let i = 0; i < this._blobContainerDropdowns.length; i++) {
|
||||||
errors.push(constants.INVALID_BLOBCONTAINER_ERROR);
|
if ((<azdata.CategoryValue>this._blobContainerDropdowns[i].value).displayName === constants.NO_BLOBCONTAINERS_FOUND) {
|
||||||
|
errors.push(constants.INVALID_BLOBCONTAINER_ERROR);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NetworkContainerType.FILE_SHARE:
|
case NetworkContainerType.FILE_SHARE:
|
||||||
@@ -476,8 +529,11 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
if ((<azdata.CategoryValue>this._fileShareStorageAccountDropdown.value).displayName === constants.NO_STORAGE_ACCOUNT_FOUND) {
|
if ((<azdata.CategoryValue>this._fileShareStorageAccountDropdown.value).displayName === constants.NO_STORAGE_ACCOUNT_FOUND) {
|
||||||
errors.push(constants.INVALID_STORAGE_ACCOUNT_ERROR);
|
errors.push(constants.INVALID_STORAGE_ACCOUNT_ERROR);
|
||||||
}
|
}
|
||||||
if ((<azdata.CategoryValue>this._fileShareFileShareDropdown.value).displayName === constants.NO_FILESHARES_FOUND) {
|
for (let i = 0; i < this._fileShareDropdowns.length; i++) {
|
||||||
errors.push(constants.INVALID_FILESHARE_ERROR);
|
if ((<azdata.CategoryValue>this._fileShareDropdowns[i].value).displayName === constants.NO_FILESHARES_FOUND) {
|
||||||
|
errors.push(constants.INVALID_FILESHARE_ERROR);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -508,8 +564,10 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
this._fileShareContainer.updateCssStyles({ 'display': (containerType === NetworkContainerType.FILE_SHARE) ? 'inline' : 'none' });
|
this._fileShareContainer.updateCssStyles({ 'display': (containerType === NetworkContainerType.FILE_SHARE) ? 'inline' : 'none' });
|
||||||
this._blobContainer.updateCssStyles({ 'display': (containerType === NetworkContainerType.BLOB_CONTAINER) ? 'inline' : 'none' });
|
this._blobContainer.updateCssStyles({ 'display': (containerType === NetworkContainerType.BLOB_CONTAINER) ? 'inline' : 'none' });
|
||||||
this._networkShareContainer.updateCssStyles({ 'display': (containerType === NetworkContainerType.NETWORK_SHARE) ? 'inline' : 'none' });
|
this._networkShareContainer.updateCssStyles({ 'display': (containerType === NetworkContainerType.NETWORK_SHARE) ? 'inline' : 'none' });
|
||||||
this._networkShareLocationText.updateProperties({
|
this._networkShareLocations.forEach((inputBox) => {
|
||||||
required: containerType === NetworkContainerType.NETWORK_SHARE
|
inputBox.updateProperties({
|
||||||
|
required: containerType === NetworkContainerType.NETWORK_SHARE
|
||||||
|
});
|
||||||
});
|
});
|
||||||
this._windowsUserAccountText.updateProperties({
|
this._windowsUserAccountText.updateProperties({
|
||||||
required: containerType === NetworkContainerType.NETWORK_SHARE
|
required: containerType === NetworkContainerType.NETWORK_SHARE
|
||||||
@@ -517,21 +575,47 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
this._passwordText.updateProperties({
|
this._passwordText.updateProperties({
|
||||||
required: containerType === NetworkContainerType.NETWORK_SHARE
|
required: containerType === NetworkContainerType.NETWORK_SHARE
|
||||||
});
|
});
|
||||||
|
this._networkShareLocations.forEach((inputBox) => {
|
||||||
this._networkShareLocationText.validate();
|
inputBox.validate();
|
||||||
|
});
|
||||||
this._windowsUserAccountText.validate();
|
this._windowsUserAccountText.validate();
|
||||||
this._passwordText.validate();
|
this._passwordText.validate();
|
||||||
this._networkShareContainerSubscriptionDropdown.validate();
|
this._networkShareContainerSubscriptionDropdown.validate();
|
||||||
this._networkShareContainerStorageAccountDropdown.validate();
|
this._networkShareContainerStorageAccountDropdown.validate();
|
||||||
this._blobContainerSubscriptionDropdown.validate();
|
this._blobContainerSubscriptionDropdown.validate();
|
||||||
this._blobContainerStorageAccountDropdown.validate();
|
this._blobContainerStorageAccountDropdown.validate();
|
||||||
this._blobContainerBlobDropdown.validate();
|
this._blobContainerDropdowns.forEach((dropdown) => {
|
||||||
|
dropdown.validate();
|
||||||
|
});
|
||||||
this._fileShareSubscriptionDropdown.validate();
|
this._fileShareSubscriptionDropdown.validate();
|
||||||
this._fileShareStorageAccountDropdown.validate();
|
this._fileShareStorageAccountDropdown.validate();
|
||||||
this._fileShareFileShareDropdown.validate();
|
this._fileShareDropdowns.forEach(dropdown => {
|
||||||
|
dropdown.validate();
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private validateFields(): void {
|
||||||
|
this._networkShareLocations.forEach((inputBox) => {
|
||||||
|
inputBox.validate();
|
||||||
|
});
|
||||||
|
this._windowsUserAccountText.validate();
|
||||||
|
this._passwordText.validate();
|
||||||
|
this._networkShareContainerSubscriptionDropdown.validate();
|
||||||
|
this._networkShareContainerStorageAccountDropdown.validate();
|
||||||
|
this._blobContainerSubscriptionDropdown.validate();
|
||||||
|
this._blobContainerStorageAccountDropdown.validate();
|
||||||
|
this._blobContainerDropdowns.forEach((dropdown) => {
|
||||||
|
dropdown.validate();
|
||||||
|
});
|
||||||
|
this._fileShareSubscriptionDropdown.validate();
|
||||||
|
this._fileShareStorageAccountDropdown.validate();
|
||||||
|
this._fileShareDropdowns.forEach((dropdown) => {
|
||||||
|
dropdown.validate();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private async getSubscriptionValues(): Promise<void> {
|
private async getSubscriptionValues(): Promise<void> {
|
||||||
if (!this.migrationStateModel._databaseBackup.subscription) {
|
if (!this.migrationStateModel._databaseBackup.subscription) {
|
||||||
this._networkShareContainerSubscriptionDropdown.loading = true;
|
this._networkShareContainerSubscriptionDropdown.loading = true;
|
||||||
@@ -568,14 +652,18 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
private async loadFileShareStorageDropdown(): Promise<void> {
|
private async loadFileShareStorageDropdown(): Promise<void> {
|
||||||
if (!this.migrationStateModel._databaseBackup.storageAccount) {
|
if (!this.migrationStateModel._databaseBackup.storageAccount) {
|
||||||
this._fileShareStorageAccountDropdown.loading = true;
|
this._fileShareStorageAccountDropdown.loading = true;
|
||||||
this._fileShareFileShareDropdown.loading = true;
|
this._fileShareDropdowns.forEach((dropdown) => {
|
||||||
|
dropdown.loading = true;
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
this._fileShareStorageAccountDropdown.values = await this.migrationStateModel.getStorageAccountValues(this.migrationStateModel._databaseBackup.subscription);
|
this._fileShareStorageAccountDropdown.values = await this.migrationStateModel.getStorageAccountValues(this.migrationStateModel._databaseBackup.subscription);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
} finally {
|
} finally {
|
||||||
this._fileShareStorageAccountDropdown.loading = false;
|
this._fileShareStorageAccountDropdown.loading = false;
|
||||||
this._fileShareFileShareDropdown.loading = false;
|
this._fileShareDropdowns.forEach((dropdown) => {
|
||||||
|
dropdown.loading = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -583,41 +671,58 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
private async loadblobStorageDropdown(): Promise<void> {
|
private async loadblobStorageDropdown(): Promise<void> {
|
||||||
if (!this.migrationStateModel._databaseBackup.storageAccount) {
|
if (!this.migrationStateModel._databaseBackup.storageAccount) {
|
||||||
this._blobContainerStorageAccountDropdown.loading = true;
|
this._blobContainerStorageAccountDropdown.loading = true;
|
||||||
this._blobContainerBlobDropdown.loading = true;
|
this._blobContainerDropdowns.forEach((dropdown) => {
|
||||||
|
dropdown.loading = true;
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
this._blobContainerStorageAccountDropdown.values = await this.migrationStateModel.getStorageAccountValues(this.migrationStateModel._databaseBackup.subscription);
|
this._blobContainerStorageAccountDropdown.values = await this.migrationStateModel.getStorageAccountValues(this.migrationStateModel._databaseBackup.subscription);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
} finally {
|
} finally {
|
||||||
this._blobContainerStorageAccountDropdown.loading = false;
|
this._blobContainerStorageAccountDropdown.loading = false;
|
||||||
this._blobContainerBlobDropdown.loading = true;
|
this._blobContainerDropdowns.forEach((dropdown) => {
|
||||||
|
dropdown.loading = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadFileShareDropdown(): Promise<void> {
|
private async loadFileShareDropdown(): Promise<void> {
|
||||||
if (!this.migrationStateModel._fileShares) {
|
if (!this.migrationStateModel._fileShares) {
|
||||||
this._fileShareFileShareDropdown.loading = true;
|
this._fileShareDropdowns.forEach((dropdown) => {
|
||||||
|
dropdown.loading = true;
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
this._fileShareFileShareDropdown.values = await this.migrationStateModel.getFileShareValues(this.migrationStateModel._databaseBackup.subscription, this.migrationStateModel._databaseBackup.storageAccount);
|
const fileShareValues = await this.migrationStateModel.getFileShareValues(this.migrationStateModel._databaseBackup.subscription, this.migrationStateModel._databaseBackup.storageAccount);
|
||||||
|
this._fileShareDropdowns.forEach((dropdown) => {
|
||||||
|
dropdown.values = fileShareValues;
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
} finally {
|
} finally {
|
||||||
this._fileShareFileShareDropdown.loading = false;
|
this._fileShareDropdowns.forEach((dropdown) => {
|
||||||
|
dropdown.loading = true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadBlobContainerDropdown(): Promise<void> {
|
private async loadBlobContainerDropdown(): Promise<void> {
|
||||||
if (!this.migrationStateModel._blobContainers) {
|
if (!this.migrationStateModel._blobContainers) {
|
||||||
this._blobContainerBlobDropdown.loading = true;
|
this._blobContainerDropdowns.forEach((dropdown) => {
|
||||||
|
dropdown.loading = true;
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
this._blobContainerBlobDropdown.values = await this.migrationStateModel.getBlobContainerValues(this.migrationStateModel._databaseBackup.subscription, this.migrationStateModel._databaseBackup.storageAccount);
|
const blobContainerValues = await this.migrationStateModel.getBlobContainerValues(this.migrationStateModel._databaseBackup.subscription, this.migrationStateModel._databaseBackup.storageAccount);
|
||||||
|
this._blobContainerDropdowns.forEach((dropdown) => {
|
||||||
|
dropdown.values = blobContainerValues;
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
} finally {
|
} finally {
|
||||||
this._blobContainerBlobDropdown.loading = false;
|
this._blobContainerDropdowns.forEach((dropdown) => {
|
||||||
|
dropdown.loading = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,7 +132,9 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
|||||||
text: ''
|
text: ''
|
||||||
};
|
};
|
||||||
this.migrationStateModel._migrationController = this.migrationStateModel.getMigrationController(value.index);
|
this.migrationStateModel._migrationController = this.migrationStateModel.getMigrationController(value.index);
|
||||||
await this.loadControllerStatus();
|
if (value !== constants.MIGRATION_CONTROLLER_NOT_FOUND_ERROR) {
|
||||||
|
await this.loadControllerStatus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -169,7 +171,6 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
|||||||
|
|
||||||
private async loadControllerStatus(): Promise<void> {
|
private async loadControllerStatus(): Promise<void> {
|
||||||
this._statusLoadingComponent.loading = true;
|
this._statusLoadingComponent.loading = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this._migrationDetailsContainer.clearItems();
|
this._migrationDetailsContainer.clearItems();
|
||||||
|
|
||||||
|
|||||||
78
extensions/sql-migration/src/wizard/migrationModePage.ts
Normal file
78
extensions/sql-migration/src/wizard/migrationModePage.ts
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 { MigrationWizardPage } from '../models/migrationWizardPage';
|
||||||
|
import { MigrationCutover, MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
|
||||||
|
import * as constants from '../constants/strings';
|
||||||
|
|
||||||
|
export class MigrationModePage extends MigrationWizardPage {
|
||||||
|
constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) {
|
||||||
|
super(wizard, azdata.window.createWizardPage(constants.DATABASE_BACKUP_MIGRATION_MODE_LABEL, 'MigrationModePage'), migrationStateModel);
|
||||||
|
this.wizardPage.description = constants.DATABASE_BACKUP_MIGRATION_MODE_DESCRIPTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async registerContent(view: azdata.ModelView): Promise<void> {
|
||||||
|
const form = view.modelBuilder.formContainer()
|
||||||
|
.withFormItems(
|
||||||
|
[
|
||||||
|
this.migrationModeContainer(view),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
await view.initializeModel(form.component());
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onPageEnter(): Promise<void> {
|
||||||
|
}
|
||||||
|
public async onPageLeave(): Promise<void> {
|
||||||
|
}
|
||||||
|
protected async handleStateChange(e: StateChangeEvent): Promise<void> {
|
||||||
|
}
|
||||||
|
|
||||||
|
private migrationModeContainer(view: azdata.ModelView): azdata.FormComponent {
|
||||||
|
const buttonGroup = 'cutoverContainer';
|
||||||
|
|
||||||
|
const onlineButton = view.modelBuilder.radioButton().withProps({
|
||||||
|
label: constants.DATABASE_BACKUP_MIGRATION_MODE_ONLINE_LABEL,
|
||||||
|
name: buttonGroup,
|
||||||
|
checked: true
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
this.migrationStateModel._databaseBackup.migrationCutover = MigrationCutover.ONLINE;
|
||||||
|
|
||||||
|
onlineButton.onDidChangeCheckedState((e) => {
|
||||||
|
if (e) {
|
||||||
|
this.migrationStateModel._databaseBackup.migrationCutover = MigrationCutover.ONLINE;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const offlineButton = view.modelBuilder.radioButton().withProps({
|
||||||
|
label: constants.DATABASE_BACKUP_MIGRATION_MODE_OFFLINE_LABEL,
|
||||||
|
name: buttonGroup
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
offlineButton.onDidChangeCheckedState((e) => {
|
||||||
|
if (e) {
|
||||||
|
vscode.window.showInformationMessage('Feature coming soon');
|
||||||
|
onlineButton.checked = true;
|
||||||
|
//this.migrationStateModel._databaseBackup.migrationCutover = MigrationCutover.OFFLINE; TODO: Enable when offline mode is supported.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const flexContainer = view.modelBuilder.flexContainer().withItems(
|
||||||
|
[
|
||||||
|
onlineButton,
|
||||||
|
offlineButton
|
||||||
|
]
|
||||||
|
).withLayout({
|
||||||
|
flexFlow: 'column'
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
return {
|
||||||
|
component: flexContainer
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -300,8 +300,11 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
errors.push(constants.INVALID_SUBSCRIPTION_ERROR);
|
errors.push(constants.INVALID_SUBSCRIPTION_ERROR);
|
||||||
}
|
}
|
||||||
const resourceDropdownValue = (<azdata.CategoryValue>this._resourceDropdown.value).displayName;
|
const resourceDropdownValue = (<azdata.CategoryValue>this._resourceDropdown.value).displayName;
|
||||||
if (resourceDropdownValue === constants.NO_MANAGED_INSTANCE_FOUND || resourceDropdownValue === constants.NO_VIRTUAL_MACHINE_FOUND) {
|
if (resourceDropdownValue === constants.NO_MANAGED_INSTANCE_FOUND) {
|
||||||
errors.push(constants.INVALID_STORAGE_ACCOUNT_ERROR);
|
errors.push(constants.NO_MANAGED_INSTANCE_FOUND);
|
||||||
|
}
|
||||||
|
else if (resourceDropdownValue === constants.NO_VIRTUAL_MACHINE_FOUND) {
|
||||||
|
errors.push(constants.NO_VIRTUAL_MACHINE_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
|
|||||||
@@ -72,12 +72,16 @@ export class SummaryPage extends MigrationWizardPage {
|
|||||||
flexContainer.addItems(
|
flexContainer.addItems(
|
||||||
[
|
[
|
||||||
createInformationRow(this._view, constants.TYPE, constants.NETWORK_SHARE),
|
createInformationRow(this._view, constants.TYPE, constants.NETWORK_SHARE),
|
||||||
createInformationRow(this._view, constants.PATH, this.migrationStateModel._databaseBackup.networkShareLocation),
|
|
||||||
createInformationRow(this._view, constants.USER_ACCOUNT, this.migrationStateModel._databaseBackup.windowsUser),
|
createInformationRow(this._view, constants.USER_ACCOUNT, this.migrationStateModel._databaseBackup.windowsUser),
|
||||||
createInformationRow(this._view, constants.SUMMARY_AZURE_STORAGE_SUBSCRIPTION, this.migrationStateModel._databaseBackup.subscription.name),
|
createInformationRow(this._view, constants.SUMMARY_AZURE_STORAGE_SUBSCRIPTION, this.migrationStateModel._databaseBackup.subscription.name),
|
||||||
createInformationRow(this._view, constants.SUMMARY_AZURE_STORAGE, this.migrationStateModel._databaseBackup.storageAccount.name),
|
createInformationRow(this._view, constants.SUMMARY_AZURE_STORAGE, this.migrationStateModel._databaseBackup.storageAccount.name),
|
||||||
|
createHeadingTextComponent(this._view, 'Target Databases:')
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
this.migrationStateModel._migrationDbs.forEach((db, index) => {
|
||||||
|
flexContainer.addItem(createInformationRow(this._view, constants.TARGET_NAME_FOR_DATABASE(db), this.migrationStateModel._targetDatabaseNames[index]));
|
||||||
|
flexContainer.addItem(createInformationRow(this._view, constants.TARGET_NETWORK_SHARE_LOCATION(db), this.migrationStateModel._databaseBackup.networkShareLocations[index]));
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
case NetworkContainerType.FILE_SHARE:
|
case NetworkContainerType.FILE_SHARE:
|
||||||
flexContainer.addItems(
|
flexContainer.addItems(
|
||||||
@@ -85,19 +89,25 @@ export class SummaryPage extends MigrationWizardPage {
|
|||||||
createInformationRow(this._view, constants.TYPE, constants.FILE_SHARE),
|
createInformationRow(this._view, constants.TYPE, constants.FILE_SHARE),
|
||||||
createInformationRow(this._view, constants.SUMMARY_AZURE_STORAGE_SUBSCRIPTION, this.migrationStateModel._databaseBackup.subscription.name),
|
createInformationRow(this._view, constants.SUMMARY_AZURE_STORAGE_SUBSCRIPTION, this.migrationStateModel._databaseBackup.subscription.name),
|
||||||
createInformationRow(this._view, constants.SUMMARY_AZURE_STORAGE, this.migrationStateModel._databaseBackup.storageAccount.name),
|
createInformationRow(this._view, constants.SUMMARY_AZURE_STORAGE, this.migrationStateModel._databaseBackup.storageAccount.name),
|
||||||
createInformationRow(this._view, constants.FILE_SHARE, this.migrationStateModel._databaseBackup.fileShare.name),
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
this.migrationStateModel._migrationDbs.forEach((db, index) => {
|
||||||
|
flexContainer.addItem(createInformationRow(this._view, constants.TARGET_NAME_FOR_DATABASE(db), this.migrationStateModel._targetDatabaseNames[index]));
|
||||||
|
flexContainer.addItem(createInformationRow(this._view, constants.TARGET_FILE_SHARE(db), this.migrationStateModel._databaseBackup.fileShares[index].name));
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
case NetworkContainerType.BLOB_CONTAINER:
|
case NetworkContainerType.BLOB_CONTAINER:
|
||||||
flexContainer.addItems(
|
flexContainer.addItems(
|
||||||
[
|
[
|
||||||
createInformationRow(this._view, constants.TYPE, constants.BLOB_CONTAINER),
|
createInformationRow(this._view, constants.TYPE, constants.BLOB_CONTAINER),
|
||||||
createInformationRow(this._view, constants.SUMMARY_AZURE_STORAGE_SUBSCRIPTION, this.migrationStateModel._databaseBackup.blobContainer.subscription.name),
|
createInformationRow(this._view, constants.SUMMARY_AZURE_STORAGE_SUBSCRIPTION, this.migrationStateModel._databaseBackup.subscription.name),
|
||||||
createInformationRow(this._view, constants.SUMMARY_AZURE_STORAGE, this.migrationStateModel._databaseBackup.storageAccount.name),
|
createInformationRow(this._view, constants.SUMMARY_AZURE_STORAGE, this.migrationStateModel._databaseBackup.storageAccount.name),
|
||||||
createInformationRow(this._view, constants.BLOB_CONTAINER, this.migrationStateModel._databaseBackup.blobContainer.name),
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
this.migrationStateModel._migrationDbs.forEach((db, index) => {
|
||||||
|
flexContainer.addItem(createInformationRow(this._view, constants.TARGET_NAME_FOR_DATABASE(db), this.migrationStateModel._targetDatabaseNames[index]));
|
||||||
|
flexContainer.addItem(createInformationRow(this._view, constants.TARGET_FILE_SHARE(db), this.migrationStateModel._databaseBackup.blobContainers[index].name));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return flexContainer;
|
return flexContainer;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { DatabaseBackupPage } from './databaseBackupPage';
|
|||||||
import { AccountsSelectionPage } from './accountsSelectionPage';
|
import { AccountsSelectionPage } from './accountsSelectionPage';
|
||||||
import { IntergrationRuntimePage } from './integrationRuntimePage';
|
import { IntergrationRuntimePage } from './integrationRuntimePage';
|
||||||
import { SummaryPage } from './summaryPage';
|
import { SummaryPage } from './summaryPage';
|
||||||
|
import { MigrationModePage } from './migrationModePage';
|
||||||
|
|
||||||
export const WIZARD_INPUT_COMPONENT_WIDTH = '400px';
|
export const WIZARD_INPUT_COMPONENT_WIDTH = '400px';
|
||||||
export class WizardController {
|
export class WizardController {
|
||||||
@@ -36,6 +37,7 @@ export class WizardController {
|
|||||||
wizard.generateScriptButton.hidden = true;
|
wizard.generateScriptButton.hidden = true;
|
||||||
const skuRecommendationPage = new SKURecommendationPage(wizard, stateModel);
|
const skuRecommendationPage = new SKURecommendationPage(wizard, stateModel);
|
||||||
// const subscriptionSelectionPage = new SubscriptionSelectionPage(wizard, stateModel);
|
// const subscriptionSelectionPage = new SubscriptionSelectionPage(wizard, stateModel);
|
||||||
|
const migrationModePage = new MigrationModePage(wizard, stateModel);
|
||||||
const azureAccountsPage = new AccountsSelectionPage(wizard, stateModel);
|
const azureAccountsPage = new AccountsSelectionPage(wizard, stateModel);
|
||||||
const databaseBackupPage = new DatabaseBackupPage(wizard, stateModel);
|
const databaseBackupPage = new DatabaseBackupPage(wizard, stateModel);
|
||||||
const integrationRuntimePage = new IntergrationRuntimePage(wizard, stateModel);
|
const integrationRuntimePage = new IntergrationRuntimePage(wizard, stateModel);
|
||||||
@@ -45,6 +47,7 @@ export class WizardController {
|
|||||||
// subscriptionSelectionPage,
|
// subscriptionSelectionPage,
|
||||||
azureAccountsPage,
|
azureAccountsPage,
|
||||||
skuRecommendationPage,
|
skuRecommendationPage,
|
||||||
|
migrationModePage,
|
||||||
databaseBackupPage,
|
databaseBackupPage,
|
||||||
integrationRuntimePage,
|
integrationRuntimePage,
|
||||||
summaryPage
|
summaryPage
|
||||||
@@ -114,7 +117,7 @@ export function createHeadingTextComponent(view: azdata.ModelView, value: string
|
|||||||
export function creaetLabelTextComponent(view: azdata.ModelView, value: string): azdata.TextComponent {
|
export function creaetLabelTextComponent(view: azdata.ModelView, value: string): azdata.TextComponent {
|
||||||
const component = createTextCompononent(view, value);
|
const component = createTextCompononent(view, value);
|
||||||
component.updateCssStyles({
|
component.updateCssStyles({
|
||||||
'width': '250px'
|
'width': '300px'
|
||||||
});
|
});
|
||||||
return component;
|
return component;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user