Adding migration status and cutover to extension (#14482)

This commit is contained in:
Aasim Khan
2021-03-02 17:11:17 -08:00
committed by GitHub
parent 1e67388653
commit f2ae5419bb
33 changed files with 1452 additions and 236 deletions

View File

@@ -7,7 +7,7 @@ import * as azdata from 'azdata';
import * as vscode from 'vscode';
import { MigrationWizardPage } from '../models/migrationWizardPage';
import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
import * as constants from '../models/strings';
import * as constants from '../constants/strings';
import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
export class AccountsSelectionPage extends MigrationWizardPage {

View File

@@ -8,8 +8,8 @@ import { EOL } from 'os';
import { getStorageAccountAccessKeys } from '../api/azure';
import { MigrationWizardPage } from '../models/migrationWizardPage';
import { MigrationCutover, MigrationStateModel, NetworkContainerType, StateChangeEvent } from '../models/stateMachine';
import * as constants from '../models/strings';
import * as constants from '../constants/strings';
import * as vscode from 'vscode';
export class DatabaseBackupPage extends MigrationWizardPage {
private _networkShareContainer!: azdata.FlexContainer;
@@ -85,7 +85,9 @@ export class DatabaseBackupPage extends MigrationWizardPage {
blobContainerButton.onDidChangeCheckedState((e) => {
if (e) {
this.toggleNetworkContainerFields(NetworkContainerType.BLOB_CONTAINER);
vscode.window.showInformationMessage('Feature coming soon');
networkShareButton.checked = true;
//this.toggleNetworkContainerFields(NetworkContainerType.BLOB_CONTAINER);
}
});
@@ -97,7 +99,9 @@ export class DatabaseBackupPage extends MigrationWizardPage {
fileShareButton.onDidChangeCheckedState((e) => {
if (e) {
this.toggleNetworkContainerFields(NetworkContainerType.FILE_SHARE);
vscode.window.showInformationMessage('Feature coming soon');
networkShareButton.checked = true;
//this.toggleNetworkContainerFields(NetworkContainerType.FILE_SHARE);
}
});
@@ -414,7 +418,9 @@ export class DatabaseBackupPage extends MigrationWizardPage {
offlineButton.onDidChangeCheckedState((e) => {
if (e) {
this.migrationStateModel._databaseBackup.migrationCutover = MigrationCutover.OFFLINE;
vscode.window.showInformationMessage('Feature coming soon');
onlineButton.checked = true;
//this.migrationStateModel._databaseBackup.migrationCutover = MigrationCutover.OFFLINE;
}
});
@@ -489,7 +495,9 @@ export class DatabaseBackupPage extends MigrationWizardPage {
public async onPageLeave(): Promise<void> {
this.migrationStateModel._databaseBackup.storageKey = (await getStorageAccountAccessKeys(this.migrationStateModel._azureAccount, this.migrationStateModel._databaseBackup.subscription, this.migrationStateModel._databaseBackup.storageAccount)).keyName1;
console.log(this.migrationStateModel._databaseBackup);
this.wizard.registerNavigationValidator((pageChangeInfo) => {
return true;
});
}
protected async handleStateChange(e: StateChangeEvent): Promise<void> {

View File

@@ -8,7 +8,7 @@ import * as vscode from 'vscode';
import { MigrationWizardPage } from '../models/migrationWizardPage';
import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
import { CreateMigrationControllerDialog } from '../dialog/createMigrationDialog/createMigrationControllerDialog';
import * as constants from '../models/strings';
import * as constants from '../constants/strings';
import { createInformationRow, WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
import { getMigrationController, getMigrationControllerAuthKeys, getMigrationControllerMonitoringData } from '../api/azure';
import { IconPathHelper } from '../constants/iconPathHelper';
@@ -147,7 +147,7 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
return flexContainer;
}
public async populateMigrationController(controllerStatus?: string): Promise<void> {
public async populateMigrationController(): Promise<void> {
this.migrationControllerDropdown.loading = true;
try {
this.migrationControllerDropdown.values = await this.migrationStateModel.getMigrationControllerValues(this.migrationStateModel._targetSubscription, this.migrationStateModel._targetManagedInstance);

View File

@@ -8,11 +8,10 @@ import * as path from 'path';
import { MigrationWizardPage } from '../models/migrationWizardPage';
import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
import { Product, ProductLookupTable } from '../models/product';
import { Disposable } from 'vscode';
import { AssessmentResultsDialog } from '../dialog/assessmentResults/assessmentResultsDialog';
import { getAvailableManagedInstanceProducts, getSubscriptions, SqlManagedInstance, Subscription } from '../api/azure';
import * as constants from '../models/strings';
import { azureResource } from 'azureResource';
import * as constants from '../constants/strings';
import * as vscode from 'vscode';
import { EOL } from 'os';
// import { SqlMigrationService } from '../../../../extensions/mssql/src/sqlMigration/sqlMigrationService';
@@ -32,11 +31,11 @@ export class SKURecommendationPage extends MigrationWizardPage {
private _azureSubscriptionText: azdata.FormComponent<azdata.TextComponent> | undefined;
private _managedInstanceSubscriptionDropdown!: azdata.DropDownComponent;
private _managedInstanceDropdown!: azdata.DropDownComponent;
private _subscriptionDropdownValues: azdata.CategoryValue[] = [];
private _subscriptionMap: Map<string, Subscription> = new Map();
private _view: azdata.ModelView | undefined;
private _rbg!: azdata.RadioCardGroupComponent;
private async initialState(view: azdata.ModelView) {
this._view = view;
this._igComponent = this.createStatusComponent(view); // The first component giving basic information
this._detailsComponent = this.createDetailsComponent(view); // The details of what can be moved
this._chooseTargetComponent = this.createChooseTargetComponent(view);
@@ -47,12 +46,24 @@ export class SKURecommendationPage extends MigrationWizardPage {
}).component();
this._managedInstanceSubscriptionDropdown = view.modelBuilder.dropDown().component();
this._managedInstanceSubscriptionDropdown.onValueChanged((e) => {
this.populateManagedInstanceDropdown();
if (e.selected) {
this.migrationStateModel._targetSubscription = this.migrationStateModel.getSubscription(e.index);
this.migrationStateModel._targetManagedInstance = undefined!;
this.migrationStateModel._migrationController = undefined!;
this.populateManagedInstanceDropdown();
}
});
const managedInstanceDropdownLabel = view.modelBuilder.text().withProps({
value: constants.MANAGED_INSTANCE
}).component();
this._managedInstanceDropdown = view.modelBuilder.dropDown().component();
this._managedInstanceDropdown.onValueChanged((e) => {
if (e.selected) {
this.migrationStateModel._migrationControllers = undefined!;
this.migrationStateModel._targetManagedInstance = this.migrationStateModel.getManagedInstance(e.index);
}
});
const targetContainer = view.modelBuilder.flexContainer().withItems(
[
@@ -137,18 +148,23 @@ export class SKURecommendationPage extends MigrationWizardPage {
private constructTargets(): void {
const products: Product[] = Object.values(ProductLookupTable);
const rbg = this._view!.modelBuilder.radioCardGroup().withProperties<azdata.RadioCardGroupComponentProperties>({
this._rbg = this._view!.modelBuilder.radioCardGroup().withProperties<azdata.RadioCardGroupComponentProperties>({
cards: [],
cardWidth: '600px',
cardHeight: '60px',
orientation: azdata.Orientation.Vertical,
iconHeight: '30px',
iconWidth: '30px'
});
}).component();
products.forEach((product) => {
const imagePath = path.resolve(this.migrationStateModel.getExtensionPath(), 'media', product.icon ?? 'ads.svg');
let dbCount = 0;
if (product.type === 'AzureSQLVM') {
dbCount = 0;
} else {
dbCount = this.migrationStateModel._migrationDbs.length;
}
const descriptions: azdata.RadioCardDescription[] = [
{
textValue: product.name,
@@ -164,7 +180,7 @@ export class SKURecommendationPage extends MigrationWizardPage {
}
},
{
textValue: '9 databases will be migrated',
textValue: `${dbCount} databases will be migrated`,
linkDisplayValue: 'View/Change',
displayLinkCodicon: true,
linkCodiconStyles: {
@@ -174,22 +190,31 @@ export class SKURecommendationPage extends MigrationWizardPage {
}
];
rbg.component().cards.push({
id: product.name,
this._rbg.cards.push({
id: product.type,
icon: imagePath,
descriptions
});
});
rbg.component().onLinkClick(async (value) => {
this._rbg.onLinkClick(async (value) => {
//check which card is being selected, and open correct dialog based on link
console.log(value);
let dialog = new AssessmentResultsDialog('ownerUri', this.migrationStateModel, 'Assessment Dialog');
let dialog = new AssessmentResultsDialog('ownerUri', this.migrationStateModel, 'Assessment Dialog', this);
await dialog.openDialog();
});
this._chooseTargetComponent?.component.addItem(rbg.component());
this._rbg.onSelectionChanged((value) => {
if (value.cardId === 'AzureSQLVM') {
vscode.window.showInformationMessage('Feature coming soon');
this._rbg.selectedCardId = 'AzureSQLMI';
}
});
this._rbg.selectedCardId = 'AzureSQLMI';
this._chooseTargetComponent?.component.addItem(this._rbg);
}
private createAzureSubscriptionText(view: azdata.ModelView): azdata.FormComponent<azdata.TextComponent> {
@@ -205,85 +230,78 @@ export class SKURecommendationPage extends MigrationWizardPage {
}
private async populateSubscriptionDropdown(): Promise<void> {
this._managedInstanceSubscriptionDropdown.loading = true;
this._managedInstanceDropdown.loading = true;
let subscriptions: azureResource.AzureResourceSubscription[] = [];
try {
subscriptions = await getSubscriptions(this.migrationStateModel._azureAccount);
subscriptions.forEach((subscription) => {
this._subscriptionMap.set(subscription.id, subscription);
this._subscriptionDropdownValues.push({
name: subscription.id,
displayName: subscription.name + ' - ' + subscription.id,
});
});
if (!this._subscriptionDropdownValues || this._subscriptionDropdownValues.length === 0) {
this._subscriptionDropdownValues = [
{
displayName: constants.NO_SUBSCRIPTIONS_FOUND,
name: ''
}
];
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;
}
this._managedInstanceSubscriptionDropdown.values = this._subscriptionDropdownValues;
} catch (error) {
this.setEmptyDropdownPlaceHolder(this._managedInstanceSubscriptionDropdown, constants.NO_SUBSCRIPTIONS_FOUND);
this._managedInstanceDropdown.loading = false;
}
this.populateManagedInstanceDropdown();
this._managedInstanceSubscriptionDropdown.loading = false;
}
private async populateManagedInstanceDropdown(): Promise<void> {
this._managedInstanceDropdown.loading = true;
let mis: SqlManagedInstance[] = [];
let miValues: azdata.CategoryValue[] = [];
try {
const subscriptionId = (<azdata.CategoryValue>this._managedInstanceSubscriptionDropdown.value).name;
mis = await getAvailableManagedInstanceProducts(this.migrationStateModel._azureAccount, this._subscriptionMap.get(subscriptionId)!);
mis.forEach((mi) => {
miValues.push({
name: mi.name,
displayName: mi.name
});
});
if (!miValues || miValues.length === 0) {
miValues = [
{
displayName: constants.NO_MANAGED_INSTANCE_FOUND,
name: ''
}
];
if (!this.migrationStateModel._targetManagedInstance) {
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;
}
this._managedInstanceDropdown.values = miValues;
} catch (error) {
this.setEmptyDropdownPlaceHolder(this._managedInstanceDropdown, constants.NO_MANAGED_INSTANCE_FOUND);
}
this._managedInstanceDropdown.loading = false;
}
private setEmptyDropdownPlaceHolder(dropDown: azdata.DropDownComponent, placeholder: string): void {
dropDown.values = [{
displayName: placeholder,
name: ''
}];
}
private eventListener: Disposable | undefined;
private eventListener: vscode.Disposable | undefined;
public async onPageEnter(): Promise<void> {
this.eventListener = this.migrationStateModel.stateChangeEvent(async (e) => this.onStateChangeEvent(e));
this.populateSubscriptionDropdown();
this.constructDetails();
this.wizard.registerNavigationValidator((pageChangeInfo) => {
const errors: string[] = [];
this.wizard.message = {
text: '',
level: azdata.window.MessageLevel.Error
};
if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
return true;
}
if (this.migrationStateModel._migrationDbs.length === 0) {
errors.push('Please select databases to migrate');
}
if ((<azdata.CategoryValue>this._managedInstanceSubscriptionDropdown.value).displayName === constants.NO_SUBSCRIPTIONS_FOUND) {
errors.push(constants.INVALID_SUBSCRIPTION_ERROR);
}
if ((<azdata.CategoryValue>this._managedInstanceDropdown.value).displayName === constants.NO_MANAGED_INSTANCE_FOUND) {
errors.push(constants.INVALID_STORAGE_ACCOUNT_ERROR);
}
if (errors.length > 0) {
this.wizard.message = {
text: errors.join(EOL),
level: azdata.window.MessageLevel.Error
};
return false;
}
return true;
});
}
public async onPageLeave(): Promise<void> {
this.eventListener?.dispose();
this.wizard.message = {
text: '',
level: azdata.window.MessageLevel.Error
};
this.wizard.registerNavigationValidator((pageChangeInfo) => {
return true;
});
}
protected async handleStateChange(e: StateChangeEvent): Promise<void> {
@@ -292,4 +310,16 @@ export class SKURecommendationPage extends MigrationWizardPage {
}
}
public refreshDatabaseCount(count: number): void {
this.wizard.message = {
text: '',
level: azdata.window.MessageLevel.Error
};
const textValue: string = `${count} databases will be migrated`;
this._rbg.cards[0].descriptions[1].textValue = textValue;
this._rbg.updateProperties({
cards: this._rbg.cards
});
}
}

View File

@@ -5,7 +5,7 @@
import * as azdata from 'azdata';
import { MigrationWizardPage } from '../models/migrationWizardPage';
import { SOURCE_CONFIGURATION_PAGE_TITLE, COLLECTING_SOURCE_CONFIGURATIONS, COLLECTING_SOURCE_CONFIGURATIONS_INFO, COLLECTING_SOURCE_CONFIGURATIONS_ERROR } from '../models/strings';
import { SOURCE_CONFIGURATION_PAGE_TITLE, COLLECTING_SOURCE_CONFIGURATIONS, COLLECTING_SOURCE_CONFIGURATIONS_INFO, COLLECTING_SOURCE_CONFIGURATIONS_ERROR } from '../constants/strings';
import { MigrationStateModel, StateChangeEvent, State } from '../models/stateMachine';
import { Disposable } from 'vscode';

View File

@@ -6,7 +6,7 @@
import * as azdata from 'azdata';
import { MigrationWizardPage } from '../models/migrationWizardPage';
import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
import { SUBSCRIPTION_SELECTION_PAGE_TITLE, SUBSCRIPTION_SELECTION_AZURE_ACCOUNT_TITLE, SUBSCRIPTION_SELECTION_AZURE_PRODUCT_TITLE, SUBSCRIPTION_SELECTION_AZURE_SUBSCRIPTION_TITLE } from '../models/strings';
import { SUBSCRIPTION_SELECTION_PAGE_TITLE, SUBSCRIPTION_SELECTION_AZURE_ACCOUNT_TITLE, SUBSCRIPTION_SELECTION_AZURE_PRODUCT_TITLE, SUBSCRIPTION_SELECTION_AZURE_SUBSCRIPTION_TITLE } from '../constants/strings';
import { Disposable } from 'vscode';
import { getSubscriptions, Subscription, getAvailableManagedInstanceProducts, AzureProduct, getAvailableSqlServers } from '../api/azure';

View File

@@ -6,7 +6,7 @@
import * as azdata from 'azdata';
import { MigrationWizardPage } from '../models/migrationWizardPage';
import { MigrationStateModel, NetworkContainerType, StateChangeEvent } from '../models/stateMachine';
import * as constants from '../models/strings';
import * as constants from '../constants/strings';
import { createHeadingTextComponent, createInformationRow } from './wizardController';
export class SummaryPage extends MigrationWizardPage {
@@ -42,7 +42,7 @@ export class SummaryPage extends MigrationWizardPage {
createInformationRow(this._view, constants.TYPE, constants.SUMMARY_MI_TYPE),
createInformationRow(this._view, constants.SUBSCRIPTION, this.migrationStateModel._targetSubscription.name),
createInformationRow(this._view, constants.SUMMARY_MI_TYPE, this.migrationStateModel._targetManagedInstance.name),
createInformationRow(this._view, constants.SUMMARY_DATABASE_COUNT_LABEL, '1'),
createInformationRow(this._view, constants.SUMMARY_DATABASE_COUNT_LABEL, this.migrationStateModel._migrationDbs.length.toString()),
createHeadingTextComponent(this._view, constants.DATABASE_BACKUP_PAGE_TITLE),
this.createNetworkContainerRows(),
createHeadingTextComponent(this._view, constants.IR_PAGE_TITLE),

View File

@@ -6,7 +6,7 @@
import * as azdata from 'azdata';
import { MigrationWizardPage } from '../models/migrationWizardPage';
import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
import * as constants from '../models/strings';
import * as constants from '../constants/strings';
import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
export class TempTargetSelectionPage extends MigrationWizardPage {

View File

@@ -6,7 +6,7 @@ import * as azdata from 'azdata';
import * as vscode from 'vscode';
import * as mssql from '../../../mssql';
import { MigrationStateModel } from '../models/stateMachine';
import { WIZARD_TITLE } from '../models/strings';
import * as loc from '../constants/strings';
import { MigrationWizardPage } from '../models/migrationWizardPage';
import { SKURecommendationPage } from './skuRecommendationPage';
// import { SubscriptionSelectionPage } from './subscriptionSelectionPage';
@@ -31,7 +31,7 @@ export class WizardController {
}
private async createWizard(stateModel: MigrationStateModel): Promise<void> {
const wizard = azdata.window.createWizard(WIZARD_TITLE, 'wide');
const wizard = azdata.window.createWizard(loc.WIZARD_TITLE, 'wide');
wizard.generateScriptButton.enabled = false;
wizard.generateScriptButton.hidden = true;
const skuRecommendationPage = new SKURecommendationPage(wizard, stateModel);