Migration wizard Refresh 11th Feb 2021 (#14257)

* Adding summary page,
Storing ongoing migrations,
localizing some string,
made changes to how dropdowns work
updated database backup page

* Moved classes into different files
Fixed a lot of typos
Fixed some UI margins
This commit is contained in:
Aasim Khan
2021-02-11 21:18:44 -08:00
committed by GitHub
parent 7739f25f7f
commit b5479d0246
16 changed files with 1121 additions and 583 deletions

View File

@@ -4,10 +4,13 @@
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import { azureResource } from 'azureResource';
import * as vscode from 'vscode';
import * as mssql from '../../../mssql';
import { MigrationController } from '../api/azure';
import { getAvailableManagedInstanceProducts, getAvailableStorageAccounts, getBlobContainers, getFileShares, getMigrationControllers, getSubscriptions, MigrationController, SqlManagedInstance, startDatabaseMigration, StartDatabaseMigrationRequest, StorageAccount } from '../api/azure';
import { SKURecommendations } from './externalContract';
import * as constants from '../models/strings';
import { MigrationLocalStorage } from './migrationLocalStorage';
export enum State {
INIT,
@@ -28,8 +31,8 @@ export enum State {
}
export enum MigrationCutover {
MANUAL,
AUTOMATIC
ONLINE,
OFFLINE
}
export enum NetworkContainerType {
@@ -42,28 +45,20 @@ export interface NetworkShare {
networkShareLocation: string;
windowsUser: string;
password: string;
storageSubscriptionId: string;
storageAccountId: string;
}
export interface BlobContainer {
subscriptionId: string;
storageAccountId: string;
containerId: string;
}
export interface FileShare {
subscriptionId: string;
storageAccountId: string;
fileShareId: string;
resourceGroupId: string;
}
export interface DatabaseBackupModel {
emailNotification: boolean;
migrationCutover: MigrationCutover;
networkContainerType: NetworkContainerType;
networkContainer: NetworkShare | BlobContainer | FileShare;
networkShareLocation: string;
windowsUser: string;
password: string;
subscription: azureResource.AzureResourceSubscription;
storageAccount: StorageAccount;
storageKey: string;
azureSecurityToken: string;
fileShare: azureResource.FileShare;
blobContainer: azureResource.BlobContainer;
}
export interface Model {
readonly sourceConnectionId: string;
@@ -80,14 +75,31 @@ export interface StateChangeEvent {
}
export class MigrationStateModel implements Model, vscode.Disposable {
public azureAccounts!: azdata.Account[];
public azureAccount!: azdata.Account;
public subscriptions!: azureResource.AzureResourceSubscription[];
public _targetSubscription!: azureResource.AzureResourceSubscription;
public _targetManagedInstances!: SqlManagedInstance[];
public _targetManagedInstance!: SqlManagedInstance;
public databaseBackup!: DatabaseBackupModel;
public _storageAccounts!: StorageAccount[];
public _fileShares!: azureResource.FileShare[];
public _blobContainers!: azureResource.BlobContainer[];
public migrationController!: MigrationController;
public migrationControllers!: MigrationController[];
public _nodeNames!: string[];
private _stateChangeEventEmitter = new vscode.EventEmitter<StateChangeEvent>();
private _currentState: State;
private _gatheringInformationError: string | undefined;
private _skuRecommendations: SKURecommendations | undefined;
private _assessmentResults: mssql.SqlMigrationAssessmentResultItem[] | undefined;
private _azureAccount!: azdata.Account;
private _databaseBackup!: DatabaseBackupModel;
private _migrationController!: MigrationController | undefined;
constructor(
private readonly _extensionContext: vscode.ExtensionContext,
@@ -98,22 +110,6 @@ export class MigrationStateModel implements Model, vscode.Disposable {
this.databaseBackup = {} as DatabaseBackupModel;
}
public get azureAccount(): azdata.Account {
return this._azureAccount;
}
public set azureAccount(account: azdata.Account) {
this._azureAccount = account;
}
public get databaseBackup(): DatabaseBackupModel {
return this._databaseBackup;
}
public set databaseBackup(dbBackup: DatabaseBackupModel) {
this._databaseBackup = dbBackup;
}
public get sourceConnectionId(): string {
return this._sourceConnectionId;
}
@@ -158,14 +154,6 @@ export class MigrationStateModel implements Model, vscode.Disposable {
return this._stateChangeEventEmitter.event;
}
public set migrationController(controller: MigrationController | undefined) {
this._migrationController = controller;
}
public get migrationController(): MigrationController | undefined {
return this._migrationController;
}
dispose() {
this._stateChangeEventEmitter.dispose();
}
@@ -173,4 +161,314 @@ export class MigrationStateModel implements Model, vscode.Disposable {
public getExtensionPath(): string {
return this._extensionContext.extensionPath;
}
public async getAccountValues(): Promise<azdata.CategoryValue[]> {
let accountValues: azdata.CategoryValue[] = [];
try {
this.azureAccounts = await azdata.accounts.getAllAccounts();
if (this.azureAccounts.length === 0) {
accountValues = [{
displayName: constants.ACCOUNT_SELECTION_PAGE_NO_LINKED_ACCOUNTS_ERROR,
name: ''
}];
}
accountValues = this.azureAccounts.map((account): azdata.CategoryValue => {
return {
displayName: account.displayInfo.displayName,
name: account.displayInfo.userId
};
});
} catch (e) {
console.log(e);
accountValues = [{
displayName: constants.ACCOUNT_SELECTION_PAGE_NO_LINKED_ACCOUNTS_ERROR,
name: ''
}];
}
return accountValues;
}
public getAccount(index: number): azdata.Account {
return this.azureAccounts[index];
}
public async getSubscriptionsDropdownValues(): Promise<azdata.CategoryValue[]> {
let subscriptionsValues: azdata.CategoryValue[] = [];
try {
if (!this.subscriptions) {
this.subscriptions = await getSubscriptions(this.azureAccount);
}
this.subscriptions.forEach((subscription) => {
subscriptionsValues.push({
name: subscription.id,
displayName: `${subscription.name} - ${subscription.id}`
});
});
if (subscriptionsValues.length === 0) {
subscriptionsValues = [
{
displayName: constants.NO_SUBSCRIPTIONS_FOUND,
name: ''
}
];
}
} catch (e) {
console.log(e);
subscriptionsValues = [
{
displayName: constants.NO_SUBSCRIPTIONS_FOUND,
name: ''
}
];
}
return subscriptionsValues;
}
public getSubscription(index: number): azureResource.AzureResourceSubscription {
return this.subscriptions[index];
}
public async getManagedInstanceValues(subscription: azureResource.AzureResourceSubscription): Promise<azdata.CategoryValue[]> {
let managedInstanceValues: azdata.CategoryValue[] = [];
try {
if (!this._targetManagedInstances) {
this._targetManagedInstances = await getAvailableManagedInstanceProducts(this.azureAccount, subscription);
}
this._targetManagedInstances.forEach((managedInstance) => {
managedInstanceValues.push({
name: managedInstance.id,
displayName: `${managedInstance.name}`
});
});
if (managedInstanceValues.length === 0) {
managedInstanceValues = [
{
displayName: constants.NO_MANAGED_INSTANCE_FOUND,
name: ''
}
];
}
} catch (e) {
console.log(e);
managedInstanceValues = [
{
displayName: constants.NO_MANAGED_INSTANCE_FOUND,
name: ''
}
];
}
return managedInstanceValues;
}
public getManagedInstance(index: number): SqlManagedInstance {
return this._targetManagedInstances[index];
}
public async getStorageAccountValues(subscription: azureResource.AzureResourceSubscription): Promise<azdata.CategoryValue[]> {
let storageAccountValues: azdata.CategoryValue[] = [];
try {
if (!this._storageAccounts) {
this._storageAccounts = await getAvailableStorageAccounts(this.azureAccount, subscription);
}
this._storageAccounts.forEach((storageAccount) => {
storageAccountValues.push({
name: storageAccount.id,
displayName: `${storageAccount.name}`
});
});
if (storageAccountValues.length === 0) {
storageAccountValues = [
{
displayName: constants.NO_STORAGE_ACCOUNT_FOUND,
name: ''
}
];
}
} catch (e) {
console.log(e);
storageAccountValues = [
{
displayName: constants.NO_STORAGE_ACCOUNT_FOUND,
name: ''
}
];
}
return storageAccountValues;
}
public getStorageAccount(index: number): StorageAccount {
return this._storageAccounts[index];
}
public async getFileShareValues(subscription: azureResource.AzureResourceSubscription, storageAccount: StorageAccount): Promise<azdata.CategoryValue[]> {
let fileShareValues: azdata.CategoryValue[] = [];
try {
if (!this._fileShares) {
this._fileShares = await getFileShares(this.azureAccount, subscription, storageAccount);
}
this._fileShares.forEach((fileShare) => {
fileShareValues.push({
name: fileShare.id,
displayName: `${fileShare.name}`
});
});
if (fileShareValues.length === 0) {
fileShareValues = [
{
displayName: constants.NO_FILESHARES_FOUND,
name: ''
}
];
}
} catch (e) {
console.log(e);
fileShareValues = [
{
displayName: constants.NO_FILESHARES_FOUND,
name: ''
}
];
}
return fileShareValues;
}
public getFileShare(index: number): azureResource.FileShare {
return this._fileShares[index];
}
public async getBlobContainerValues(subscription: azureResource.AzureResourceSubscription, storageAccount: StorageAccount): Promise<azdata.CategoryValue[]> {
let blobContainerValues: azdata.CategoryValue[] = [];
try {
if (!this._blobContainers) {
this._blobContainers = await getBlobContainers(this.azureAccount, subscription, storageAccount);
}
this._blobContainers.forEach((blobContainer) => {
blobContainerValues.push({
name: blobContainer.id,
displayName: `${blobContainer.name}`
});
});
if (blobContainerValues.length === 0) {
blobContainerValues = [
{
displayName: constants.NO_BLOBCONTAINERS_FOUND,
name: ''
}
];
}
} catch (e) {
console.log(e);
blobContainerValues = [
{
displayName: constants.NO_BLOBCONTAINERS_FOUND,
name: ''
}
];
}
return blobContainerValues;
}
public getBlobContainer(index: number): azureResource.BlobContainer {
return this._blobContainers[index];
}
public async getMigrationControllerValues(subscription: azureResource.AzureResourceSubscription, managedInstance: SqlManagedInstance): Promise<azdata.CategoryValue[]> {
let migrationControllerValues: azdata.CategoryValue[] = [];
try {
if (!this.migrationControllers) {
this.migrationControllers = await getMigrationControllers(this.azureAccount, subscription, managedInstance.resourceGroup!, managedInstance.location);
}
this.migrationControllers.forEach((migrationController) => {
migrationControllerValues.push({
name: migrationController.id,
displayName: `${migrationController.name}`
});
});
if (migrationControllerValues.length === 0) {
migrationControllerValues = [
{
displayName: constants.MIGRATION_CONTROLLER_NOT_FOUND_ERROR,
name: ''
}
];
}
} catch (e) {
console.log(e);
migrationControllerValues = [
{
displayName: constants.MIGRATION_CONTROLLER_NOT_FOUND_ERROR,
name: ''
}
];
}
return migrationControllerValues;
}
public getMigrationController(index: number): MigrationController {
return this.migrationControllers[index];
}
public async startMigration() {
const sqlConnections = await azdata.connection.getConnections();
const currentConnection = sqlConnections.find((value) => {
if (value.connectionId === this.sourceConnectionId) {
return true;
} else {
return false;
}
});
const connectionPassword = await azdata.connection.getCredentials(this.sourceConnectionId);
const requestBody: StartDatabaseMigrationRequest = {
location: this.migrationController?.properties.location!,
properties: {
SourceDatabaseName: currentConnection?.databaseName!,
MigrationController: this.migrationController?.id!,
BackupConfiguration: {
TargetLocation: {
StorageAccountResourceId: this.databaseBackup.storageAccount.id,
AccountKey: this.databaseBackup.storageKey,
},
SourceLocation: {
FileShare: {
Path: this.databaseBackup.networkShareLocation,
Username: this.databaseBackup.windowsUser,
Password: this.databaseBackup.password,
}
},
},
SourceSqlConnection: {
DataSource: currentConnection?.serverName!,
Username: currentConnection?.userName!,
Password: connectionPassword.password
},
Scope: this._targetManagedInstance.id
}
};
console.log(requestBody);
const response = await startDatabaseMigration(
this.azureAccount,
this._targetSubscription,
this._targetManagedInstance.resourceGroup!,
this.migrationController?.properties.location!,
this._targetManagedInstance.name,
this.migrationController?.name!,
requestBody
);
console.log(response);
if (!response.error) {
MigrationLocalStorage.saveMigration(currentConnection!, response, this._targetManagedInstance, this.azureAccount, this._targetSubscription);
}
vscode.window.showInformationMessage(constants.MIGRATION_STARTED);
}
}