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

@@ -8,7 +8,7 @@ import { azureResource } from 'azureResource';
import * as azurecore from 'azurecore';
import * as vscode from 'vscode';
import * as mssql from '../../../mssql';
import { getAvailableManagedInstanceProducts, getAvailableStorageAccounts, getBlobContainers, getFileShares, getSqlMigrationServices, getSubscriptions, SqlMigrationService, SqlManagedInstance, startDatabaseMigration, StartDatabaseMigrationRequest, StorageAccount, getAvailableSqlVMs, SqlVMServer } from '../api/azure';
import { getAvailableManagedInstanceProducts, getAvailableStorageAccounts, getBlobContainers, getFileShares, getSqlMigrationServices, getSubscriptions, SqlMigrationService, SqlManagedInstance, startDatabaseMigration, StartDatabaseMigrationRequest, StorageAccount, getAvailableSqlVMs, SqlVMServer, getLocations, getResourceGroups } from '../api/azure';
import { SKURecommendations } from './externalContract';
import * as constants from '../constants/strings';
import { MigrationLocalStorage } from './migrationLocalStorage';
@@ -34,8 +34,9 @@ export enum State {
}
export enum MigrationTargetType {
SQLVM = 'sqlvm',
SQLMI = 'sqlmi'
SQLVM = 'AzureSqlVirtualMachine',
SQLMI = 'AzureSqlManagedInstance',
SQLDB = 'AzureSqlDatabase'
}
export enum MigrationSourceAuthenticationType {
@@ -63,7 +64,7 @@ export interface NetworkShare {
export interface DatabaseBackupModel {
migrationCutover: MigrationCutover;
networkContainerType: NetworkContainerType;
networkShareLocations: string[];
networkShareLocation: string;
windowsUser: string;
password: string;
subscription: azureResource.AzureResourceSubscription;
@@ -100,6 +101,10 @@ export class MigrationStateModel implements Model, vscode.Disposable {
public _subscriptions!: azureResource.AzureResourceSubscription[];
public _targetSubscription!: azureResource.AzureResourceSubscription;
public _locations!: azureResource.AzureLocation[];
public _location!: azureResource.AzureLocation;
public _resourceGroups!: azureResource.AzureResourceResourceGroup[];
public _resourceGroup!: azureResource.AzureResourceResourceGroup;
public _targetManagedInstances!: SqlManagedInstance[];
public _targetSqlVirtualMachines!: SqlVMServer[];
public _targetServerInstance!: SqlManagedInstance;
@@ -173,11 +178,13 @@ export class MigrationStateModel implements Model, vscode.Disposable {
});
assessmentResults?.items.forEach((item) => {
const dbIndex = serverDatabases.indexOf(item.databaseName);
if (dbIndex === -1) {
serverLevelAssessments.push(item);
} else {
databaseLevelAssessments[dbIndex].issues.push(item);
if (item.appliesToMigrationTargetPlatform === MigrationTargetType.SQLMI) {
const dbIndex = serverDatabases.indexOf(item.databaseName);
if (dbIndex === -1) {
serverLevelAssessments.push(item);
} else {
databaseLevelAssessments[dbIndex].issues.push(item);
}
}
});
@@ -313,10 +320,88 @@ export class MigrationStateModel implements Model, vscode.Disposable {
return this._subscriptions[index];
}
public async getManagedInstanceValues(subscription: azureResource.AzureResourceSubscription): Promise<azdata.CategoryValue[]> {
public async getAzureLocationDropdownValues(subscription: azureResource.AzureResourceSubscription): Promise<azdata.CategoryValue[]> {
let locationValues: azdata.CategoryValue[] = [];
try {
this._locations = await getLocations(this._azureAccount, subscription);
this._locations.forEach((loc) => {
locationValues.push({
name: loc.name,
displayName: loc.displayName
});
});
if (locationValues.length === 0) {
locationValues = [
{
displayName: constants.INVALID_LOCATION_ERROR,
name: ''
}
];
}
} catch (e) {
console.log(e);
locationValues = [
{
displayName: constants.INVALID_LOCATION_ERROR,
name: ''
}
];
}
return locationValues;
}
public getLocation(index: number): azureResource.AzureLocation {
return this._locations[index];
}
public async getAzureResourceGroupDropdownValues(subscription: azureResource.AzureResourceSubscription): Promise<azdata.CategoryValue[]> {
let resourceGroupValues: azdata.CategoryValue[] = [];
try {
this._resourceGroups = await getResourceGroups(this._azureAccount, subscription);
this._resourceGroups.forEach((rg) => {
resourceGroupValues.push({
name: rg.id,
displayName: rg.name
});
});
if (resourceGroupValues.length === 0) {
resourceGroupValues = [
{
displayName: constants.RESOURCE_GROUP_NOT_FOUND,
name: ''
}
];
}
} catch (e) {
console.log(e);
resourceGroupValues = [
{
displayName: constants.RESOURCE_GROUP_NOT_FOUND,
name: ''
}
];
}
return resourceGroupValues;
}
public getAzureResourceGroup(index: number): azureResource.AzureResourceResourceGroup {
return this._resourceGroups[index];
}
public async getManagedInstanceValues(subscription: azureResource.AzureResourceSubscription, location: azureResource.AzureLocation, resourceGroup: azureResource.AzureResourceResourceGroup): Promise<azdata.CategoryValue[]> {
let managedInstanceValues: azdata.CategoryValue[] = [];
try {
this._targetManagedInstances = await getAvailableManagedInstanceProducts(this._azureAccount, subscription);
this._targetManagedInstances = (await getAvailableManagedInstanceProducts(this._azureAccount, subscription)).filter((mi) => {
if (mi.location === location.name && mi.resourceGroup?.toLocaleLowerCase() === resourceGroup.name.toLowerCase()) {
return true;
}
return false;
});
this._targetManagedInstances.forEach((managedInstance) => {
managedInstanceValues.push({
name: managedInstance.id,
@@ -348,13 +433,19 @@ export class MigrationStateModel implements Model, vscode.Disposable {
return this._targetManagedInstances[index];
}
public async getSqlVirtualMachineValues(subscription: azureResource.AzureResourceSubscription): Promise<azdata.CategoryValue[]> {
public async getSqlVirtualMachineValues(subscription: azureResource.AzureResourceSubscription, location: azureResource.AzureLocation, resourceGroup: azureResource.AzureResourceResourceGroup): Promise<azdata.CategoryValue[]> {
let virtualMachineValues: azdata.CategoryValue[] = [];
try {
this._targetSqlVirtualMachines = await getAvailableSqlVMs(this._azureAccount, subscription);
virtualMachineValues = this._targetSqlVirtualMachines.filter((virtualMachine) => {
return virtualMachine.properties.sqlImageOffer.toLowerCase().includes('-ws'); //filtering out all non windows sql vms.
}).map((virtualMachine) => {
this._targetSqlVirtualMachines = (await getAvailableSqlVMs(this._azureAccount, subscription, resourceGroup)).filter((virtualMachine) => {
if (virtualMachine.location === location.name) {
if (virtualMachine.properties.sqlImageOffer) {
return virtualMachine.properties.sqlImageOffer.toLowerCase().includes('-ws'); //filtering out all non windows sql vms.
}
return true; // Returning all VMs that don't have this property as we don't want to accidentally skip valid vms.
}
return false;
});
virtualMachineValues = this._targetSqlVirtualMachines.map((virtualMachine) => {
return {
name: virtualMachine.id,
displayName: `${virtualMachine.name}`
@@ -388,7 +479,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
public async getStorageAccountValues(subscription: azureResource.AzureResourceSubscription): Promise<azdata.CategoryValue[]> {
let storageAccountValues: azdata.CategoryValue[] = [];
try {
this._storageAccounts = await getAvailableStorageAccounts(this._azureAccount, subscription);
this._storageAccounts = (await getAvailableStorageAccounts(this._azureAccount, subscription)).filter(sa => sa.location === this._targetServerInstance.location);
this._storageAccounts.forEach((storageAccount) => {
storageAccountValues.push({
name: storageAccount.id,
@@ -564,17 +655,16 @@ export class MigrationStateModel implements Model, vscode.Disposable {
}
};
this._migrationDbs.forEach(async (db, index) => {
requestBody.properties.sourceDatabaseName = db;
for (let i = 0; i < this._migrationDbs.length; i++) {
requestBody.properties.sourceDatabaseName = this._migrationDbs[i];
try {
requestBody.properties.backupConfiguration.sourceLocation.fileShare!.path = this._databaseBackup.networkShareLocations[index];
requestBody.properties.backupConfiguration.sourceLocation.fileShare!.path = this._databaseBackup.networkShareLocation;
const response = await startDatabaseMigration(
this._azureAccount,
this._targetSubscription,
this._sqlMigrationService?.properties.location!,
this._targetServerInstance,
this._targetDatabaseNames[index],
this._targetDatabaseNames[i],
requestBody
);
if (response.status === 201) {
@@ -586,14 +676,13 @@ export class MigrationStateModel implements Model, vscode.Disposable {
this._targetSubscription,
this._sqlMigrationService
);
vscode.window.showInformationMessage(localize("sql.migration.starting.migration.message", 'Starting migration for database {0} to {1} - {2}', db, this._targetServerInstance.name, this._targetDatabaseNames[index]));
vscode.window.showInformationMessage(localize("sql.migration.starting.migration.message", 'Starting migration for database {0} to {1} - {2}', this._migrationDbs[i], this._targetServerInstance.name, this._targetDatabaseNames[i]));
}
} catch (e) {
console.log(e);
vscode.window.showInformationMessage(e);
}
});
}
}
}