mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-14 01:25:37 -05:00
Migration extension UX update and bug fixes. (#15384)
* Adding a null check to prevent infinite next button loading on account selection page. * Remove a useless validation in migration cutover page * Fixed some component formatting in source selection page * Completely updated target selection page UX according to latest figma mockup * Adding confirmation for migration cutover and cancel * migration vbump * azdata vbump in migration extension * letting users do a cutover with unrestored files * Fixing some localized strings * Adding readme file for migration extension. * Adding a static link for readme gif * added sql mi typing, localized strings, some null checks * casting target instance as sql mi
This commit is contained in:
@@ -91,6 +91,32 @@ declare module 'azureResource' {
|
||||
}
|
||||
|
||||
export interface AzureSqlManagedInstance extends AzureGraphResource {
|
||||
sku: {
|
||||
capacity: number;
|
||||
family: string;
|
||||
name: string;
|
||||
tier: 'GeneralPurpose' | 'BusinessCritical';
|
||||
},
|
||||
properties: {
|
||||
provisioningState: string,
|
||||
storageAccountType: string,
|
||||
maintenanceConfigurationId: string,
|
||||
state: string,
|
||||
licenseType: string,
|
||||
zoneRedundant: false,
|
||||
fullyQualifiedDomainName: string,
|
||||
collation: string,
|
||||
administratorLogin: string,
|
||||
minimalTlsVersion: string,
|
||||
subnetId: string,
|
||||
publicDataEndpointEnabled: boolean,
|
||||
storageSizeInGB: number,
|
||||
timezoneId: string,
|
||||
proxyOverride: string,
|
||||
vCores: number,
|
||||
dnsZone: string,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export interface ManagedDatabase {
|
||||
|
||||
30
extensions/sql-migration/README.md
Normal file
30
extensions/sql-migration/README.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Azure SQL Migration
|
||||
Azure SQL Migration extension can be used to determine readiness of your SQL Server instances, identify a recommended Azure SQL target, and complete the migration of your SQL Server instance to Azure SQL Managed Instance or SQL Server on Azure Virtual Machine.
|
||||
|
||||
## Installation
|
||||
From Azure Data Studio extension gallery, install the latest version of “Azure SQL Migration” extension and launch the wizard as shown below.
|
||||
|
||||

|
||||
|
||||
|
||||
## Things you need before starting Azure SQL migration
|
||||
- Azure account details
|
||||
- Azure SQL Managed Instance or SQL Server on Azure Virtual Machine
|
||||
- Backup location details
|
||||
|
||||
## Getting started
|
||||
Refer to getting started document (https://aka.ms/ads-sql-migration) for detailed documentation on capabilities and current limitations.
|
||||
|
||||
## Need assistance or have questions/feedback
|
||||
Please reach out to DMSFeedback@microsoft.com
|
||||
|
||||
|
||||
## Code of Conduct
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
## Privacy Statement
|
||||
The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/en-us/privacystatement) describes the privacy statement of this software.
|
||||
|
||||
## License
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
Licensed under the [Source EULA](https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt).
|
||||
BIN
extensions/sql-migration/images/ADSMigration.gif
Normal file
BIN
extensions/sql-migration/images/ADSMigration.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
@@ -2,7 +2,7 @@
|
||||
"name": "sql-migration",
|
||||
"displayName": "%displayName%",
|
||||
"description": "%description%",
|
||||
"version": "0.0.11",
|
||||
"version": "0.0.12",
|
||||
"publisher": "Microsoft",
|
||||
"preview": true,
|
||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt",
|
||||
@@ -10,7 +10,7 @@
|
||||
"aiKey": "06ba2446-fa56-40aa-853a-26b73255b723",
|
||||
"engines": {
|
||||
"vscode": "*",
|
||||
"azdata": ">=1.27.0"
|
||||
"azdata": ">=1.29.0"
|
||||
},
|
||||
"activationEvents": [
|
||||
"onDashboardOpen",
|
||||
|
||||
@@ -291,7 +291,7 @@ function sortResourceArrayByName(resourceArray: SortableAzureResources[]): void
|
||||
});
|
||||
}
|
||||
|
||||
function getResourceGroupFromId(id: string): string {
|
||||
export function getResourceGroupFromId(id: string): string {
|
||||
return id.replace(RegExp('^(.*?)/resourceGroups/'), '').replace(RegExp('/providers/.*'), '').toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,12 @@ export const SUBSCRIPTION_SELECTION_PAGE_TITLE = localize('sql.migration.wizard.
|
||||
export const SUBSCRIPTION_SELECTION_AZURE_ACCOUNT_TITLE = localize('sql.migration.wizard.subscription.azure.account.title', "Azure Account");
|
||||
export const SUBSCRIPTION_SELECTION_AZURE_SUBSCRIPTION_TITLE = localize('sql.migration.wizard.subscription.azure.subscription.title', "Azure Subscription");
|
||||
export const SUBSCRIPTION_SELECTION_AZURE_PRODUCT_TITLE = localize('sql.migration.wizard.subscription.azure.product.title', "Azure Product");
|
||||
export const SKU_RECOMMENDATION_VIEW_ASSESSMENT_MI = localize('sql.migration.sku.recommendation.view.assessment.mi', "View assessment results and select one or more database(s) to migrate to Azure SQL Managed Instance (PaaS)");
|
||||
export const SKU_RECOMMENDATION_VIEW_ASSESSMENT_VM = localize('sql.migration.sku.recommendation.view.assessment.vm', "View assessment results and select one or more database(s) to migrate to SQL Server on Azure Virtual Machine (IaaS)");
|
||||
export const VIEW_SELECT_BUTTON_LABEL = localize('sql.migration.view.select.button.label', "View/Select");
|
||||
export function TOTAL_DATABASES_SELECTED(selectedDbCount: number, totalDbCount: number): string {
|
||||
return localize('total.databases.selected', "{0} of {1} Database(s) selected.", selectedDbCount, totalDbCount);
|
||||
}
|
||||
|
||||
export const ASSESSMENT_COMPLETED = (serverName: string): string => {
|
||||
return localize('sql.migration.generic.congratulations', "We have completed the assessment of your SQL Server Instance '{0}'.", serverName);
|
||||
@@ -52,6 +58,9 @@ export const ASSESSMENT_COMPLETED = (serverName: string): string => {
|
||||
export function ASSESSMENT_TILE(serverName: string): string {
|
||||
return localize('sql.migration.assessment', "Assessment Dialog for '{0}'", serverName);
|
||||
}
|
||||
export function CAN_BE_MIGRATED(eligibleDbs: number, totalDbs: number): string {
|
||||
return localize('sql.migration.can.be.migrated', "{0} out of {1} databases can be migrated", eligibleDbs, totalDbs);
|
||||
}
|
||||
|
||||
// Accounts page
|
||||
export const ACCOUNTS_SELECTION_PAGE_TITLE = localize('sql.migration.wizard.account.title', "Azure Account");
|
||||
@@ -270,6 +279,7 @@ export const EASTUS2EUAP = localize('sql.migration.eastus2euap', 'East US 2 EUAP
|
||||
|
||||
//Migration cutover dialog
|
||||
export const MIGRATION_CUTOVER = localize('sql.migration.cutover', "Migration cutover");
|
||||
export const COMPLETE_CUTOVER = localize('sql.migration.complete.cutover', "Complete cutover");
|
||||
export const SOURCE_DATABASE = localize('sql.migration.source.database', "Source database name");
|
||||
export const SOURCE_SERVER = localize('sql.migration.source.server', "Source server");
|
||||
export const SOURCE_VERSION = localize('sql.migration.source.version', "Source version");
|
||||
@@ -299,8 +309,23 @@ export function ACTIVE_BACKUP_FILES_ITEMS(fileCount: number) {
|
||||
}
|
||||
export const COPY_MIGRATION_DETAILS = localize('sql.migration.copy.migration.details', "Copy Migration Details");
|
||||
export const DETAILS_COPIED = localize('sql.migration.details.copied', "Details copied");
|
||||
export const CANCEL_MIGRATION_CONFIRMATION = localize('sql.cancel.migration.confirmation', "Are you sure you want to cancel this migration?");
|
||||
export const YES = localize('sql.migration.yes', "Yes");
|
||||
export const NO = localize('sql.migration.no', "No");
|
||||
|
||||
|
||||
//Migration confirm cutover dialog
|
||||
export const BUSINESS_CRITICAL_INFO = localize('sql.migration.bc.info', "Managed Instance migration cutover for Business Critical service tier can take significantly longer than General Purpose as three secondary replicas have to be seeded for Always On High Availability group. This operation duration depends on the size of data. Seeding speed in 90% of cases is 220 GB/hour or higher.");
|
||||
export const CUTOVER_HELP_MAIN = localize('sql.migration.cutover.help.main', "When you are ready to do the migration cutover, perform the following steps to complete the database migration. Please note that the database is ready for cutover only after a full backup has been restored on the target Azure SQL Database Managed Instance.");
|
||||
export const CUTOVER_HELP_STEP1 = localize('sql.migration.cutover.step.1', "1. Stop all the incoming transactions coming to the source database.");
|
||||
export const CUTOVER_HELP_STEP2 = localize('sql.migration.cutover.step.2', "2. Take the final transaction log backup and provide backup file in the SMB network share.");
|
||||
export const CUTOVER_HELP_STEP3 = localize('sql.migration.cutover.step.3', "3. Make sure all the pending log backups are restored on the target. At that point, “Pending log backups” counter shows zero and then perform the cutover. Performing cutover operation without applying all the transaction log backup files may result in loss of data.");
|
||||
export function PENDING_BACKUPS(count: number): string {
|
||||
return localize('sql.migartion.cutover.pending.backup', "Pending log backups: {0}", count);
|
||||
}
|
||||
export const CONFIRM_CUTOVER_CHECKBOX = localize('sql.migration.confirm.checkbox.message', "Confirm all pending log backups are restored");
|
||||
export function CUTOVER_IN_PROGRESS(dbName: string): string {
|
||||
return localize('sql.migration.cutover.in.progress', "Cutover in progress for database '{0}'", dbName);
|
||||
}
|
||||
//Migration status dialog
|
||||
export const SEARCH_FOR_MIGRATIONS = localize('sql.migration.search.for.migration', "Search for migrations");
|
||||
export const ONLINE = localize('sql.migration.online', "Online");
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { MigrationCutoverDialogModel } from './migrationCutoverDialogModel';
|
||||
import * as constants from '../../constants/strings';
|
||||
import * as vscode from 'vscode';
|
||||
import { SqlManagedInstance } from '../../api/azure';
|
||||
|
||||
export class ConfirmCutoverDialog {
|
||||
private _dialogObject!: azdata.window.Dialog;
|
||||
private _view!: azdata.ModelView;
|
||||
|
||||
constructor(private migrationCutoverModel: MigrationCutoverDialogModel) {
|
||||
this._dialogObject = azdata.window.createModelViewDialog('', 'ConfirmCutoverDialog', 500);
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
let tab = azdata.window.createTab('');
|
||||
tab.registerContent(async (view: azdata.ModelView) => {
|
||||
this._view = view;
|
||||
|
||||
const completeCutoverText = view.modelBuilder.text().withProps({
|
||||
value: constants.COMPLETE_CUTOVER,
|
||||
CSSStyles: {
|
||||
'font-size': '20px',
|
||||
'font-weight': 'bold',
|
||||
'margin-bottom': '0px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
const sourceDatabaseText = view.modelBuilder.text().withProps({
|
||||
value: this.migrationCutoverModel._migration.migrationContext.properties.sourceDatabaseName,
|
||||
CSSStyles: {
|
||||
'font-size': '10px',
|
||||
'margin': '5px 0px 10px 0px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
const separator = this._view.modelBuilder.separator().withProps({ width: '800px' }).component();
|
||||
|
||||
let infoDisplay = 'none';
|
||||
if (this.migrationCutoverModel._migration.targetManagedInstance.id.toLocaleLowerCase().includes('managedinstances')
|
||||
&& (<SqlManagedInstance>this.migrationCutoverModel._migration.targetManagedInstance)?.sku?.tier === 'BusinessCritical') {
|
||||
infoDisplay = 'inline';
|
||||
}
|
||||
|
||||
const businessCriticalinfoBox = this._view.modelBuilder.infoBox().withProps({
|
||||
text: constants.BUSINESS_CRITICAL_INFO,
|
||||
style: 'information',
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'display': infoDisplay
|
||||
}
|
||||
}).component();
|
||||
|
||||
const helpMainText = this._view.modelBuilder.text().withProps({
|
||||
value: constants.CUTOVER_HELP_MAIN,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
}
|
||||
}).component();
|
||||
|
||||
const helpStepsText = this._view.modelBuilder.text().withProps({
|
||||
value: `${constants.CUTOVER_HELP_STEP1}
|
||||
${constants.CUTOVER_HELP_STEP2}
|
||||
${constants.CUTOVER_HELP_STEP3}`,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
}
|
||||
}).component();
|
||||
|
||||
const pendingBackupCount = this.migrationCutoverModel.migrationStatus.properties.migrationStatusDetails?.activeBackupSets.filter(f => f.listOfBackupFiles[0].status !== 'Restored' && f.listOfBackupFiles[0].status !== 'Ignored').length;
|
||||
const pendingText = this._view.modelBuilder.text().withProps({
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold'
|
||||
},
|
||||
value: constants.PENDING_BACKUPS(pendingBackupCount!)
|
||||
}).component();
|
||||
|
||||
const confirmCheckbox = this._view.modelBuilder.checkBox().withProps({
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
},
|
||||
label: constants.CONFIRM_CUTOVER_CHECKBOX,
|
||||
}).component();
|
||||
|
||||
confirmCheckbox.onChanged(e => {
|
||||
this._dialogObject.okButton.enabled = e;
|
||||
});
|
||||
|
||||
|
||||
const container = this._view.modelBuilder.flexContainer().withLayout({
|
||||
flexFlow: 'column'
|
||||
}).withItems([
|
||||
completeCutoverText,
|
||||
sourceDatabaseText,
|
||||
separator,
|
||||
businessCriticalinfoBox,
|
||||
helpMainText,
|
||||
helpStepsText,
|
||||
pendingText,
|
||||
confirmCheckbox
|
||||
]).component();
|
||||
|
||||
|
||||
this._dialogObject.okButton.enabled = false;
|
||||
this._dialogObject.okButton.label = constants.COMPLETE_CUTOVER;
|
||||
this._dialogObject.okButton.onClick((e) => {
|
||||
this.migrationCutoverModel.startCutover();
|
||||
vscode.window.showInformationMessage(constants.CUTOVER_IN_PROGRESS(this.migrationCutoverModel._migration.migrationContext.properties.sourceDatabaseName));
|
||||
});
|
||||
|
||||
const formBuilder = view.modelBuilder.formContainer().withFormItems(
|
||||
[
|
||||
{
|
||||
component: container
|
||||
}
|
||||
],
|
||||
{
|
||||
horizontal: false
|
||||
}
|
||||
);
|
||||
const form = formBuilder.withLayout({ width: '100%' }).component();
|
||||
return view.initializeModel(form);
|
||||
});
|
||||
this._dialogObject.content = [tab];
|
||||
azdata.window.openDialog(this._dialogObject);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import * as loc from '../../constants/strings';
|
||||
import { getSqlServerName } from '../../api/utils';
|
||||
import { EOL } from 'os';
|
||||
import * as vscode from 'vscode';
|
||||
import { ConfirmCutoverDialog } from './confirmCutoverDialog';
|
||||
|
||||
export class MigrationCutoverDialog {
|
||||
private _dialogObject!: azdata.window.Dialog;
|
||||
@@ -41,8 +42,6 @@ export class MigrationCutoverDialog {
|
||||
|
||||
private fileTable!: azdata.TableComponent;
|
||||
|
||||
private _startCutover!: boolean;
|
||||
|
||||
constructor(migration: MigrationContext) {
|
||||
this._model = new MigrationCutoverDialogModel(migration);
|
||||
this._dialogObject = azdata.window.createModelViewDialog('', 'MigrationCutoverDialog', 1000);
|
||||
@@ -333,22 +332,17 @@ export class MigrationCutoverDialog {
|
||||
iconPath: IconPathHelper.cutover,
|
||||
iconHeight: '14px',
|
||||
iconWidth: '12px',
|
||||
label: 'Start Cutover',
|
||||
label: loc.COMPLETE_CUTOVER,
|
||||
height: '20px',
|
||||
width: '100px',
|
||||
width: '130px',
|
||||
enabled: false
|
||||
}).component();
|
||||
|
||||
this._cutoverButton.onDidClick(async (e) => {
|
||||
if (this._startCutover) {
|
||||
await this._model.startCutover();
|
||||
this.refreshStatus();
|
||||
} else {
|
||||
this._dialogObject.message = {
|
||||
text: loc.CANNOT_START_CUTOVER_ERROR,
|
||||
level: azdata.window.MessageLevel.Error
|
||||
};
|
||||
}
|
||||
await this.refreshStatus();
|
||||
const dialog = new ConfirmCutoverDialog(this._model);
|
||||
await dialog.initialize();
|
||||
await this.refreshStatus();
|
||||
});
|
||||
|
||||
headerActions.addItem(this._cutoverButton, {
|
||||
@@ -365,7 +359,12 @@ export class MigrationCutoverDialog {
|
||||
}).component();
|
||||
|
||||
this._cancelButton.onDidClick((e) => {
|
||||
this.cancelMigration();
|
||||
vscode.window.showInformationMessage(loc.CANCEL_MIGRATION_CONFIRMATION, loc.YES, loc.NO).then(async (v) => {
|
||||
if (v === loc.YES) {
|
||||
await this.cancelMigration();
|
||||
await this.refreshStatus();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
headerActions.addItem(this._cancelButton, {
|
||||
@@ -537,14 +536,9 @@ export class MigrationCutoverDialog {
|
||||
row.lastLSN
|
||||
];
|
||||
});
|
||||
if (this._model.migrationStatus.properties.migrationStatusDetails?.isFullBackupRestored) {
|
||||
this._startCutover = true;
|
||||
}
|
||||
|
||||
if (migrationStatusTextValue === MigrationStatus.InProgress) {
|
||||
const fileNotRestored = await tableData.some(file => file.status !== 'Restored' && file.status !== 'Ignored');
|
||||
this._cutoverButton.enabled = !fileNotRestored;
|
||||
this._cancelButton.enabled = true;
|
||||
this._cutoverButton.enabled = tableData.length > 0;
|
||||
} else {
|
||||
this._cutoverButton.enabled = false;
|
||||
this._cancelButton.enabled = false;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as vscode from 'vscode';
|
||||
import { azureResource } from 'azureResource';
|
||||
import { DatabaseMigration, SqlMigrationService, SqlManagedInstance, getMigrationStatus, AzureAsyncOperationResource, getMigrationAsyncOperationDetails } from '../api/azure';
|
||||
import { DatabaseMigration, SqlMigrationService, SqlManagedInstance, getMigrationStatus, AzureAsyncOperationResource, getMigrationAsyncOperationDetails, SqlVMServer } from '../api/azure';
|
||||
import * as azdata from 'azdata';
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ export class MigrationLocalStorage {
|
||||
public static saveMigration(
|
||||
connectionProfile: azdata.connection.ConnectionProfile,
|
||||
migrationContext: DatabaseMigration,
|
||||
targetMI: SqlManagedInstance,
|
||||
targetMI: SqlManagedInstance | SqlVMServer,
|
||||
azureAccount: azdata.Account,
|
||||
subscription: azureResource.AzureResourceSubscription,
|
||||
controller: SqlMigrationService,
|
||||
@@ -93,7 +93,7 @@ export class MigrationLocalStorage {
|
||||
export interface MigrationContext {
|
||||
sourceConnectionProfile: azdata.connection.ConnectionProfile,
|
||||
migrationContext: DatabaseMigration,
|
||||
targetManagedInstance: SqlManagedInstance,
|
||||
targetManagedInstance: SqlManagedInstance | SqlVMServer,
|
||||
azureAccount: azdata.Account,
|
||||
subscription: azureResource.AzureResourceSubscription,
|
||||
controller: SqlMigrationService,
|
||||
|
||||
@@ -108,7 +108,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
public _resourceGroup!: azureResource.AzureResourceResourceGroup;
|
||||
public _targetManagedInstances!: SqlManagedInstance[];
|
||||
public _targetSqlVirtualMachines!: SqlVMServer[];
|
||||
public _targetServerInstance!: SqlManagedInstance;
|
||||
public _targetServerInstance!: SqlManagedInstance | SqlVMServer;
|
||||
public _databaseBackup!: DatabaseBackupModel;
|
||||
public _migrationDbs: string[] = [];
|
||||
public _storageAccounts!: StorageAccount[];
|
||||
@@ -444,7 +444,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
public async getManagedDatabases(): Promise<string[]> {
|
||||
return (await getSqlManagedInstanceDatabases(this._azureAccount,
|
||||
this._targetSubscription,
|
||||
this._targetServerInstance)).map(t => t.name);
|
||||
<SqlManagedInstance>this._targetServerInstance)).map(t => t.name);
|
||||
}
|
||||
|
||||
public async getSqlVirtualMachineValues(subscription: azureResource.AzureResourceSubscription, location: azureResource.AzureLocation, resourceGroup: azureResource.AzureResourceResourceGroup): Promise<azdata.CategoryValue[]> {
|
||||
|
||||
@@ -37,9 +37,9 @@ export class AccountsSelectionPage extends MigrationWizardPage {
|
||||
|
||||
const azureAccountLabel = view.modelBuilder.text().withProps({
|
||||
value: constants.ACCOUNTS_SELECTION_PAGE_TITLE,
|
||||
requiredIndicator: true,
|
||||
CSSStyles: {
|
||||
'margin': '0px'
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold',
|
||||
}
|
||||
}).component();
|
||||
|
||||
@@ -96,7 +96,10 @@ export class AccountsSelectionPage extends MigrationWizardPage {
|
||||
const linkAccountButton = view.modelBuilder.hyperlink()
|
||||
.withProps({
|
||||
label: constants.ACCOUNT_LINK_BUTTON_LABEL,
|
||||
url: ''
|
||||
url: '',
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
}
|
||||
})
|
||||
.component();
|
||||
|
||||
@@ -130,9 +133,9 @@ export class AccountsSelectionPage extends MigrationWizardPage {
|
||||
|
||||
const azureTenantDropdownLabel = view.modelBuilder.text().withProps({
|
||||
value: constants.AZURE_TENANT,
|
||||
requiredIndicator: true,
|
||||
CSSStyles: {
|
||||
'margin': '0px'
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold'
|
||||
}
|
||||
}).component();
|
||||
|
||||
@@ -185,7 +188,7 @@ export class AccountsSelectionPage extends MigrationWizardPage {
|
||||
|
||||
public async onPageEnter(): Promise<void> {
|
||||
this.wizard.registerNavigationValidator(pageChangeInfo => {
|
||||
if (this.migrationStateModel._azureAccount.isStale === true) {
|
||||
if (this.migrationStateModel._azureAccount?.isStale === true) {
|
||||
this.wizard.message = {
|
||||
text: constants.ACCOUNT_STALE_ERROR(this.migrationStateModel._azureAccount)
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@ import { MigrationStateModel, 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, SqlMigrationService } from '../api/azure';
|
||||
import { getLocationDisplayName, getSqlMigrationService, getSqlMigrationServiceAuthKeys, getSqlMigrationServiceMonitoringData, SqlManagedInstance, SqlMigrationService } from '../api/azure';
|
||||
import { IconPathHelper } from '../constants/iconPathHelper';
|
||||
|
||||
export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
@@ -400,7 +400,7 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
}
|
||||
this._dmsDropdown.loading = true;
|
||||
try {
|
||||
this._dmsDropdown.values = await this.migrationStateModel.getSqlMigrationServiceValues(this.migrationStateModel._targetSubscription, this.migrationStateModel._targetServerInstance, resourceGroupName);
|
||||
this._dmsDropdown.values = await this.migrationStateModel.getSqlMigrationServiceValues(this.migrationStateModel._targetSubscription, <SqlManagedInstance>this.migrationStateModel._targetServerInstance, resourceGroupName);
|
||||
let index = -1;
|
||||
if (this.migrationStateModel._sqlMigrationService) {
|
||||
index = (<azdata.CategoryValue[]>this._dmsDropdown.values).findIndex(v => v.displayName.toLowerCase() === this.migrationStateModel._sqlMigrationService.name.toLowerCase());
|
||||
|
||||
@@ -20,7 +20,6 @@ export interface Product {
|
||||
}
|
||||
|
||||
export class SKURecommendationPage extends MigrationWizardPage {
|
||||
|
||||
private _view!: azdata.ModelView;
|
||||
private _igComponent!: azdata.TextComponent;
|
||||
private _assessmentStatusIcon!: azdata.ImageComponent;
|
||||
@@ -42,12 +41,17 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
||||
private _formContainer!: azdata.ComponentBuilder<azdata.FormContainer, azdata.ComponentProperties>;
|
||||
private _assessmentLoader!: azdata.LoadingComponent;
|
||||
private _rootContainer!: azdata.FlexContainer;
|
||||
private _viewAssessmentsHelperText!: azdata.TextComponent;
|
||||
private _databaseSelectedHelperText!: azdata.TextComponent;
|
||||
private assessmentGroupContainer!: azdata.FlexContainer;
|
||||
private _targetContainer!: azdata.FlexContainer;
|
||||
|
||||
private _supportedProducts: Product[] = [
|
||||
{
|
||||
type: MigrationTargetType.SQLMI,
|
||||
name: constants.SKU_RECOMMENDATION_MI_CARD_TEXT,
|
||||
icon: IconPathHelper.sqlMiLogo
|
||||
icon: IconPathHelper.sqlMiLogo,
|
||||
|
||||
},
|
||||
{
|
||||
type: MigrationTargetType.SQLVM,
|
||||
@@ -81,112 +85,27 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
||||
|
||||
this._detailsComponent = this.createDetailsComponent(view); // The details of what can be moved
|
||||
|
||||
const chooseYourTargetText = this._view.modelBuilder.text().withProps({
|
||||
value: constants.SKU_RECOMMENDATION_CHOOSE_A_TARGET,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold',
|
||||
'margin-top': '16px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
const statusContainer = this._view.modelBuilder.flexContainer().withLayout({
|
||||
flexFlow: 'column'
|
||||
}).withItems(
|
||||
[
|
||||
igContainer,
|
||||
this._detailsComponent
|
||||
this._detailsComponent,
|
||||
chooseYourTargetText
|
||||
]
|
||||
).component();
|
||||
this._chooseTargetComponent = await this.createChooseTargetComponent(view);
|
||||
this._azureSubscriptionText = this.createAzureSubscriptionText(view);
|
||||
|
||||
|
||||
const managedInstanceSubscriptionDropdownLabel = view.modelBuilder.text().withProps({
|
||||
value: constants.SUBSCRIPTION,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||
}).component();
|
||||
this._managedInstanceSubscriptionDropdown = view.modelBuilder.dropDown().withProps({
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
editable: true
|
||||
}).component();
|
||||
this._managedInstanceSubscriptionDropdown.onValueChanged((e) => {
|
||||
if (e) {
|
||||
const selectedIndex = (<azdata.CategoryValue[]>this._managedInstanceSubscriptionDropdown.values)?.findIndex(v => v.displayName === e);
|
||||
this.migrationStateModel._targetSubscription = this.migrationStateModel.getSubscription(selectedIndex);
|
||||
this.migrationStateModel._targetServerInstance = undefined!;
|
||||
this.migrationStateModel._sqlMigrationService = undefined!;
|
||||
this.populateLocationAndResourceGroupDropdown();
|
||||
}
|
||||
});
|
||||
this._resourceDropdownLabel = view.modelBuilder.text().withProps({
|
||||
value: constants.MANAGED_INSTANCE,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||
}).component();
|
||||
|
||||
const azureLocationLabel = view.modelBuilder.text().withProps({
|
||||
value: constants.LOCATION,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||
}).component();
|
||||
this._azureLocationDropdown = view.modelBuilder.dropDown().withProps({
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||
}).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,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||
}).component();
|
||||
|
||||
|
||||
const azureResourceGroupLabel = view.modelBuilder.text().withProps({
|
||||
value: constants.RESOURCE_GROUP,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||
}).component();
|
||||
this._azureResourceGroupDropdown = view.modelBuilder.dropDown().withProps({
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||
}).component();
|
||||
this._azureResourceGroupDropdown.onValueChanged((e) => {
|
||||
if (e.selected) {
|
||||
this.migrationStateModel._resourceGroup = this.migrationStateModel.getAzureResourceGroup(e.index);
|
||||
this.populateResourceInstanceDropdown();
|
||||
}
|
||||
});
|
||||
this._resourceDropdownLabel = view.modelBuilder.text().withProps({
|
||||
value: constants.MANAGED_INSTANCE,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||
}).component();
|
||||
|
||||
this._resourceDropdown = view.modelBuilder.dropDown().withProps({
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||
}).component();
|
||||
this._resourceDropdown.onValueChanged((e) => {
|
||||
if (e.selected &&
|
||||
e.selected !== constants.NO_MANAGED_INSTANCE_FOUND &&
|
||||
e.selected !== constants.NO_VIRTUAL_MACHINE_FOUND) {
|
||||
this.migrationStateModel._sqlMigrationServices = undefined!;
|
||||
if (this._rbg.selectedCardId === MigrationTargetType.SQLVM) {
|
||||
this.migrationStateModel._targetServerInstance = this.migrationStateModel.getVirtualMachine(e.index);
|
||||
} else {
|
||||
this.migrationStateModel._targetServerInstance = this.migrationStateModel.getManagedInstance(e.index);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const targetContainer = view.modelBuilder.flexContainer().withItems(
|
||||
[
|
||||
managedInstanceSubscriptionDropdownLabel,
|
||||
this._managedInstanceSubscriptionDropdown,
|
||||
azureLocationLabel,
|
||||
this._azureLocationDropdown,
|
||||
azureResourceGroupLabel,
|
||||
this._azureResourceGroupDropdown,
|
||||
this._resourceDropdownLabel,
|
||||
this._resourceDropdown
|
||||
]
|
||||
).withLayout({
|
||||
flexFlow: 'column'
|
||||
}).component();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
this.assessmentGroupContainer = await this.createViewAssessmentsContainer();
|
||||
this._targetContainer = this.createTargetDropdownContainer();
|
||||
this._formContainer = view.modelBuilder.formContainer().withFormItems(
|
||||
[
|
||||
{
|
||||
@@ -194,14 +113,13 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
||||
component: statusContainer
|
||||
},
|
||||
{
|
||||
title: constants.SKU_RECOMMENDATION_CHOOSE_A_TARGET,
|
||||
component: this._chooseTargetComponent
|
||||
},
|
||||
{
|
||||
component: this._azureSubscriptionText
|
||||
component: this.assessmentGroupContainer
|
||||
},
|
||||
{
|
||||
component: targetContainer
|
||||
component: this._targetContainer
|
||||
}
|
||||
]
|
||||
).withProps({
|
||||
@@ -237,7 +155,8 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
||||
CSSStyles: {
|
||||
'font-size': '14px',
|
||||
'margin': '0 0 0 8px',
|
||||
'line-height': '20px'
|
||||
'line-height': '20px',
|
||||
'font-weight': 'bold'
|
||||
}
|
||||
}).component();
|
||||
return component;
|
||||
@@ -256,87 +175,46 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
||||
|
||||
this._rbg = this._view!.modelBuilder.radioCardGroup().withProps({
|
||||
cards: [],
|
||||
cardWidth: '600px',
|
||||
cardHeight: '40px',
|
||||
orientation: azdata.Orientation.Vertical,
|
||||
iconHeight: '30px',
|
||||
iconWidth: '30px'
|
||||
iconHeight: '35px',
|
||||
iconWidth: '35px',
|
||||
cardWidth: '250px',
|
||||
cardHeight: '130px',
|
||||
iconPosition: 'left',
|
||||
CSSStyles: {
|
||||
'margin-top': '0px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
this._supportedProducts.forEach((product) => {
|
||||
const descriptions: azdata.RadioCardDescription[] = [
|
||||
{
|
||||
textValue: product.name,
|
||||
textStyles: {
|
||||
'font-size': '14px',
|
||||
'font-weight': 'bold',
|
||||
'line-height': '20px'
|
||||
},
|
||||
linkDisplayValue: 'Learn more',
|
||||
linkStyles: {
|
||||
'font-size': '14px',
|
||||
'line-height': '20px'
|
||||
},
|
||||
displayLinkCodicon: true,
|
||||
linkCodiconStyles: {
|
||||
'font-size': '14px',
|
||||
'line-height': '20px'
|
||||
},
|
||||
},
|
||||
{
|
||||
textValue: '0 selected',
|
||||
textStyles: {
|
||||
'font-size': '13px',
|
||||
'line-height': '18px'
|
||||
},
|
||||
linkStyles: {
|
||||
'font-size': '14px',
|
||||
'line-height': '20px'
|
||||
},
|
||||
linkDisplayValue: 'View/Change',
|
||||
displayLinkCodicon: true,
|
||||
linkCodiconStyles: {
|
||||
'font-size': '13px',
|
||||
'line-height': '18px'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
this._rbg.cards.push({
|
||||
id: product.type,
|
||||
icon: product.icon,
|
||||
descriptions
|
||||
descriptions: [
|
||||
{
|
||||
textValue: product.name,
|
||||
textStyles: {
|
||||
'font-size': '14px',
|
||||
'font-weight': 'bold'
|
||||
}
|
||||
},
|
||||
{
|
||||
textValue: '',
|
||||
textStyles: {
|
||||
'font-size': '13px',
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
const serverName = (await this.migrationStateModel.getSourceConnectionProfile()).serverName;
|
||||
let miDialog = new AssessmentResultsDialog('ownerUri', this.migrationStateModel, constants.ASSESSMENT_TILE(serverName), this, MigrationTargetType.SQLMI);
|
||||
let vmDialog = new AssessmentResultsDialog('ownerUri', this.migrationStateModel, constants.ASSESSMENT_TILE(serverName), this, MigrationTargetType.SQLVM);
|
||||
|
||||
this._rbg.onLinkClick(async (value) => {
|
||||
if (value.cardId === MigrationTargetType.SQLVM) {
|
||||
this._rbg.selectedCardId = MigrationTargetType.SQLVM;
|
||||
if (value.description.linkDisplayValue === 'View/Change') {
|
||||
await vmDialog.openDialog();
|
||||
} else if (value.description.linkDisplayValue === 'Learn more') {
|
||||
vscode.env.openExternal(vscode.Uri.parse('https://docs.microsoft.com/azure/azure-sql/virtual-machines/windows/sql-server-on-azure-vm-iaas-what-is-overview'));
|
||||
}
|
||||
} else if (value.cardId === MigrationTargetType.SQLMI) {
|
||||
this._rbg.selectedCardId = MigrationTargetType.SQLMI;
|
||||
if (value.description.linkDisplayValue === 'View/Change') {
|
||||
await miDialog.openDialog();
|
||||
} else if (value.description.linkDisplayValue === 'Learn more') {
|
||||
vscode.env.openExternal(vscode.Uri.parse('https://docs.microsoft.com/azure/azure-sql/managed-instance/sql-managed-instance-paas-overview '));
|
||||
}
|
||||
this._rbg.onSelectionChanged((value) => {
|
||||
if (value) {
|
||||
this.assessmentGroupContainer.display = 'inline';
|
||||
this.changeTargetType(value.cardId);
|
||||
}
|
||||
});
|
||||
|
||||
this._rbg.onSelectionChanged((value) => {
|
||||
this.changeTargetType(value.cardId);
|
||||
});
|
||||
|
||||
this._rbg.selectedCardId = MigrationTargetType.SQLMI;
|
||||
|
||||
this._rbgLoader = this._view.modelBuilder.loadingComponent().withItem(
|
||||
this._rbg
|
||||
).component();
|
||||
@@ -349,17 +227,180 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
||||
return component;
|
||||
}
|
||||
|
||||
private async createViewAssessmentsContainer(): Promise<azdata.FlexContainer> {
|
||||
this._viewAssessmentsHelperText = this._view.modelBuilder.text().withProps({
|
||||
value: constants.SKU_RECOMMENDATION_VIEW_ASSESSMENT_MI,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold',
|
||||
},
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||
}).component();
|
||||
|
||||
const button = this._view.modelBuilder.button().withProps({
|
||||
label: constants.VIEW_SELECT_BUTTON_LABEL,
|
||||
width: 100
|
||||
}).component();
|
||||
|
||||
const serverName = (await this.migrationStateModel.getSourceConnectionProfile()).serverName;
|
||||
let miDialog = new AssessmentResultsDialog('ownerUri', this.migrationStateModel, constants.ASSESSMENT_TILE(serverName), this, MigrationTargetType.SQLMI);
|
||||
let vmDialog = new AssessmentResultsDialog('ownerUri', this.migrationStateModel, constants.ASSESSMENT_TILE(serverName), this, MigrationTargetType.SQLVM);
|
||||
|
||||
button.onDidClick(async (e) => {
|
||||
if (this._rbg.selectedCardId === MigrationTargetType.SQLVM) {
|
||||
this._rbg.selectedCardId = MigrationTargetType.SQLVM;
|
||||
await vmDialog.openDialog();
|
||||
} else if (this._rbg.selectedCardId === MigrationTargetType.SQLMI) {
|
||||
this._rbg.selectedCardId = MigrationTargetType.SQLMI;
|
||||
await miDialog.openDialog();
|
||||
}
|
||||
});
|
||||
|
||||
this._databaseSelectedHelperText = this._view.modelBuilder.text().withProps({
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
}
|
||||
}).component();
|
||||
|
||||
const container = this._view.modelBuilder.flexContainer().withItems([
|
||||
this._viewAssessmentsHelperText,
|
||||
button,
|
||||
this._databaseSelectedHelperText
|
||||
]).withProps({
|
||||
'display': 'none'
|
||||
}).component();
|
||||
return container;
|
||||
}
|
||||
|
||||
private createTargetDropdownContainer(): azdata.FlexContainer {
|
||||
this._azureSubscriptionText = this._view.modelBuilder.text().withProps({
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'line-height': '18px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
|
||||
const managedInstanceSubscriptionDropdownLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.SUBSCRIPTION,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold',
|
||||
}
|
||||
}).component();
|
||||
this._managedInstanceSubscriptionDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
editable: true
|
||||
}).component();
|
||||
this._managedInstanceSubscriptionDropdown.onValueChanged((e) => {
|
||||
if (e) {
|
||||
const selectedIndex = (<azdata.CategoryValue[]>this._managedInstanceSubscriptionDropdown.values)?.findIndex(v => v.displayName === e);
|
||||
this.migrationStateModel._targetSubscription = this.migrationStateModel.getSubscription(selectedIndex);
|
||||
this.migrationStateModel._targetServerInstance = undefined!;
|
||||
this.migrationStateModel._sqlMigrationService = undefined!;
|
||||
this.populateLocationAndResourceGroupDropdown();
|
||||
}
|
||||
});
|
||||
|
||||
const azureLocationLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.LOCATION,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold',
|
||||
}
|
||||
}).component();
|
||||
this._azureLocationDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||
}).component();
|
||||
this._azureLocationDropdown.onValueChanged((e) => {
|
||||
if (e.selected) {
|
||||
this.migrationStateModel._location = this.migrationStateModel.getLocation(e.index);
|
||||
this.populateResourceInstanceDropdown();
|
||||
}
|
||||
});
|
||||
|
||||
const azureResourceGroupLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.RESOURCE_GROUP,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold',
|
||||
}
|
||||
}).component();
|
||||
this._azureResourceGroupDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||
}).component();
|
||||
this._azureResourceGroupDropdown.onValueChanged((e) => {
|
||||
if (e.selected) {
|
||||
this.migrationStateModel._resourceGroup = this.migrationStateModel.getAzureResourceGroup(e.index);
|
||||
this.populateResourceInstanceDropdown();
|
||||
}
|
||||
});
|
||||
this._resourceDropdownLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.MANAGED_INSTANCE,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold',
|
||||
}
|
||||
}).component();
|
||||
|
||||
this._resourceDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||
}).component();
|
||||
this._resourceDropdown.onValueChanged((e) => {
|
||||
if (e?.selected &&
|
||||
e.selected !== constants.NO_MANAGED_INSTANCE_FOUND &&
|
||||
e.selected !== constants.NO_VIRTUAL_MACHINE_FOUND) {
|
||||
this.migrationStateModel._sqlMigrationServices = undefined!;
|
||||
if (this._rbg.selectedCardId === MigrationTargetType.SQLVM) {
|
||||
this.migrationStateModel._targetServerInstance = this.migrationStateModel.getVirtualMachine(e.index);
|
||||
} else {
|
||||
this.migrationStateModel._targetServerInstance = this.migrationStateModel.getManagedInstance(e.index);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return this._view.modelBuilder.flexContainer().withItems(
|
||||
[
|
||||
this._azureSubscriptionText,
|
||||
managedInstanceSubscriptionDropdownLabel,
|
||||
this._managedInstanceSubscriptionDropdown,
|
||||
azureLocationLabel,
|
||||
this._azureLocationDropdown,
|
||||
azureResourceGroupLabel,
|
||||
this._azureResourceGroupDropdown,
|
||||
this._resourceDropdownLabel,
|
||||
this._resourceDropdown
|
||||
]
|
||||
).withLayout({
|
||||
flexFlow: 'column',
|
||||
}).withProps({
|
||||
CSSStyles: {
|
||||
'display': 'none'
|
||||
}
|
||||
}).component();
|
||||
|
||||
}
|
||||
|
||||
private changeTargetType(newTargetType: string) {
|
||||
if (newTargetType === MigrationTargetType.SQLMI) {
|
||||
this._viewAssessmentsHelperText.value = constants.SKU_RECOMMENDATION_VIEW_ASSESSMENT_MI;
|
||||
this._databaseSelectedHelperText.value = constants.TOTAL_DATABASES_SELECTED(this.migrationStateModel._miDbs.length, this.migrationStateModel._serverDatabases.length);
|
||||
this.migrationStateModel._targetType = MigrationTargetType.SQLMI;
|
||||
this._azureSubscriptionText.value = constants.SELECT_AZURE_MI;
|
||||
this.migrationStateModel._migrationDbs = this.migrationStateModel._miDbs;
|
||||
} else {
|
||||
this._viewAssessmentsHelperText.value = constants.SKU_RECOMMENDATION_VIEW_ASSESSMENT_VM;
|
||||
this._databaseSelectedHelperText.value = constants.TOTAL_DATABASES_SELECTED(this.migrationStateModel._vmDbs.length, this.migrationStateModel._serverDatabases.length);
|
||||
this.migrationStateModel._targetType = MigrationTargetType.SQLVM;
|
||||
this._azureSubscriptionText.value = constants.SELECT_AZURE_VM;
|
||||
this.migrationStateModel._migrationDbs = this.migrationStateModel._vmDbs;
|
||||
}
|
||||
this.migrationStateModel.refreshDatabaseBackupPage = true;
|
||||
this._targetContainer.display = (this.migrationStateModel._migrationDbs.length === 0) ? 'none' : 'inline';
|
||||
this.populateResourceInstanceDropdown();
|
||||
}
|
||||
|
||||
@@ -378,17 +419,6 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
||||
this._assessmentLoader.loading = false;
|
||||
}
|
||||
|
||||
private createAzureSubscriptionText(view: azdata.ModelView): azdata.TextComponent {
|
||||
const component = view.modelBuilder.text().withProps({
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'line-height': '18px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
private async populateSubscriptionDropdown(): Promise<void> {
|
||||
if (!this.migrationStateModel._targetSubscription) {
|
||||
this._managedInstanceSubscriptionDropdown.loading = true;
|
||||
@@ -518,34 +548,32 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
||||
text: '',
|
||||
level: azdata.window.MessageLevel.Error
|
||||
};
|
||||
|
||||
if (this._rbg.selectedCardId === MigrationTargetType.SQLMI) {
|
||||
this.migrationStateModel._migrationDbs = this.migrationStateModel._miDbs;
|
||||
} else {
|
||||
this.migrationStateModel._migrationDbs = this.migrationStateModel._vmDbs;
|
||||
}
|
||||
|
||||
this._azureResourceGroupDropdown.display = (!this._rbg.selectedCardId) ? 'none' : 'inline';
|
||||
this._targetContainer.display = (this.migrationStateModel._migrationDbs.length === 0) ? 'none' : 'inline';
|
||||
|
||||
|
||||
if (this.migrationStateModel._assessmentResults) {
|
||||
const dbCount = this.migrationStateModel._assessmentResults.databaseAssessments.length;
|
||||
|
||||
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)`;
|
||||
const miCardText = constants.CAN_BE_MIGRATED(dbWithoutIssuesCount, dbCount);
|
||||
this._rbg.cards[0].descriptions[1].textValue = miCardText;
|
||||
|
||||
const vmCardText = `${dbCount} out of ${dbCount} databases can be migrated (${this.migrationStateModel._vmDbs.length} selected)`;
|
||||
const vmCardText = constants.CAN_BE_MIGRATED(dbCount, dbCount);
|
||||
this._rbg.cards[1].descriptions[1].textValue = vmCardText;
|
||||
|
||||
this._rbg.updateProperties({
|
||||
cards: this._rbg.cards
|
||||
});
|
||||
} else {
|
||||
|
||||
const miCardText = `${this.migrationStateModel._miDbs.length} selected`;
|
||||
this._rbg.cards[0].descriptions[1].textValue = miCardText;
|
||||
|
||||
const vmCardText = `${this.migrationStateModel._vmDbs.length} selected`;
|
||||
this._rbg.cards[1].descriptions[1].textValue = vmCardText;
|
||||
this._rbg.cards[0].descriptions[1].textValue = '';
|
||||
this._rbg.cards[1].descriptions[1].textValue = '';
|
||||
|
||||
this._rbg.updateProperties({
|
||||
cards: this._rbg.cards
|
||||
|
||||
@@ -58,14 +58,18 @@ export class SqlSourceConfigurationPage extends MigrationWizardPage {
|
||||
this._view,
|
||||
constants.ENTER_YOUR_SQL_CREDS,
|
||||
{
|
||||
'width': '600px'
|
||||
'width': '600px',
|
||||
'font-size': '13px',
|
||||
}
|
||||
);
|
||||
|
||||
const serverLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.SERVER,
|
||||
requiredIndicator: true,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold',
|
||||
}
|
||||
}).component();
|
||||
|
||||
const server = this._view.modelBuilder.inputBox().withProps({
|
||||
@@ -76,8 +80,11 @@ export class SqlSourceConfigurationPage extends MigrationWizardPage {
|
||||
|
||||
const authenticationTypeLable = this._view.modelBuilder.text().withProps({
|
||||
value: constants.AUTHENTICATION_TYPE,
|
||||
requiredIndicator: true,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold',
|
||||
}
|
||||
}).component();
|
||||
|
||||
const authenticationTypeInput = this._view.modelBuilder.inputBox().withProps({
|
||||
@@ -88,8 +95,11 @@ export class SqlSourceConfigurationPage extends MigrationWizardPage {
|
||||
|
||||
const usernameLable = this._view.modelBuilder.text().withProps({
|
||||
value: constants.USERNAME,
|
||||
requiredIndicator: true,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold',
|
||||
}
|
||||
}).component();
|
||||
this._usernameInput = this._view.modelBuilder.inputBox().withProps({
|
||||
value: username,
|
||||
@@ -103,8 +113,11 @@ export class SqlSourceConfigurationPage extends MigrationWizardPage {
|
||||
|
||||
const passwordLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.DATABASE_BACKUP_NETWORK_SHARE_PASSWORD_LABEL,
|
||||
requiredIndicator: true,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold',
|
||||
}
|
||||
}).component();
|
||||
this._password = this._view.modelBuilder.inputBox().withProps({
|
||||
value: (await azdata.connection.getCredentials(this.migrationStateModel.sourceConnectionId)).password,
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import { MigrationWizardPage } from '../models/migrationWizardPage';
|
||||
import { MigrationMode, MigrationStateModel, NetworkContainerType, StateChangeEvent } from '../models/stateMachine';
|
||||
import { MigrationMode, MigrationStateModel, MigrationTargetType, NetworkContainerType, StateChangeEvent } from '../models/stateMachine';
|
||||
import * as constants from '../constants/strings';
|
||||
import { createHeadingTextComponent, createInformationRow } from './wizardController';
|
||||
import { getResourceGroupFromId } from '../api/azure';
|
||||
|
||||
export class SummaryPage extends MigrationWizardPage {
|
||||
private _view!: azdata.ModelView;
|
||||
@@ -43,11 +44,11 @@ export class SummaryPage extends MigrationWizardPage {
|
||||
createInformationRow(this._view, constants.SUMMARY_DATABASE_COUNT_LABEL, this.migrationStateModel._migrationDbs.length.toString()),
|
||||
|
||||
createHeadingTextComponent(this._view, constants.SKU_RECOMMENDATION_PAGE_TITLE),
|
||||
createInformationRow(this._view, constants.SKU_RECOMMENDATION_PAGE_TITLE, (this.migrationStateModel._targetServerInstance.type === 'microsoft.compute/virtualmachines') ? constants.SUMMARY_VM_TYPE : constants.SUMMARY_MI_TYPE),
|
||||
createInformationRow(this._view, constants.SKU_RECOMMENDATION_PAGE_TITLE, (this.migrationStateModel._targetType === MigrationTargetType.SQLVM) ? constants.SUMMARY_VM_TYPE : constants.SUMMARY_MI_TYPE),
|
||||
createInformationRow(this._view, constants.SUBSCRIPTION, this.migrationStateModel._targetSubscription.name),
|
||||
createInformationRow(this._view, constants.LOCATION, await this.migrationStateModel.getLocationDisplayName(this.migrationStateModel._targetServerInstance.location)),
|
||||
createInformationRow(this._view, constants.RESOURCE_GROUP, await this.migrationStateModel.getLocationDisplayName(this.migrationStateModel._targetServerInstance.resourceGroup!)),
|
||||
createInformationRow(this._view, (this.migrationStateModel._targetServerInstance.type === 'microsoft.compute/virtualmachines') ? constants.SUMMARY_VM_TYPE : constants.SUMMARY_MI_TYPE, await this.migrationStateModel.getLocationDisplayName(this.migrationStateModel._targetServerInstance.name!)),
|
||||
createInformationRow(this._view, constants.RESOURCE_GROUP, getResourceGroupFromId(this.migrationStateModel._targetServerInstance.id)),
|
||||
createInformationRow(this._view, (this.migrationStateModel._targetType === MigrationTargetType.SQLVM) ? constants.SUMMARY_VM_TYPE : constants.SUMMARY_MI_TYPE, await this.migrationStateModel.getLocationDisplayName(this.migrationStateModel._targetServerInstance.name!)),
|
||||
|
||||
createHeadingTextComponent(this._view, constants.DATABASE_BACKUP_MIGRATION_MODE_LABEL),
|
||||
createInformationRow(this._view, constants.MODE, this.migrationStateModel._databaseBackup.migrationMode === MigrationMode.ONLINE ? constants.DATABASE_BACKUP_MIGRATION_MODE_ONLINE_LABEL : constants.DATABASE_BACKUP_MIGRATION_MODE_OFFLINE_LABEL),
|
||||
|
||||
Reference in New Issue
Block a user