Fixing bugs for migration extension private preview 1. (#14872)

* Fixing Database backup page target layout

* Filtering out Azure sql db issues from assessment results
Correcting the database count for issued databases in sku rec page.

* Adding copy migration details button to migration status

* Adding start migration button to toolbar

* Fixing a syntax error in package.json

* Adding rg and location to target selection page
Filtering storage account by target location.

* Fixing dashboard title to azure sql migration

* Not making assessment targets selected by default.

* Adding tooltip for database and instance table items.

* Fixing duplicate task widget

* Some fixes mentioned in the PR
Localizing button text
renaming  a var
changing null to undefined.

* Adding enum for Migration target types

* Fixing a critical multi db migration bug because of unhandled race condition

* Adding Azure location api to azure core

* Adding source database info in status
This commit is contained in:
Aasim Khan
2021-03-26 10:32:28 -07:00
committed by GitHub
parent e0f24cc268
commit 4d78aefe57
15 changed files with 336 additions and 198 deletions

View File

@@ -332,12 +332,32 @@ export class DatabaseBackupPage extends MigrationWizardPage {
this.migrationStateModel._databaseBackup.storageAccount = this.migrationStateModel.getStorageAccount(value.index);
}
});
const networkLocationInputBoxLabel = this._view.modelBuilder.text().withProps({
value: constants.DATABASE_BACKUP_NETWORK_SHARE_LOCATION_LABEL,
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.networkShareLocation = value;
});
const networkShareDatabaseConfigHeader = view.modelBuilder.text().withProps({
value: constants.ENTER_NETWORK_SHARE_INFORMATION
}).component();
this._networkShareDatabaseConfigContainer = view.modelBuilder.flexContainer().withLayout({
flexFlow: 'column'
}).component();
@@ -347,14 +367,16 @@ export class DatabaseBackupPage extends MigrationWizardPage {
[
azureAccountHelpText,
networkShareHelpText,
subscriptionLabel,
this._networkShareContainerSubscriptionDropdown,
storageAccountLabel,
this._networkShareContainerStorageAccountDropdown,
networkLocationInputBoxLabel,
networkLocationInputBox,
windowsUserAccountLabel,
this._windowsUserAccountText,
passwordLabel,
this._passwordText,
subscriptionLabel,
this._networkShareContainerSubscriptionDropdown,
storageAccountLabel,
this._networkShareContainerStorageAccountDropdown,
networkShareDatabaseConfigHeader,
this._networkShareDatabaseConfigContainer
]
@@ -374,7 +396,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
this._fileShareDropdowns = [];
this._blobContainerDropdowns = [];
this.migrationStateModel._targetDatabaseNames = [];
this.migrationStateModel._databaseBackup.networkShareLocations = [];
this.migrationStateModel._databaseBackup.fileShares = [];
this.migrationStateModel._databaseBackup.blobContainers = [];
this._networkShareDatabaseConfigContainer.clearItems();
@@ -389,42 +410,16 @@ export class DatabaseBackupPage extends MigrationWizardPage {
requiredIndicator: true
}).component();
const targetNameNetworkInputBox = this._view.modelBuilder.inputBox().withProps({
required: true
required: true,
value: db
}).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
targetNameNetworkInputBox
]
);

View File

@@ -26,6 +26,8 @@ export class SKURecommendationPage extends MigrationWizardPage {
private _chooseTargetComponent!: azdata.DivContainer;
private _azureSubscriptionText!: azdata.TextComponent;
private _managedInstanceSubscriptionDropdown!: azdata.DropDownComponent;
private _azureLocationDropdown!: azdata.DropDownComponent;
private _azureResourceGroupDropdown!: azdata.DropDownComponent;
private _resourceDropdownLabel!: azdata.TextComponent;
private _resourceDropdown!: azdata.DropDownComponent;
private _rbg!: azdata.RadioCardGroupComponent;
@@ -66,6 +68,35 @@ export class SKURecommendationPage extends MigrationWizardPage {
this.migrationStateModel._targetSubscription = this.migrationStateModel.getSubscription(e.index);
this.migrationStateModel._targetServerInstance = undefined!;
this.migrationStateModel._sqlMigrationService = undefined!;
this.populateLocationAndResourceGroupDropdown();
}
});
this._resourceDropdownLabel = view.modelBuilder.text().withProps({
value: constants.MANAGED_INSTANCE
}).component();
const azureLocationLabel = view.modelBuilder.text().withProps({
value: constants.LOCATION
}).component();
this._azureLocationDropdown = view.modelBuilder.dropDown().component();
this._azureLocationDropdown.onValueChanged((e) => {
if (e.selected) {
this.migrationStateModel._location = this.migrationStateModel.getLocation(e.index);
this.populateResourceInstanceDropdown();
}
});
this._resourceDropdownLabel = view.modelBuilder.text().withProps({
value: constants.MANAGED_INSTANCE
}).component();
const azureResourceGroupLabel = view.modelBuilder.text().withProps({
value: constants.RESOURCE_GROUP
}).component();
this._azureResourceGroupDropdown = view.modelBuilder.dropDown().component();
this._azureResourceGroupDropdown.onValueChanged((e) => {
if (e.selected) {
this.migrationStateModel._resourceGroup = this.migrationStateModel.getAzureResourceGroup(e.index);
this.populateResourceInstanceDropdown();
}
});
@@ -91,6 +122,10 @@ export class SKURecommendationPage extends MigrationWizardPage {
[
managedInstanceSubscriptionDropdownLabel,
this._managedInstanceSubscriptionDropdown,
azureLocationLabel,
this._azureLocationDropdown,
azureResourceGroupLabel,
this._azureResourceGroupDropdown,
this._resourceDropdownLabel,
this._resourceDropdown
]
@@ -284,16 +319,30 @@ export class SKURecommendationPage extends MigrationWizardPage {
}
}
public async populateLocationAndResourceGroupDropdown(): Promise<void> {
this._azureResourceGroupDropdown.loading = true;
this._azureLocationDropdown.loading = true;
try {
this._azureResourceGroupDropdown.values = await this.migrationStateModel.getAzureResourceGroupDropdownValues(this.migrationStateModel._targetSubscription);
this._azureLocationDropdown.values = await this.migrationStateModel.getAzureLocationDropdownValues(this.migrationStateModel._targetSubscription);
} catch (e) {
console.log(e);
} finally {
this._azureResourceGroupDropdown.loading = false;
this._azureLocationDropdown.loading = false;
}
}
private async populateResourceInstanceDropdown(): Promise<void> {
this._resourceDropdown.loading = true;
try {
if (this._rbg.selectedCardId === MigrationTargetType.SQLVM) {
this._resourceDropdownLabel.value = constants.AZURE_SQL_DATABASE_VIRTUAL_MACHINE;
this._resourceDropdown.values = await this.migrationStateModel.getSqlVirtualMachineValues(this.migrationStateModel._targetSubscription);
this._resourceDropdown.values = await this.migrationStateModel.getSqlVirtualMachineValues(this.migrationStateModel._targetSubscription, this.migrationStateModel._location, this.migrationStateModel._resourceGroup);
} else {
this._resourceDropdownLabel.value = constants.AZURE_SQL_DATABASE_MANAGED_INSTANCE;
this._resourceDropdown.values = await this.migrationStateModel.getManagedInstanceValues(this.migrationStateModel._targetSubscription);
this._resourceDropdown.values = await this.migrationStateModel.getManagedInstanceValues(this.migrationStateModel._targetSubscription, this.migrationStateModel._location, this.migrationStateModel._resourceGroup);
}
} catch (e) {
console.log(e);
@@ -322,6 +371,13 @@ export class SKURecommendationPage extends MigrationWizardPage {
if ((<azdata.CategoryValue>this._managedInstanceSubscriptionDropdown.value).displayName === constants.NO_SUBSCRIPTIONS_FOUND) {
errors.push(constants.INVALID_SUBSCRIPTION_ERROR);
}
if ((<azdata.CategoryValue>this._azureLocationDropdown.value).displayName === constants.NO_LOCATION_FOUND) {
errors.push(constants.INVALID_LOCATION_ERROR);
}
if ((<azdata.CategoryValue>this._managedInstanceSubscriptionDropdown.value).displayName === constants.RESOURCE_GROUP_NOT_FOUND) {
errors.push(constants.INVALID_RESOURCE_GROUP_ERROR);
}
const resourceDropdownValue = (<azdata.CategoryValue>this._resourceDropdown.value).displayName;
if (resourceDropdownValue === constants.NO_MANAGED_INSTANCE_FOUND) {
errors.push(constants.NO_MANAGED_INSTANCE_FOUND);
@@ -377,8 +433,8 @@ export class SKURecommendationPage extends MigrationWizardPage {
if (this.migrationStateModel._assessmentResults) {
const dbCount = this.migrationStateModel._assessmentResults.databaseAssessments.length;
const dbWithIssuesCount = this.migrationStateModel._assessmentResults.databaseAssessments.filter(db => db.issues.length > 0).length;
const miCardText = `${dbWithIssuesCount} out of ${dbCount} databases can be migrated (${this.migrationStateModel._miDbs.length} selected)`;
const dbWithoutIssuesCount = this.migrationStateModel._assessmentResults.databaseAssessments.filter(db => db.issues.length === 0).length;
const miCardText = `${dbWithoutIssuesCount} out of ${dbCount} databases can be migrated (${this.migrationStateModel._miDbs.length} selected)`;
this._rbg.cards[0].descriptions[1].textValue = miCardText;
const vmCardText = `${dbCount} out of ${dbCount} databases can be migrated (${this.migrationStateModel._vmDbs.length} selected)`;

View File

@@ -72,6 +72,7 @@ export class SummaryPage extends MigrationWizardPage {
flexContainer.addItems(
[
createInformationRow(this._view, constants.TYPE, constants.NETWORK_SHARE),
createInformationRow(this._view, constants.DATABASE_BACKUP_NETWORK_SHARE_LOCATION_LABEL, 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),
@@ -80,7 +81,6 @@ export class SummaryPage extends MigrationWizardPage {
);
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:

View File

@@ -1,111 +0,0 @@
/*---------------------------------------------------------------------------------------------
* 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 { MigrationWizardPage } from '../models/migrationWizardPage';
import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
import * as constants from '../constants/strings';
import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
export class TempTargetSelectionPage extends MigrationWizardPage {
private _managedInstanceSubscriptionDropdown!: azdata.DropDownComponent;
private _managedInstanceDropdown!: azdata.DropDownComponent;
constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) {
super(wizard, azdata.window.createWizardPage(constants.TARGET_SELECTION_PAGE_TITLE), migrationStateModel);
}
protected async registerContent(view: azdata.ModelView): Promise<void> {
const managedInstanceSubscriptionDropdownLabel = view.modelBuilder.text()
.withProps({
value: constants.SUBSCRIPTION
}).component();
this._managedInstanceSubscriptionDropdown = view.modelBuilder.dropDown()
.withProps({
width: WIZARD_INPUT_COMPONENT_WIDTH
}).component();
this._managedInstanceSubscriptionDropdown.onValueChanged((e) => {
if (e.selected) {
this.migrationStateModel._targetSubscription = this.migrationStateModel.getSubscription(e.index);
this.migrationStateModel._targetServerInstance = undefined!;
this.migrationStateModel._sqlMigrationService = undefined!;
this.populateManagedInstanceDropdown();
}
});
const managedInstanceDropdownLabel = view.modelBuilder.text().withProps({
value: constants.MANAGED_INSTANCE
}).component();
this._managedInstanceDropdown = view.modelBuilder.dropDown()
.withProps({
width: WIZARD_INPUT_COMPONENT_WIDTH
}).component();
this._managedInstanceDropdown.onValueChanged((e) => {
if (e.selected) {
this.migrationStateModel._sqlMigrationServices = undefined!;
this.migrationStateModel._targetServerInstance = this.migrationStateModel.getManagedInstance(e.index);
}
});
const targetContainer = view.modelBuilder.flexContainer().withItems(
[
managedInstanceSubscriptionDropdownLabel,
this._managedInstanceSubscriptionDropdown,
managedInstanceDropdownLabel,
this._managedInstanceDropdown
]
).withLayout({
flexFlow: 'column'
}).component();
const form = view.modelBuilder.formContainer()
.withFormItems(
[
{
component: targetContainer
}
]
);
await view.initializeModel(form.component());
}
public async onPageEnter(): Promise<void> {
this.populateSubscriptionDropdown();
}
public async onPageLeave(): Promise<void> {
}
protected async handleStateChange(e: StateChangeEvent): Promise<void> {
}
private async populateSubscriptionDropdown(): Promise<void> {
if (!this.migrationStateModel._targetSubscription) {
this._managedInstanceSubscriptionDropdown.loading = true;
this._managedInstanceDropdown.loading = true;
try {
this._managedInstanceSubscriptionDropdown.values = await this.migrationStateModel.getSubscriptionsDropdownValues();
} catch (e) {
console.log(e);
} finally {
this._managedInstanceSubscriptionDropdown.loading = false;
}
}
}
private async populateManagedInstanceDropdown(): Promise<void> {
if (!this.migrationStateModel._targetServerInstance) {
this._managedInstanceDropdown.loading = true;
try {
this._managedInstanceDropdown.values = await this.migrationStateModel.getManagedInstanceValues(this.migrationStateModel._targetSubscription);
} catch (e) {
console.log(e);
} finally {
this._managedInstanceDropdown.loading = false;
}
}
}
}