Improvements in blob storage support for SQL Migration. (#15693)

* changing the cutover icon on migration cutover page.

* Fixing monitoring table and pending log backups

* converting file upload times in utc to local time zones

* adding autorefresh to dashboard, migration status and cutover dialogs.

* Supporting blob container e2e

* vbump extension

* Fixing some PR comments

* Fixed broken blob container dropdown onChange event

* Localizing display string in refresh dialog
Fixing some localized strings

* Fixing var declaration

* making a class readonly for 250px width

* removing refresh interval dialog and replacing it with hardcoded values.

* Fixing summary page IR information.

* surfacing test connection error

* Clearing intervals on view closed to remove auto refresh.
This commit is contained in:
Aasim Khan
2021-06-17 22:19:42 -07:00
committed by GitHub
parent 35832e83da
commit 488ccea731
16 changed files with 458 additions and 228 deletions

View File

@@ -684,6 +684,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
}).component();
targetDatabaseInput.onTextChanged((value) => {
this.migrationStateModel._targetDatabaseNames[index] = value.trim();
this.validateFields();
});
this._networkShareTargetDatabaseNames.push(targetDatabaseInput);
@@ -749,7 +750,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
fireOnTextChange: true,
}).component();
blobContainerDropdown.onValueChanged(value => {
const selectedIndex = findDropDownItemIndex(blobContainerStorageAccountDropdown, value);
const selectedIndex = findDropDownItemIndex(blobContainerDropdown, value);
if (selectedIndex > -1 && value !== constants.NO_BLOBCONTAINERS_FOUND) {
this.migrationStateModel._databaseBackup.blobs[index].blobContainer = this.migrationStateModel.getBlobContainer(selectedIndex);
}
@@ -830,23 +831,24 @@ export class DatabaseBackupPage extends MigrationWizardPage {
}
});
const duplicates: Map<string, number[]> = new Map();
for (let i = 0; i < this.migrationStateModel._targetDatabaseNames.length; i++) {
const blobContainerId = this.migrationStateModel._databaseBackup.blobs[i].blobContainer.id;
if (duplicates.has(blobContainerId)) {
duplicates.get(blobContainerId)?.push(i);
} else {
duplicates.set(blobContainerId, [i]);
if (errors.length > 0) {
const duplicates: Map<string, number[]> = new Map();
for (let i = 0; i < this.migrationStateModel._targetDatabaseNames.length; i++) {
const blobContainerId = this.migrationStateModel._databaseBackup.blobs[i].blobContainer?.id;
if (duplicates.has(blobContainerId)) {
duplicates.get(blobContainerId)?.push(i);
} else {
duplicates.set(blobContainerId, [i]);
}
}
duplicates.forEach((d) => {
if (d.length > 1) {
const dupString = `${d.map(index => this.migrationStateModel._migrationDbs[index]).join(', ')}`;
errors.push(constants.PROVIDE_UNIQUE_CONTAINERS + dupString);
}
});
}
duplicates.forEach((d) => {
if (d.length > 1) {
const dupString = `${d.map(index => this.migrationStateModel._migrationDbs[index]).join(', ')}`;
errors.push(constants.PROVIDE_UNIQUE_CONTAINERS + dupString);
}
});
break;
}

View File

@@ -6,11 +6,11 @@
import * as azdata from 'azdata';
import * as vscode from 'vscode';
import { MigrationWizardPage } from '../models/migrationWizardPage';
import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
import { MigrationStateModel, NetworkContainerType, StateChangeEvent } from '../models/stateMachine';
import { CreateSqlMigrationServiceDialog } from '../dialog/createSqlMigrationService/createSqlMigrationServiceDialog';
import * as constants from '../constants/strings';
import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
import { getLocationDisplayName, getSqlMigrationService, getSqlMigrationServiceAuthKeys, getSqlMigrationServiceMonitoringData, SqlManagedInstance, SqlMigrationService } from '../api/azure';
import { getLocationDisplayName, getSqlMigrationService, getSqlMigrationServiceAuthKeys, getSqlMigrationServiceMonitoringData, SqlManagedInstance } from '../api/azure';
import { IconPathHelper } from '../constants/iconPathHelper';
import { findDropDownItemIndex } from '../api/utils';
@@ -24,6 +24,7 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
private _resourceGroupDropdown!: azdata.DropDownComponent;
private _dmsDropdown!: azdata.DropDownComponent;
private _dmsInfoContainer!: azdata.FlexContainer;
private _dmsStatusInfoBox!: azdata.InfoBoxComponent;
private _authKeyTable!: azdata.DeclarativeTableComponent;
private _refreshButton!: azdata.ButtonComponent;
@@ -34,8 +35,6 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
private _refresh1!: azdata.ButtonComponent;
private _refresh2!: azdata.ButtonComponent;
private _firstEnter: boolean = true;
constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) {
super(wizard, azdata.window.createWizardPage(constants.IR_PAGE_TITLE), migrationStateModel);
}
@@ -51,13 +50,20 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
}
}).component();
createNewMigrationService.onDidClick((e) => {
const dialog = new CreateSqlMigrationServiceDialog(this.migrationStateModel, this);
dialog.initialize();
createNewMigrationService.onDidClick(async (e) => {
const dialog = new CreateSqlMigrationServiceDialog();
const createdDmsResult = await dialog.createNewDms(this.migrationStateModel, (<azdata.CategoryValue>this._resourceGroupDropdown.value).displayName);
this.migrationStateModel._sqlMigrationServiceResourceGroup = createdDmsResult.resourceGroup;
this.migrationStateModel._sqlMigrationService = createdDmsResult.service;
await this.loadResourceGroupDropdown();
await this.populateDms(createdDmsResult.resourceGroup);
});
this._statusLoadingComponent = view.modelBuilder.loadingComponent().withItem(this.createDMSDetailsContainer()).component();
this._dmsInfoContainer = this._view.modelBuilder.flexContainer().withItems([
this._statusLoadingComponent
]).component();
const dmsPortalInfo = this._view.modelBuilder.infoBox().withProps({
text: constants.DMS_PORTAL_INFO,
style: 'information',
@@ -80,7 +86,7 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
component: dmsPortalInfo
},
{
component: this._statusLoadingComponent
component: this._dmsInfoContainer
}
]
@@ -89,10 +95,11 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
}
public async onPageEnter(): Promise<void> {
if (this._firstEnter) {
this.populateMigrationService();
this._firstEnter = false;
}
this._subscription.value = this.migrationStateModel._targetSubscription.name;
this._location.value = await getLocationDisplayName(this.migrationStateModel._targetServerInstance.location);
this.loadResourceGroupDropdown();
this._dmsInfoContainer.display = (this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE) ? 'inline' : 'none';
this.wizard.registerNavigationValidator((pageChangeInfo) => {
if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
this.wizard.message = {
@@ -108,7 +115,7 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
};
return false;
}
if (state !== 'Online') {
if (this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE && state !== 'Online') {
this.wizard.message = {
level: azdata.window.MessageLevel.Error,
text: constants.SERVICE_OFFLINE_ERROR
@@ -202,6 +209,9 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
this._dmsDropdown.onValueChanged(async (value) => {
if (value && value !== constants.SQL_MIGRATION_SERVICE_NOT_FOUND_ERROR) {
if (this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE) {
this._dmsInfoContainer.display = 'inline';
}
this.wizard.message = {
text: ''
};
@@ -210,6 +220,8 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
this.migrationStateModel._sqlMigrationService = this.migrationStateModel.getMigrationService(selectedIndex);
await this.loadMigrationServiceStatus();
}
} else {
this._dmsInfoContainer.display = 'none';
}
});
@@ -254,8 +266,11 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
this._refreshButton.onDidClick(async (e) => {
this._connectionStatusLoader.loading = true;
await this.loadStatus();
this._connectionStatusLoader.loading = false;
try {
await this.loadStatus();
} finally {
this._connectionStatusLoader.loading = false;
}
});
const connectionLabelContainer = this._view.modelBuilder.flexContainer().withProps({
@@ -387,53 +402,24 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
return container;
}
public async populateMigrationService(sqlMigrationService?: SqlMigrationService, serviceNodes?: string[], resourceGroupName?: string): Promise<void> {
public async loadResourceGroupDropdown(): Promise<void> {
this._resourceGroupDropdown.loading = true;
this._dmsDropdown.loading = true;
if (sqlMigrationService && serviceNodes) {
this.migrationStateModel._sqlMigrationService = sqlMigrationService;
this.migrationStateModel._nodeNames = serviceNodes;
}
try {
this._subscription.value = this.migrationStateModel._targetSubscription.name;
this._location.value = await getLocationDisplayName(this.migrationStateModel._targetServerInstance.location);
this._resourceGroupDropdown.values = await this.migrationStateModel.getAzureResourceGroupDropdownValues(this.migrationStateModel._targetSubscription);
let index = 0;
if (resourceGroupName) {
index = findDropDownItemIndex(this._resourceGroupDropdown, resourceGroupName);
}
if ((<azdata.CategoryValue>this._resourceGroupDropdown.value)?.displayName.toLowerCase() === (<azdata.CategoryValue>this._resourceGroupDropdown.values[index])?.displayName.toLowerCase()) {
await this.populateDms((<azdata.CategoryValue>this._resourceGroupDropdown.value)?.displayName);
} else {
this._resourceGroupDropdown.value = this._resourceGroupDropdown.values[index];
}
} catch (error) {
console.log(error);
const resourceGroupDropdownValue = this._resourceGroupDropdown.values.find(v => v.displayName === this.migrationStateModel._sqlMigrationServiceResourceGroup);
this._resourceGroupDropdown.value = (resourceGroupDropdownValue) ? resourceGroupDropdownValue : this._resourceGroupDropdown.values[0];
} finally {
this._resourceGroupDropdown.loading = false;
}
}
public async populateDms(resourceGroupName: string): Promise<void> {
if (!resourceGroupName) {
return;
}
this._dmsDropdown.loading = true;
try {
this._dmsDropdown.values = await this.migrationStateModel.getSqlMigrationServiceValues(this.migrationStateModel._targetSubscription, <SqlManagedInstance>this.migrationStateModel._targetServerInstance, resourceGroupName);
let index = -1;
if (this.migrationStateModel._sqlMigrationService) {
index = findDropDownItemIndex(this._dmsDropdown, this.migrationStateModel._sqlMigrationService.name);
}
if (index > -1) {
this._dmsDropdown.value = this._dmsDropdown.values[index];
} else {
this._dmsDropdown.value = this._dmsDropdown.values[0];
}
} catch (e) {
console.log(e);
const selectedSqlMigrationService = this._dmsDropdown.values.find(v => v.displayName.toLowerCase() === this.migrationStateModel._sqlMigrationService?.name.toLowerCase());
this._dmsDropdown.value = (selectedSqlMigrationService) ? selectedSqlMigrationService : this._dmsDropdown.values[0];
} finally {
this._dmsDropdown.loading = false;
}

View File

@@ -61,10 +61,13 @@ export class SummaryPage extends MigrationWizardPage {
createInformationRow(this._view, constants.LOCATION, this.migrationStateModel._sqlMigrationService.location),
createInformationRow(this._view, constants.SUBSCRIPTION, this.migrationStateModel._sqlMigrationService.properties.resourceGroup),
createInformationRow(this._view, constants.IR_PAGE_TITLE, this.migrationStateModel._targetSubscription.name),
createInformationRow(this._view, constants.SUBSCRIPTION, this.migrationStateModel._sqlMigrationService.name),
createInformationRow(this._view, constants.SHIR, this.migrationStateModel._nodeNames[0]),
createInformationRow(this._view, constants.SUBSCRIPTION, this.migrationStateModel._sqlMigrationService.name)
]
);
if (this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE && this.migrationStateModel._nodeNames.length > 0) {
this._flexContainer.addItem(createInformationRow(this._view, constants.SHIR, this.migrationStateModel._nodeNames.join(', ')));
}
}
public async onPageLeave(): Promise<void> {