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:
Aasim Khan
2021-03-09 23:40:52 -08:00
committed by GitHub
parent c05ba883c4
commit bfa8ec0301
17 changed files with 492 additions and 199 deletions

View File

@@ -7,27 +7,31 @@ import * as azdata from 'azdata';
import { EOL } from 'os';
import { getStorageAccountAccessKeys } from '../api/azure';
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 vscode from 'vscode';
export class DatabaseBackupPage extends MigrationWizardPage {
private _view!: azdata.ModelView;
private _networkShareContainer!: azdata.FlexContainer;
private _networkShareContainerSubscriptionDropdown!: azdata.DropDownComponent;
private _networkShareContainerStorageAccountDropdown!: azdata.DropDownComponent;
private _networkShareLocationText!: azdata.InputBoxComponent;
private _windowsUserAccountText!: azdata.InputBoxComponent;
private _passwordText!: azdata.InputBoxComponent;
private _networkShareDatabaseConfigContainer!: azdata.FlexContainer;
private _networkShareLocations!: azdata.InputBoxComponent[];
private _blobContainer!: azdata.FlexContainer;
private _blobContainerSubscriptionDropdown!: azdata.DropDownComponent;
private _blobContainerStorageAccountDropdown!: azdata.DropDownComponent;
private _blobContainerBlobDropdown!: azdata.DropDownComponent;
private _blobContainerDatabaseConfigContainer!: azdata.FlexContainer;
private _blobContainerDropdowns!: azdata.DropDownComponent[];
private _fileShareContainer!: azdata.FlexContainer;
private _fileShareSubscriptionDropdown!: 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) {
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> {
this._view = view;
this._networkShareContainer = this.createNetworkShareContainer(view);
this._blobContainer = this.createBlobContainer(view);
this._fileShareContainer = this.createFileShareContainer(view);
@@ -56,7 +60,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
title: '',
component: networkContainer
},
this.migrationModeContainer(view),
]
);
await view.initializeModel(form.component());
@@ -68,7 +71,8 @@ export class DatabaseBackupPage extends MigrationWizardPage {
const networkShareButton = view.modelBuilder.radioButton()
.withProps({
name: buttonGroup,
label: constants.DATABASE_BACKUP_NC_NETWORK_SHARE_RADIO_LABEL
label: constants.DATABASE_BACKUP_NC_NETWORK_SHARE_RADIO_LABEL,
checked: true
}).component();
networkShareButton.onDidChangeCheckedState((e) => {
@@ -150,26 +154,19 @@ export class DatabaseBackupPage extends MigrationWizardPage {
this._fileShareStorageAccountDropdown.onValueChanged(async (value) => {
if (value.selected) {
this.migrationStateModel._databaseBackup.storageAccount = this.migrationStateModel.getStorageAccount(value.index);
this.migrationStateModel._databaseBackup.fileShare = undefined!;
this.migrationStateModel._databaseBackup.fileShares = undefined!;
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()
.withItems(
@@ -178,8 +175,8 @@ export class DatabaseBackupPage extends MigrationWizardPage {
this._fileShareSubscriptionDropdown,
storageAccountLabel,
this._fileShareStorageAccountDropdown,
fileShareLabel,
this._fileShareFileShareDropdown
fileShareDatabaseConfigHeader,
this._fileShareDatabaseConfigContainer
]
).withLayout({
flexFlow: 'column'
@@ -220,24 +217,19 @@ export class DatabaseBackupPage extends MigrationWizardPage {
this._blobContainerStorageAccountDropdown.onValueChanged(async (value) => {
if (value.selected) {
this.migrationStateModel._databaseBackup.storageAccount = this.migrationStateModel.getStorageAccount(value.index);
this.migrationStateModel._databaseBackup.blobContainer = undefined!;
this.migrationStateModel._databaseBackup.blobContainers = undefined!;
await this.loadBlobContainerDropdown();
}
});
const containerLabel = view.modelBuilder.text().withProps({
value: constants.DATABASE_BACKUP_BLOB_STORAGE_ACCOUNT_CONTAINER_LABEL,
requiredIndicator: true,
const blobContainerDatabaseConfigHeader = view.modelBuilder.text().withProps({
value: constants.ENTER_BLOB_CONTAINER_INFORMATION
}).component();
this._blobContainerDatabaseConfigContainer = view.modelBuilder.flexContainer().withLayout({
flexFlow: 'column'
}).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()
.withItems(
@@ -246,8 +238,8 @@ export class DatabaseBackupPage extends MigrationWizardPage {
this._blobContainerSubscriptionDropdown,
storageAccountLabel,
this._blobContainerStorageAccountDropdown,
containerLabel,
this._blobContainerBlobDropdown
blobContainerDatabaseConfigHeader,
this._blobContainerDatabaseConfigContainer
]
).withLayout({
flexFlow: 'column'
@@ -264,31 +256,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
value: constants.DATABASE_BACKUP_NC_NETWORK_SHARE_HELP_TEXT,
}).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()
.withProps({
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(
[
azureAccountHelpText,
networkShareHelpText,
networkShareLocationLabel,
this._networkShareLocationText,
subscriptionLabel,
this._networkShareContainerSubscriptionDropdown,
storageAccountLabel,
this._networkShareContainerStorageAccountDropdown,
windowsUserAccountLabel,
this._windowsUserAccountText,
passwordLabel,
this._passwordText,
azureAccountHelpText,
subscriptionLabel,
this._networkShareContainerSubscriptionDropdown,
storageAccountLabel,
this._networkShareContainerStorageAccountDropdown
networkShareDatabaseConfigHeader,
this._networkShareDatabaseConfigContainer
]
).withLayout({
flexFlow: 'column'
@@ -390,57 +367,130 @@ export class DatabaseBackupPage extends MigrationWizardPage {
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> {
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();
this.wizard.registerNavigationValidator((pageChangeInfo) => {
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) {
errors.push(constants.INVALID_STORAGE_ACCOUNT_ERROR);
}
if ((<azdata.CategoryValue>this._blobContainerBlobDropdown.value).displayName === constants.NO_BLOBCONTAINERS_FOUND) {
errors.push(constants.INVALID_BLOBCONTAINER_ERROR);
for (let i = 0; i < this._blobContainerDropdowns.length; i++) {
if ((<azdata.CategoryValue>this._blobContainerDropdowns[i].value).displayName === constants.NO_BLOBCONTAINERS_FOUND) {
errors.push(constants.INVALID_BLOBCONTAINER_ERROR);
break;
}
}
break;
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) {
errors.push(constants.INVALID_STORAGE_ACCOUNT_ERROR);
}
if ((<azdata.CategoryValue>this._fileShareFileShareDropdown.value).displayName === constants.NO_FILESHARES_FOUND) {
errors.push(constants.INVALID_FILESHARE_ERROR);
for (let i = 0; i < this._fileShareDropdowns.length; i++) {
if ((<azdata.CategoryValue>this._fileShareDropdowns[i].value).displayName === constants.NO_FILESHARES_FOUND) {
errors.push(constants.INVALID_FILESHARE_ERROR);
break;
}
}
break;
}
@@ -508,8 +564,10 @@ export class DatabaseBackupPage extends MigrationWizardPage {
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._networkShareLocationText.updateProperties({
required: containerType === NetworkContainerType.NETWORK_SHARE
this._networkShareLocations.forEach((inputBox) => {
inputBox.updateProperties({
required: containerType === NetworkContainerType.NETWORK_SHARE
});
});
this._windowsUserAccountText.updateProperties({
required: containerType === NetworkContainerType.NETWORK_SHARE
@@ -517,21 +575,47 @@ export class DatabaseBackupPage extends MigrationWizardPage {
this._passwordText.updateProperties({
required: containerType === NetworkContainerType.NETWORK_SHARE
});
this._networkShareLocationText.validate();
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._blobContainerBlobDropdown.validate();
this._blobContainerDropdowns.forEach((dropdown) => {
dropdown.validate();
});
this._fileShareSubscriptionDropdown.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> {
if (!this.migrationStateModel._databaseBackup.subscription) {
this._networkShareContainerSubscriptionDropdown.loading = true;
@@ -568,14 +652,18 @@ export class DatabaseBackupPage extends MigrationWizardPage {
private async loadFileShareStorageDropdown(): Promise<void> {
if (!this.migrationStateModel._databaseBackup.storageAccount) {
this._fileShareStorageAccountDropdown.loading = true;
this._fileShareFileShareDropdown.loading = true;
this._fileShareDropdowns.forEach((dropdown) => {
dropdown.loading = true;
});
try {
this._fileShareStorageAccountDropdown.values = await this.migrationStateModel.getStorageAccountValues(this.migrationStateModel._databaseBackup.subscription);
} catch (error) {
console.log(error);
} finally {
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> {
if (!this.migrationStateModel._databaseBackup.storageAccount) {
this._blobContainerStorageAccountDropdown.loading = true;
this._blobContainerBlobDropdown.loading = true;
this._blobContainerDropdowns.forEach((dropdown) => {
dropdown.loading = true;
});
try {
this._blobContainerStorageAccountDropdown.values = await this.migrationStateModel.getStorageAccountValues(this.migrationStateModel._databaseBackup.subscription);
} catch (error) {
console.log(error);
} finally {
this._blobContainerStorageAccountDropdown.loading = false;
this._blobContainerBlobDropdown.loading = true;
this._blobContainerDropdowns.forEach((dropdown) => {
dropdown.loading = false;
});
}
}
}
private async loadFileShareDropdown(): Promise<void> {
if (!this.migrationStateModel._fileShares) {
this._fileShareFileShareDropdown.loading = true;
this._fileShareDropdowns.forEach((dropdown) => {
dropdown.loading = true;
});
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) {
console.log(error);
} finally {
this._fileShareFileShareDropdown.loading = false;
this._fileShareDropdowns.forEach((dropdown) => {
dropdown.loading = true;
});
}
}
}
private async loadBlobContainerDropdown(): Promise<void> {
if (!this.migrationStateModel._blobContainers) {
this._blobContainerBlobDropdown.loading = true;
this._blobContainerDropdowns.forEach((dropdown) => {
dropdown.loading = true;
});
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) {
console.log(error);
} finally {
this._blobContainerBlobDropdown.loading = false;
this._blobContainerDropdowns.forEach((dropdown) => {
dropdown.loading = false;
});
}
}
}

View File

@@ -132,7 +132,9 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
text: ''
};
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> {
this._statusLoadingComponent.loading = true;
try {
this._migrationDetailsContainer.clearItems();

View 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
};
}
}

View File

@@ -300,8 +300,11 @@ export class SKURecommendationPage extends MigrationWizardPage {
errors.push(constants.INVALID_SUBSCRIPTION_ERROR);
}
const resourceDropdownValue = (<azdata.CategoryValue>this._resourceDropdown.value).displayName;
if (resourceDropdownValue === constants.NO_MANAGED_INSTANCE_FOUND || resourceDropdownValue === constants.NO_VIRTUAL_MACHINE_FOUND) {
errors.push(constants.INVALID_STORAGE_ACCOUNT_ERROR);
if (resourceDropdownValue === constants.NO_MANAGED_INSTANCE_FOUND) {
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) {

View File

@@ -72,12 +72,16 @@ export class SummaryPage extends MigrationWizardPage {
flexContainer.addItems(
[
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.SUMMARY_AZURE_STORAGE_SUBSCRIPTION, this.migrationStateModel._databaseBackup.subscription.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;
case NetworkContainerType.FILE_SHARE:
flexContainer.addItems(
@@ -85,19 +89,25 @@ export class SummaryPage extends MigrationWizardPage {
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, 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;
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.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.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;
}

View File

@@ -14,6 +14,7 @@ import { DatabaseBackupPage } from './databaseBackupPage';
import { AccountsSelectionPage } from './accountsSelectionPage';
import { IntergrationRuntimePage } from './integrationRuntimePage';
import { SummaryPage } from './summaryPage';
import { MigrationModePage } from './migrationModePage';
export const WIZARD_INPUT_COMPONENT_WIDTH = '400px';
export class WizardController {
@@ -36,6 +37,7 @@ export class WizardController {
wizard.generateScriptButton.hidden = true;
const skuRecommendationPage = new SKURecommendationPage(wizard, stateModel);
// const subscriptionSelectionPage = new SubscriptionSelectionPage(wizard, stateModel);
const migrationModePage = new MigrationModePage(wizard, stateModel);
const azureAccountsPage = new AccountsSelectionPage(wizard, stateModel);
const databaseBackupPage = new DatabaseBackupPage(wizard, stateModel);
const integrationRuntimePage = new IntergrationRuntimePage(wizard, stateModel);
@@ -45,6 +47,7 @@ export class WizardController {
// subscriptionSelectionPage,
azureAccountsPage,
skuRecommendationPage,
migrationModePage,
databaseBackupPage,
integrationRuntimePage,
summaryPage
@@ -114,7 +117,7 @@ export function createHeadingTextComponent(view: azdata.ModelView, value: string
export function creaetLabelTextComponent(view: azdata.ModelView, value: string): azdata.TextComponent {
const component = createTextCompononent(view, value);
component.updateCssStyles({
'width': '250px'
'width': '300px'
});
return component;
}