mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Renaming controller to migration service and other bug fixes/ validations. (#14751)
* - Added coming soon message for learn more. - Potential fix for learn more message * Renaming of controller to sqlMigrationService * Surfacing some errors -Azure account is stale error -Migration Service creation error. * Adding refresh azure token validation. * Fixing some errors pointed during PR -Fixing property names -Fixing count * Fixing migration status - Adding special error handling for resource not found error - Deleting unfound migrations from local cache - Using prefetched migration status for view all Misc fixes: - Using SQL server version name instead of number - Fixing Icons on sku recommendation page - Fixing table column width in cutover dialog - Adding spinner button to refresh. * Fixing all strings in migration service page and dialog * fixed a string error in create service dialog
This commit is contained in:
@@ -86,10 +86,10 @@ export async function getBlobContainers(account: azdata.Account, subscription: S
|
|||||||
return blobContainers!;
|
return blobContainers!;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getMigrationController(account: azdata.Account, subscription: Subscription, resourceGroupName: string, regionName: string, controllerName: string): Promise<SqlMigrationController> {
|
export async function getSqlMigrationService(account: azdata.Account, subscription: Subscription, resourceGroupName: string, regionName: string, sqlMigrationServiceName: string): Promise<SqlMigrationService> {
|
||||||
const api = await getAzureCoreAPI();
|
const api = await getAzureCoreAPI();
|
||||||
const host = `https://${regionName}.management.azure.com`;
|
const host = `https://${regionName}.management.azure.com`;
|
||||||
const path = `/subscriptions/${subscription.id}/resourceGroups/${resourceGroupName}/providers/Microsoft.DataMigration/Controllers/${controllerName}?api-version=2020-09-01-preview`;
|
const path = `/subscriptions/${subscription.id}/resourceGroups/${resourceGroupName}/providers/Microsoft.DataMigration/sqlMigrationServices/${sqlMigrationServiceName}?api-version=2020-09-01-preview`;
|
||||||
const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host);
|
const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host);
|
||||||
if (response.errors.length > 0) {
|
if (response.errors.length > 0) {
|
||||||
throw new Error(response.errors.toString());
|
throw new Error(response.errors.toString());
|
||||||
@@ -97,10 +97,10 @@ export async function getMigrationController(account: azdata.Account, subscripti
|
|||||||
return response.response.data;
|
return response.response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getMigrationControllers(account: azdata.Account, subscription: Subscription, resourceGroupName: string, regionName: string): Promise<SqlMigrationController[]> {
|
export async function getSqlMigrationServices(account: azdata.Account, subscription: Subscription, regionName: string): Promise<SqlMigrationService[]> {
|
||||||
const api = await getAzureCoreAPI();
|
const api = await getAzureCoreAPI();
|
||||||
const host = `https://${regionName}.management.azure.com`;
|
const host = `https://${regionName}.management.azure.com`;
|
||||||
const path = `/subscriptions/${subscription.id}/providers/Microsoft.DataMigration/Controllers?api-version=2020-09-01-preview`;
|
const path = `/subscriptions/${subscription.id}/providers/Microsoft.DataMigration/sqlMigrationServices?api-version=2020-09-01-preview`;
|
||||||
const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host);
|
const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host);
|
||||||
if (response.errors.length > 0) {
|
if (response.errors.length > 0) {
|
||||||
throw new Error(response.errors.toString());
|
throw new Error(response.errors.toString());
|
||||||
@@ -109,10 +109,10 @@ export async function getMigrationControllers(account: azdata.Account, subscript
|
|||||||
return response.response.data.value;
|
return response.response.data.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createMigrationController(account: azdata.Account, subscription: Subscription, resourceGroupName: string, regionName: string, controllerName: string): Promise<SqlMigrationController> {
|
export async function createSqlMigrationService(account: azdata.Account, subscription: Subscription, resourceGroupName: string, regionName: string, sqlMigrationServiceName: string): Promise<SqlMigrationService> {
|
||||||
const api = await getAzureCoreAPI();
|
const api = await getAzureCoreAPI();
|
||||||
const host = `https://${regionName}.management.azure.com`;
|
const host = `https://${regionName}.management.azure.com`;
|
||||||
const path = `/subscriptions/${subscription.id}/resourceGroups/${resourceGroupName}/providers/Microsoft.DataMigration/Controllers/${controllerName}?api-version=2020-09-01-preview`;
|
const path = `/subscriptions/${subscription.id}/resourceGroups/${resourceGroupName}/providers/Microsoft.DataMigration/sqlMigrationServices/${sqlMigrationServiceName}?api-version=2020-09-01-preview`;
|
||||||
const requestBody = {
|
const requestBody = {
|
||||||
'location': regionName
|
'location': regionName
|
||||||
};
|
};
|
||||||
@@ -123,10 +123,10 @@ export async function createMigrationController(account: azdata.Account, subscri
|
|||||||
return response.response.data;
|
return response.response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getMigrationControllerAuthKeys(account: azdata.Account, subscription: Subscription, resourceGroupName: string, regionName: string, controllerName: string): Promise<GetMigrationControllerAuthKeysResult> {
|
export async function getSqlMigrationServiceAuthKeys(account: azdata.Account, subscription: Subscription, resourceGroupName: string, regionName: string, sqlMigrationServiceName: string): Promise<SqlMigrationServiceAuthenticationKeys> {
|
||||||
const api = await getAzureCoreAPI();
|
const api = await getAzureCoreAPI();
|
||||||
const host = `https://${regionName}.management.azure.com`;
|
const host = `https://${regionName}.management.azure.com`;
|
||||||
const path = `/subscriptions/${subscription.id}/resourceGroups/${resourceGroupName}/providers/Microsoft.DataMigration/Controllers/${controllerName}/ListAuthKeys?api-version=2020-09-01-preview`;
|
const path = `/subscriptions/${subscription.id}/resourceGroups/${resourceGroupName}/providers/Microsoft.DataMigration/sqlMigrationServices/${sqlMigrationServiceName}/ListAuthKeys?api-version=2020-09-01-preview`;
|
||||||
const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.POST, undefined, true, host);
|
const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.POST, undefined, true, host);
|
||||||
if (response.errors.length > 0) {
|
if (response.errors.length > 0) {
|
||||||
throw new Error(response.errors.toString());
|
throw new Error(response.errors.toString());
|
||||||
@@ -150,10 +150,10 @@ export async function getStorageAccountAccessKeys(account: azdata.Account, subsc
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getMigrationControllerMonitoringData(account: azdata.Account, subscription: Subscription, resourceGroupName: string, regionName: string, controllerName: string): Promise<GetMigrationControllerMonitoringData> {
|
export async function getSqlMigrationServiceMonitoringData(account: azdata.Account, subscription: Subscription, resourceGroupName: string, regionName: string, sqlMigrationService: string): Promise<IntegrationRuntimeMonitoringData> {
|
||||||
const api = await getAzureCoreAPI();
|
const api = await getAzureCoreAPI();
|
||||||
const host = `https://${regionName}.management.azure.com`;
|
const host = `https://${regionName}.management.azure.com`;
|
||||||
const path = `/subscriptions/${subscription.id}/resourceGroups/${resourceGroupName}/providers/Microsoft.DataMigration/Controllers/${controllerName}/monitoringData?api-version=2020-09-01-preview`;
|
const path = `/subscriptions/${subscription.id}/resourceGroups/${resourceGroupName}/providers/Microsoft.DataMigration/sqlMigrationServices/${sqlMigrationService}/monitoringData?api-version=2020-09-01-preview`;
|
||||||
const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host);
|
const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host);
|
||||||
if (response.errors.length > 0) {
|
if (response.errors.length > 0) {
|
||||||
throw new Error(response.errors.toString());
|
throw new Error(response.errors.toString());
|
||||||
@@ -182,6 +182,9 @@ export async function getDatabaseMigration(account: azdata.Account, subscription
|
|||||||
const path = `${migrationId}?api-version=2020-09-01-preview`;
|
const path = `${migrationId}?api-version=2020-09-01-preview`;
|
||||||
const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host);
|
const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host);
|
||||||
if (response.errors.length > 0) {
|
if (response.errors.length > 0) {
|
||||||
|
if (response.response.status === 404 && response.response.data.error.code === 'ResourceDoesNotExist') {
|
||||||
|
throw new Error(response.response.data.error.code);
|
||||||
|
}
|
||||||
throw new Error(response.errors.toString());
|
throw new Error(response.errors.toString());
|
||||||
}
|
}
|
||||||
return response.response.data;
|
return response.response.data;
|
||||||
@@ -198,10 +201,10 @@ export async function getMigrationStatus(account: azdata.Account, subscription:
|
|||||||
return response.response.data;
|
return response.response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function listMigrationsByController(account: azdata.Account, subscription: Subscription, controller: SqlMigrationController): Promise<DatabaseMigration[]> {
|
export async function listMigrationsBySqlMigrationService(account: azdata.Account, subscription: Subscription, sqlMigrationService: SqlMigrationService): Promise<DatabaseMigration[]> {
|
||||||
const api = await getAzureCoreAPI();
|
const api = await getAzureCoreAPI();
|
||||||
const host = `https://eastus2euap.management.azure.com`;
|
const host = `https://eastus2euap.management.azure.com`;
|
||||||
const path = `${controller.id}/listMigrations?$expand=MigrationStatusDetails&api-version=2020-09-01-preview`;
|
const path = `${sqlMigrationService.id}/listMigrations?$expand=MigrationStatusDetails&api-version=2020-09-01-preview`;
|
||||||
const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host);
|
const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host);
|
||||||
if (response.errors.length > 0) {
|
if (response.errors.length > 0) {
|
||||||
throw new Error(response.errors.toString());
|
throw new Error(response.errors.toString());
|
||||||
@@ -233,7 +236,7 @@ export async function stopMigration(account: azdata.Account, subscription: Subsc
|
|||||||
/**
|
/**
|
||||||
* For now only east us euap is supported. Actual API calls will be added in the public release.
|
* For now only east us euap is supported. Actual API calls will be added in the public release.
|
||||||
*/
|
*/
|
||||||
export function getMigrationControllerRegions(): azdata.CategoryValue[] {
|
export function getSqlMigrationServiceRegions(): azdata.CategoryValue[] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
displayName: loc.EASTUS2EUAP,
|
displayName: loc.EASTUS2EUAP,
|
||||||
@@ -242,7 +245,7 @@ export function getMigrationControllerRegions(): azdata.CategoryValue[] {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
type SortableAzureResources = AzureProduct | azureResource.FileShare | azureResource.BlobContainer | azureResource.AzureResourceSubscription | SqlMigrationController;
|
type SortableAzureResources = AzureProduct | azureResource.FileShare | azureResource.BlobContainer | azureResource.AzureResourceSubscription | SqlMigrationService;
|
||||||
function sortResourceArrayByName(resourceArray: SortableAzureResources[]): void {
|
function sortResourceArrayByName(resourceArray: SortableAzureResources[]): void {
|
||||||
if (!resourceArray) {
|
if (!resourceArray) {
|
||||||
return;
|
return;
|
||||||
@@ -258,7 +261,7 @@ function sortResourceArrayByName(resourceArray: SortableAzureResources[]): void
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MigrationControllerProperties {
|
export interface SqlMigrationServiceProperties {
|
||||||
name: string;
|
name: string;
|
||||||
subscriptionId: string;
|
subscriptionId: string;
|
||||||
resourceGroup: string;
|
resourceGroup: string;
|
||||||
@@ -268,8 +271,8 @@ export interface MigrationControllerProperties {
|
|||||||
isProvisioned?: boolean;
|
isProvisioned?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SqlMigrationController {
|
export interface SqlMigrationService {
|
||||||
properties: MigrationControllerProperties;
|
properties: SqlMigrationServiceProperties;
|
||||||
location: string;
|
location: string;
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -279,7 +282,7 @@ export interface SqlMigrationController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GetMigrationControllerAuthKeysResult {
|
export interface SqlMigrationServiceAuthenticationKeys {
|
||||||
authKey1: string,
|
authKey1: string,
|
||||||
authKey2: string
|
authKey2: string
|
||||||
}
|
}
|
||||||
@@ -289,12 +292,12 @@ export interface GetStorageAccountAccessKeysResult {
|
|||||||
keyName2: string
|
keyName2: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GetMigrationControllerMonitoringData {
|
export interface IntegrationRuntimeMonitoringData {
|
||||||
name: string,
|
name: string,
|
||||||
nodes: MigrationControllerNode[];
|
nodes: IntegrationRuntimeNode[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MigrationControllerNode {
|
export interface IntegrationRuntimeNode {
|
||||||
availableMemoryInMB: number,
|
availableMemoryInMB: number,
|
||||||
concurrentJobsLimit: number
|
concurrentJobsLimit: number
|
||||||
concurrentJobsRunning: number,
|
concurrentJobsRunning: number,
|
||||||
@@ -307,27 +310,32 @@ export interface MigrationControllerNode {
|
|||||||
export interface StartDatabaseMigrationRequest {
|
export interface StartDatabaseMigrationRequest {
|
||||||
location: string,
|
location: string,
|
||||||
properties: {
|
properties: {
|
||||||
SourceDatabaseName: string,
|
sourceDatabaseName: string,
|
||||||
MigrationController: string,
|
migrationService: string,
|
||||||
BackupConfiguration: {
|
backupConfiguration: {
|
||||||
TargetLocation: {
|
targetLocation: {
|
||||||
StorageAccountResourceId: string,
|
storageAccountResourceId: string,
|
||||||
AccountKey: string,
|
accountKey: string,
|
||||||
}
|
}
|
||||||
SourceLocation: {
|
sourceLocation: {
|
||||||
FileShare: {
|
fileShare?: {
|
||||||
Path: string,
|
path: string,
|
||||||
Username: string,
|
username: string,
|
||||||
Password: string,
|
password: string,
|
||||||
|
},
|
||||||
|
azureBlob?: {
|
||||||
|
storageAccountResourceId: string,
|
||||||
|
accountKey: string,
|
||||||
|
blobContainerName: string
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
SourceSqlConnection: {
|
sourceSqlConnection: {
|
||||||
DataSource: string,
|
dataSource: string,
|
||||||
Username: string,
|
username: string,
|
||||||
Password: string
|
password: string
|
||||||
},
|
},
|
||||||
Scope: string
|
scope: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -347,10 +355,12 @@ export interface DatabaseMigrationProperties {
|
|||||||
provisioningState: string;
|
provisioningState: string;
|
||||||
migrationStatus: string;
|
migrationStatus: string;
|
||||||
migrationStatusDetails?: MigrationStatusDetails;
|
migrationStatusDetails?: MigrationStatusDetails;
|
||||||
|
startedOn: string;
|
||||||
|
endedOn: string;
|
||||||
sourceSqlConnection: SqlConnectionInfo;
|
sourceSqlConnection: SqlConnectionInfo;
|
||||||
sourceDatabaseName: string;
|
sourceDatabaseName: string;
|
||||||
targetDatabaseCollation: string;
|
targetDatabaseCollation: string;
|
||||||
migrationController: string;
|
migrationService: string;
|
||||||
migrationOperationId: string;
|
migrationOperationId: string;
|
||||||
backupConfiguration: BackupConfiguration;
|
backupConfiguration: BackupConfiguration;
|
||||||
autoCutoverConfiguration: AutoCutoverConfiguration;
|
autoCutoverConfiguration: AutoCutoverConfiguration;
|
||||||
|
|||||||
@@ -21,3 +21,22 @@ export function deepClone<T>(obj: T): T {
|
|||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getSqlServerName(majorVersion: number): string | undefined {
|
||||||
|
switch (majorVersion) {
|
||||||
|
case 10:
|
||||||
|
return 'SQL Server 2008';
|
||||||
|
case 11:
|
||||||
|
return 'SQL Server 2012';
|
||||||
|
case 12:
|
||||||
|
return 'SQL Server 2014';
|
||||||
|
case 13:
|
||||||
|
return 'SQL Server 2016';
|
||||||
|
case 14:
|
||||||
|
return 'SQL Server 2017';
|
||||||
|
case 15:
|
||||||
|
return 'SQL Server 2019';
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ export class IconPathHelper {
|
|||||||
public static inProgressMigration: IconPath;
|
public static inProgressMigration: IconPath;
|
||||||
public static completedMigration: IconPath;
|
public static completedMigration: IconPath;
|
||||||
public static notStartedMigration: IconPath;
|
public static notStartedMigration: IconPath;
|
||||||
|
public static sqlVmLogo: IconPath;
|
||||||
|
public static sqlMiLogo: IconPath;
|
||||||
|
|
||||||
public static setExtensionContext(context: vscode.ExtensionContext) {
|
public static setExtensionContext(context: vscode.ExtensionContext) {
|
||||||
IconPathHelper.copy = {
|
IconPathHelper.copy = {
|
||||||
@@ -68,5 +70,13 @@ export class IconPathHelper {
|
|||||||
light: context.asAbsolutePath('images/cutover.svg'),
|
light: context.asAbsolutePath('images/cutover.svg'),
|
||||||
dark: context.asAbsolutePath('images/cutover.svg')
|
dark: context.asAbsolutePath('images/cutover.svg')
|
||||||
};
|
};
|
||||||
|
IconPathHelper.sqlMiLogo = {
|
||||||
|
light: context.asAbsolutePath('images/sqlMI.svg'),
|
||||||
|
dark: context.asAbsolutePath('images/sqlMI.svg')
|
||||||
|
};
|
||||||
|
IconPathHelper.sqlVmLogo = {
|
||||||
|
light: context.asAbsolutePath('images/sqlVM.svg'),
|
||||||
|
dark: context.asAbsolutePath('images/sqlVM.svg')
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { AzureAccount } from 'azurecore';
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
@@ -28,6 +29,8 @@ export const SKU_RECOMMENDATION_SOME_SUCCESSFUL = (migratableCount: number, data
|
|||||||
export const SKU_RECOMMENDATION_CHOOSE_A_TARGET = localize('sql.migration.wizard.sku.choose_a_target', "Choose a target Azure SQL");
|
export const SKU_RECOMMENDATION_CHOOSE_A_TARGET = localize('sql.migration.wizard.sku.choose_a_target', "Choose a target Azure SQL");
|
||||||
|
|
||||||
export const SKU_RECOMMENDATION_NONE_SUCCESSFUL = localize('sql.migration.sku.none', "Based on the results of our source configuration scans, none of your databases can be migrated to Azure SQL.");
|
export const SKU_RECOMMENDATION_NONE_SUCCESSFUL = localize('sql.migration.sku.none', "Based on the results of our source configuration scans, none of your databases can be migrated to Azure SQL.");
|
||||||
|
export const SKU_RECOMMENDATION_MI_CARD_TEXT = localize('sql.migration.sku.mi.card.title', "Azure Managed Instance (Microsoft managed)");
|
||||||
|
export const SKU_RECOMMENDATION_VM_CARD_TEXT = localize('sql.migration.sku.vm.card.title', "Azure SQL Virtual Machine (Customer managed)'");
|
||||||
|
|
||||||
export const SUBSCRIPTION_SELECTION_PAGE_TITLE = localize('sql.migration.wizard.subscription.title', "Azure Subscription Selection");
|
export const SUBSCRIPTION_SELECTION_PAGE_TITLE = localize('sql.migration.wizard.subscription.title', "Azure Subscription Selection");
|
||||||
export const SUBSCRIPTION_SELECTION_AZURE_ACCOUNT_TITLE = localize('sql.migration.wizard.subscription.azure.account.title', "Azure Account");
|
export const SUBSCRIPTION_SELECTION_AZURE_ACCOUNT_TITLE = localize('sql.migration.wizard.subscription.azure.account.title', "Azure Account");
|
||||||
@@ -47,7 +50,9 @@ export function accountLinkedMessage(count: number): string {
|
|||||||
return count === 1 ? localize('sql.migration.wizard.account.count.single.message', '{0} account linked', count) : localize('sql.migration.wizard.account.count.multiple.message', '{0} accounts linked', count);
|
return count === 1 ? localize('sql.migration.wizard.account.count.single.message', '{0} account linked', count) : localize('sql.migration.wizard.account.count.multiple.message', '{0} accounts linked', count);
|
||||||
}
|
}
|
||||||
export const AZURE_TENANT = localize('sql.migration.azure.tenant', "Azure AD tenant");
|
export const AZURE_TENANT = localize('sql.migration.azure.tenant', "Azure AD tenant");
|
||||||
|
export function ACCOUNT_STALE_ERROR(account: AzureAccount) {
|
||||||
|
return localize('azure.accounts.accountStaleError', "The access token for selected account '{0}' is no longer valid. Please click the 'Link Account' button and refresh the account or select a different account.", `${account.displayInfo.displayName} (${account.displayInfo.userId})`);
|
||||||
|
}
|
||||||
|
|
||||||
// database backup page
|
// database backup page
|
||||||
export const DATABASE_BACKUP_PAGE_TITLE = localize('sql.migration.database.page.title', "Database Backup");
|
export const DATABASE_BACKUP_PAGE_TITLE = localize('sql.migration.database.page.title', "Database Backup");
|
||||||
@@ -106,50 +111,51 @@ export const ENTER_BLOB_CONTAINER_INFORMATION = localize('sql.migration.blob.con
|
|||||||
export const ENTER_FILE_SHARE_INFORMATION = localize('sql.migration.enter.file.share.information', "Enter the target name and select the file share location of selected databases");
|
export const ENTER_FILE_SHARE_INFORMATION = localize('sql.migration.enter.file.share.information', "Enter the target name and select the file share location of selected databases");
|
||||||
|
|
||||||
// integration runtime page
|
// integration runtime page
|
||||||
export const IR_PAGE_TITLE = localize('sql.migration.ir.page.title', "Migration Controller");
|
export const IR_PAGE_TITLE = localize('sql.migration.ir.page.title', "Azure Database Migration Service");
|
||||||
export const IR_PAGE_DESCRIPTION = localize('sql.migration.ir.page.description', "A migration controller is an ARM (Azure Resource Manager) resource created in your Azure subscription and it is needed to coordinate and monitor data migration activities. If one already exists in your subscription, you can reuse it here. Alternatively you can create a new one by clicking New. {0}");
|
export const IR_PAGE_DESCRIPTION = localize('sql.migration.ir.page.description', "Azure Database Migration Service (DMS) orchestrates database migration activities and tracks their progress. You can select an existing DMS for Azure SQL target if you have created one previously or create a new one below. {0}");
|
||||||
export const IR_PAGE_NOTE = localize('sql.migration.ir.page.note', "Note: Migration Controller will run in your Azure subscription in the chosen resource group and does not incur any cost for running it.");
|
export const IR_PAGE_NOTE = localize('sql.migration.ir.page.note', "Note: DMS will run in your Azure subscription in the chosen resource group and does not incur any cost for running it.");
|
||||||
export const SELECT_A_MIGRATION_CONTROLLER = localize('sql.migration.controller', "Select a migration controller");
|
export const SELECT_A_SQL_MIGRATION_SERVICE = localize('sql.migration.select.a.migration.service', "Select Azure Data Migration Service");
|
||||||
export const DEFAULT_SETUP_BUTTON = localize('sql.migration.default.setup.button', "Setup with defaults: Add migration controller with one click express setup using default options.");
|
export const DEFAULT_SETUP_BUTTON = localize('sql.migration.default.setup.button', "Setup with defaults: Add DMS with one click express setup using default options.");
|
||||||
export const CUSTOM_SETUP_BUTTON = localize('sql.migration.custom.setup.button', "Custom setup: Add migration controller after customizing most options.");
|
export const CUSTOM_SETUP_BUTTON = localize('sql.migration.custom.setup.button', "Custom setup: Add DMS after customizing most options.");
|
||||||
export const MIGRATION_CONTROLLER_NOT_FOUND_ERROR = localize('sql.migration.ir.page.migration.controller.not.found', "No Migration Controllers found. Please create a new one");
|
export const SQL_MIGRATION_SERVICE_NOT_FOUND_ERROR = localize('sql.migration.ir.page.sql.migration.service.not.found', "No DMS found. Please create a new one");
|
||||||
export const CREATE_NEW = localize('sql.migration.create.new', "Create new");
|
export const CREATE_NEW = localize('sql.migration.create.new', "Create new");
|
||||||
export const INVALID_CONTROLLER_ERROR = localize('sql.migration.invalid.controller.error', "Please select a valid controller");
|
export const INVALID_SERVICE_ERROR = localize('sql.migration.invalid.migration.service.error', "Please select a valid DMS");
|
||||||
export const CONTROLLER_OFFLINE_ERROR = localize('sql.migration.invalid.controller.offline.error', "Please select a controller that is connected to a node");
|
export const SERVICE_OFFLINE_ERROR = localize('sql.migration.invalid.migration.service.offline.error', "Please select a DMS that is connected to a node");
|
||||||
export const AUTHENTICATION_KEYS = localize('sql.migration.authentication.types', "Authentication Keys");
|
export const AUTHENTICATION_KEYS = localize('sql.migration.authentication.types', "Authentication Keys");
|
||||||
export function CONTROLLER_DETAILS_HEADER(controllerName: string) {
|
export function SQL_MIGRATION_SERVICE_DETAILS_HEADER(sqlMigrationServiceName: string) {
|
||||||
return localize('sql.migration.controller.header', "Migration Controller \"{0}\" details:`", controllerName);
|
return localize('sql.migration.service.header', "Azure Data Migration Service \"{0}\" details:`", sqlMigrationServiceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// create migration controller dialog
|
// create migration service dialog
|
||||||
export const CONTROLLER_DIALOG_DESCRIPTION = localize('sql.migration.controller.container.description', "A migration controller is an ARM (Azure Resource Manager) resource created in your Azure subscription and it is needed to coordinate and monitor data migration activities. {0}");
|
export const CREATE_MIGRATION_SERVICE_TITLE = localize('sql.migration.services.dialog.title', "Create Azure Data Migration Service");
|
||||||
export const CONTROLLER_DIALOG_CONTROLLER_CONTAINER_LOADING_HELP = localize('sql.migration.controller.container.loading.help', "Loading Controller");
|
export const MIGRATION_SERVICE_DIALOG_DESCRIPTION = localize('sql.migration.services.container.description', "Enter the information below to add a new Azure Data Migration Service.");
|
||||||
export const CONTROLLER_DIALOG_CREATE_CONTROLLER_FORM_HEADING = localize('sql.migration.controller.dialog.create.controller.form.heading', "Enter the information below to add a new migration controller.");
|
export const LOADING_MIGRATION_SERVICES = localize('sql.migration.service.container.loading.help', "Loading Migration Services");
|
||||||
export const CONTROLLER_DIALOG_CONTROLLER_CONTAINER_HEADING = localize('sql.migration.controller.container.heading', "Setup Integration Runtime");
|
export const CREATE_SERVICE_FORM_HEADING = localize('sql.migration.service.dialog.create.service.form.heading', "Enter the information below to add a new Migration Service.");
|
||||||
export const CONTROLLER_DIALOG_CONTROLLER_CONTAINER_DESCRIPTION = localize('sql.migration.controller.container.container.description', "Follow the instructions below to setup self-hosted Integration Runtime.");
|
export const SERVICE_CONTAINER_HEADING = localize('sql.migration.service.container.heading', "Setup Integration Runtime");
|
||||||
export const CONTROLLER_STEP1 = localize('sql.migration.ir.setup.step1', "Step 1: {0}");
|
export const SERVICE_CONTAINER_DESCRIPTION = localize('sql.migration.service.container.container.description', "Follow the instructions below to setup self-hosted Integration Runtime.");
|
||||||
export const CONTROLLER_STEP1_LINK = localize('sql.migration.option', "Download and install integration runtime");
|
export const SERVICE_STEP1 = localize('sql.migration.ir.setup.step1', "Step 1: {0}");
|
||||||
export const CONTROLLER_STEP2 = localize('sql.migration.ir.setup.step2', "Step 2: Use this key to register your integration runtime");
|
export const SERVICE_STEP1_LINK = localize('sql.migration.option', "Download and install integration runtime");
|
||||||
export const CONTROLLER_STEP3 = localize('sql.migration.ir.setup.step3', "Step 3: Check connection");
|
export const SERVICE_STEP2 = localize('sql.migration.ir.setup.step2', "Step 2: Use this key to register your integration runtime");
|
||||||
export const CONTROLLER_CONNECTION_STATUS = localize('sql.migration.connection.status', "Connection Status");
|
export const SERVICE_STEP3 = localize('sql.migration.ir.setup.step3', "Step 3: Check connection");
|
||||||
export const CONTROLLER_KEY1_LABEL = localize('sql.migration.key1.label', "Key 1");
|
export const SERVICE_CONNECTION_STATUS = localize('sql.migration.connection.status', "Connection Status");
|
||||||
export const CONTROLLER_KEY2_LABEL = localize('sql.migration.key2.label', "Key 2");
|
export const SERVICE_KEY1_LABEL = localize('sql.migration.key1.label', "Key 1");
|
||||||
export const CONTROLLER_KEY_COPIED_HELP = localize('sql.migration.key.copied', "Key copied");
|
export const SERVICE_KEY2_LABEL = localize('sql.migration.key2.label', "Key 2");
|
||||||
|
export const SERVICE_KEY_COPIED_HELP = localize('sql.migration.key.copied', "Key copied");
|
||||||
export const REFRESH_KEYS = localize('sql.migration.refresh.keys', "Refresh keys");
|
export const REFRESH_KEYS = localize('sql.migration.refresh.keys', "Refresh keys");
|
||||||
export const COPY_KEY = localize('sql.migration.copy.key', "Copy key");
|
export const COPY_KEY = localize('sql.migration.copy.key', "Copy key");
|
||||||
export const AUTH_KEY_COLUMN_HEADER = localize('sql.migration.authkeys.header', "Authentication key");
|
export const AUTH_KEY_COLUMN_HEADER = localize('sql.migration.authkeys.header', "Authentication key");
|
||||||
export function CONTROLLER_NOT_READY(controllerName: string): string {
|
export function SERVICE_NOT_READY(serviceName: string): string {
|
||||||
return localize('sql.migration.controller.not.ready', "Migration Controller {0} is not connected to self-hosted Integration Runtime on any node.", controllerName);
|
return localize('sql.migration.service.not.ready', "Azure Data Migration Service is not registered. Azure Data Migration Service '{0}' needs to be registered with self-hosted Integration Runtime on any node.", serviceName);
|
||||||
}
|
}
|
||||||
export function CONTROLLER_READY(controllerName: string, host: string): string {
|
export function SERVICE_READY(serviceName: string, host: string): string {
|
||||||
return localize('sql.migration.controller.ready', "Migration Controller '{0}' is connected to self-hosted Integration Runtime on the node - {1}", controllerName, host);
|
return localize('sql.migration.service.ready', "Azure Data Migration Service '{0}' is connected to self-hosted Integration Runtime running on the node - {1}", serviceName, host);
|
||||||
}
|
}
|
||||||
export const RESOURCE_GROUP_NOT_FOUND = localize('sql.migration.resource.group.not.found', "No resource Groups found");
|
export const RESOURCE_GROUP_NOT_FOUND = localize('sql.migration.resource.group.not.found', "No resource Groups found");
|
||||||
export const INVALID_RESOURCE_GROUP_ERROR = localize('sql.migration.invalid.resourceGroup.error', "Please select a valid resource group to proceed.");
|
export const INVALID_RESOURCE_GROUP_ERROR = localize('sql.migration.invalid.resourceGroup.error', "Please select a valid resource group to proceed.");
|
||||||
export const INVALID_REGION_ERROR = localize('sql.migration.invalid.region.error', "Please select a valid region to proceed.");
|
export const INVALID_REGION_ERROR = localize('sql.migration.invalid.region.error', "Please select a valid region to proceed.");
|
||||||
export const INVALID_CONTROLLER_NAME_ERROR = localize('sql.migration.invalid.controller.name.error', "Please enter a valid name for the migration controller.");
|
export const INVALID_SERVICE_NAME_ERROR = localize('sql.migration.invalid.service.name.error', "Please enter a valid name for the Migration Service.");
|
||||||
export const CONTROLLER_NOT_FOUND = localize('sql.migration.controller.not.found', "No Migration Controllers found. Please create a new one.");
|
export const SERVICE_NOT_FOUND = localize('sql.migration.service.not.found', "No Migration Services found. Please create a new one.");
|
||||||
export const CONTROLLER_NOT_SETUP_ERROR = localize('sql.migration.controller.not.setup', "Please add a migration controller to proceed.");
|
export const SERVICE_NOT_SETUP_ERROR = localize('sql.migration.service.not.setup', "Please add a Migration Service to proceed.");
|
||||||
export const MANAGED_INSTANCE = localize('sql.migration.managed.instance', "Azure SQL managed instance");
|
export const MANAGED_INSTANCE = localize('sql.migration.managed.instance', "Azure SQL managed instance");
|
||||||
export const NO_MANAGED_INSTANCE_FOUND = localize('sql.migration.no.managedInstance.found', "No managed instance found");
|
export const NO_MANAGED_INSTANCE_FOUND = localize('sql.migration.no.managedInstance.found', "No managed instance found");
|
||||||
export const NO_VIRTUAL_MACHINE_FOUND = localize('sql.migration.no.virtualMachine.found', "No virtual machine found");
|
export const NO_VIRTUAL_MACHINE_FOUND = localize('sql.migration.no.virtualMachine.found', "No virtual machine found");
|
||||||
@@ -215,12 +221,11 @@ export const MIGRATION_COMPLETED = localize('sql.migration.migration.completed',
|
|||||||
export const SUCCESSFULLY_MIGRATED_TO_AZURE_SQL = localize('sql.migration.successfully.migrated.to.azure.sql', "Successfully migrated to Azure SQL");
|
export const SUCCESSFULLY_MIGRATED_TO_AZURE_SQL = localize('sql.migration.successfully.migrated.to.azure.sql', "Successfully migrated to Azure SQL");
|
||||||
export const MIGRATION_NOT_STARTED = localize('sql.migration.migration.not.started', "Migration not started");
|
export const MIGRATION_NOT_STARTED = localize('sql.migration.migration.not.started', "Migration not started");
|
||||||
export const CHOOSE_TO_MIGRATE_TO_AZURE_SQL = localize('sql.migration.choose.to.migrate.to.azure.sql', "Choose to migrate to Azure SQL");
|
export const CHOOSE_TO_MIGRATE_TO_AZURE_SQL = localize('sql.migration.choose.to.migrate.to.azure.sql', "Choose to migrate to Azure SQL");
|
||||||
|
export const COMING_SOON = localize('sql.migration.coming.soon', "Coming soon");
|
||||||
|
|
||||||
// Azure APIs
|
// Azure APIs
|
||||||
export const EASTUS2EUAP = localize('sql.migration.eastus2euap', 'East US 2 EUAP');
|
export const EASTUS2EUAP = localize('sql.migration.eastus2euap', 'East US 2 EUAP');
|
||||||
|
|
||||||
|
|
||||||
//Migration cutover dialog
|
//Migration cutover dialog
|
||||||
export const MIGRATION_CUTOVER = localize('sql.migration.cutover', "Migration cutover");
|
export const MIGRATION_CUTOVER = localize('sql.migration.cutover', "Migration cutover");
|
||||||
export const SOURCE_SERVER = localize('sql.migration.source.server', "Source server");
|
export const SOURCE_SERVER = localize('sql.migration.source.server', "Source server");
|
||||||
@@ -253,6 +258,7 @@ export function ACTIVE_BACKUP_FILES_ITEMS(fileCount: number) {
|
|||||||
//Migration status dialog
|
//Migration status dialog
|
||||||
export const SEARCH_FOR_MIGRATIONS = localize('sql.migration.search.for.migration', "Search for migrations");
|
export const SEARCH_FOR_MIGRATIONS = localize('sql.migration.search.for.migration', "Search for migrations");
|
||||||
export const ONLINE = localize('sql.migration.online', "Online");
|
export const ONLINE = localize('sql.migration.online', "Online");
|
||||||
|
export const OFFLINE = localize('sql.migration.offline', "Offline");
|
||||||
export const DATABASE = localize('sql.migration.database', "Database");
|
export const DATABASE = localize('sql.migration.database', "Database");
|
||||||
export const TARGET_AZURE_SQL_INSTANCE_NAME = localize('sql.migration.target.azure.sql.instance.name', "Target Azure SQL Instance Name");
|
export const TARGET_AZURE_SQL_INSTANCE_NAME = localize('sql.migration.target.azure.sql.instance.name', "Target Azure SQL Instance Name");
|
||||||
export const CUTOVER_TYPE = localize('sql.migration.cutover.type', "Cutover type");
|
export const CUTOVER_TYPE = localize('sql.migration.cutover.type', "Cutover type");
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import * as vscode from 'vscode';
|
|||||||
import { MigrationContext, MigrationLocalStorage } from '../models/migrationLocalStorage';
|
import { MigrationContext, MigrationLocalStorage } from '../models/migrationLocalStorage';
|
||||||
import * as loc from '../constants/strings';
|
import * as loc from '../constants/strings';
|
||||||
import { IconPath, IconPathHelper } from '../constants/iconPathHelper';
|
import { IconPath, IconPathHelper } from '../constants/iconPathHelper';
|
||||||
import { getDatabaseMigration } from '../api/azure';
|
|
||||||
import { MigrationStatusDialog } from '../dialog/migrationStatus/migrationStatusDialog';
|
import { MigrationStatusDialog } from '../dialog/migrationStatus/migrationStatusDialog';
|
||||||
import { MigrationCategory } from '../dialog/migrationStatus/migrationStatusDialogModel';
|
import { MigrationCategory } from '../dialog/migrationStatus/migrationStatusDialogModel';
|
||||||
|
|
||||||
@@ -22,12 +21,25 @@ interface IActionMetadata {
|
|||||||
|
|
||||||
const maxWidth = 800;
|
const maxWidth = 800;
|
||||||
|
|
||||||
|
interface StatusCard {
|
||||||
|
container: azdata.DivContainer;
|
||||||
|
count: azdata.TextComponent
|
||||||
|
}
|
||||||
|
|
||||||
export class DashboardWidget {
|
export class DashboardWidget {
|
||||||
|
|
||||||
private _migrationStatusCardsContainer!: azdata.FlexContainer;
|
private _migrationStatusCardsContainer!: azdata.FlexContainer;
|
||||||
private _migrationStatusCardLoadingContainer!: azdata.LoadingComponent;
|
private _migrationStatusCardLoadingContainer!: azdata.LoadingComponent;
|
||||||
private _view!: azdata.ModelView;
|
private _view!: azdata.ModelView;
|
||||||
|
|
||||||
|
|
||||||
|
private _inProgressMigrationButton!: StatusCard;
|
||||||
|
private _successfulMigrationButton!: StatusCard;
|
||||||
|
private _notStartedMigrationCard!: StatusCard;
|
||||||
|
private _migrationStatus!: MigrationContext[];
|
||||||
|
|
||||||
|
private _viewAllMigrationsButton!: azdata.ButtonComponent;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,6 +155,10 @@ export class DashboardWidget {
|
|||||||
}
|
}
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
|
preRequisiteLearnMoreLink.onDidClick((value) => {
|
||||||
|
vscode.window.showInformationMessage(loc.COMING_SOON);
|
||||||
|
});
|
||||||
|
|
||||||
const preReqContainer = view.modelBuilder.flexContainer().withItems([
|
const preReqContainer = view.modelBuilder.flexContainer().withItems([
|
||||||
preRequisiteListTitle,
|
preRequisiteListTitle,
|
||||||
preRequisiteListElement
|
preRequisiteListElement
|
||||||
@@ -197,101 +213,51 @@ export class DashboardWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async refreshMigrations(): Promise<void> {
|
private async refreshMigrations(): Promise<void> {
|
||||||
|
this._viewAllMigrationsButton.enabled = false;
|
||||||
this._migrationStatusCardLoadingContainer.loading = true;
|
this._migrationStatusCardLoadingContainer.loading = true;
|
||||||
this._migrationStatusCardsContainer.clearItems();
|
|
||||||
try {
|
try {
|
||||||
const migrationStatus = await this.getMigrations();
|
this._migrationStatus = await this.getMigrations();
|
||||||
|
|
||||||
const inProgressMigrations = migrationStatus.filter((value) => {
|
const inProgressMigrations = this._migrationStatus.filter((value) => {
|
||||||
const status = value.migrationContext.properties.migrationStatus;
|
const status = value.migrationContext.properties.migrationStatus;
|
||||||
return status === 'InProgress' || status === 'Creating' || status === 'Completing';
|
return status === 'InProgress' || status === 'Creating' || status === 'Completing';
|
||||||
});
|
});
|
||||||
const inProgressMigrationButton = this.createStatusCard(
|
|
||||||
IconPathHelper.inProgressMigration,
|
|
||||||
loc.MIGRATION_IN_PROGRESS,
|
|
||||||
loc.LOG_SHIPPING_IN_PROGRESS,
|
|
||||||
inProgressMigrations.length
|
|
||||||
);
|
|
||||||
inProgressMigrationButton.onDidClick((e) => {
|
|
||||||
const dialog = new MigrationStatusDialog(migrationStatus, MigrationCategory.ONGOING);
|
|
||||||
dialog.initialize();
|
|
||||||
});
|
|
||||||
this._migrationStatusCardsContainer.addItem(inProgressMigrationButton);
|
|
||||||
|
|
||||||
const successfulMigration = migrationStatus.filter((value) => {
|
this._inProgressMigrationButton.count.value = inProgressMigrations.length.toString();
|
||||||
|
|
||||||
|
const successfulMigration = this._migrationStatus.filter((value) => {
|
||||||
const status = value.migrationContext.properties.migrationStatus;
|
const status = value.migrationContext.properties.migrationStatus;
|
||||||
return status === 'Succeeded';
|
return status === 'Succeeded';
|
||||||
});
|
});
|
||||||
const successfulMigrationButton = this.createStatusCard(
|
|
||||||
IconPathHelper.completedMigration,
|
this._successfulMigrationButton.count.value = successfulMigration.length.toString();
|
||||||
loc.MIGRATION_COMPLETED,
|
|
||||||
loc.SUCCESSFULLY_MIGRATED_TO_AZURE_SQL,
|
|
||||||
successfulMigration.length
|
|
||||||
);
|
|
||||||
successfulMigrationButton.onDidClick((e) => {
|
|
||||||
const dialog = new MigrationStatusDialog(migrationStatus, MigrationCategory.SUCCEEDED);
|
|
||||||
dialog.initialize();
|
|
||||||
});
|
|
||||||
this._migrationStatusCardsContainer.addItem(
|
|
||||||
successfulMigrationButton
|
|
||||||
);
|
|
||||||
|
|
||||||
const currentConnection = (await azdata.connection.getCurrentConnection());
|
const currentConnection = (await azdata.connection.getCurrentConnection());
|
||||||
const localMigrations = MigrationLocalStorage.getMigrationsBySourceConnections(currentConnection);
|
|
||||||
const migrationDatabases = new Set(
|
const migrationDatabases = new Set(
|
||||||
localMigrations.filter((value) => {
|
this._migrationStatus.map((value) => {
|
||||||
|
|
||||||
}).map((value) => {
|
|
||||||
return value.migrationContext.properties.sourceDatabaseName;
|
return value.migrationContext.properties.sourceDatabaseName;
|
||||||
}));
|
}));
|
||||||
const serverDatabases = await azdata.connection.listDatabases(currentConnection.connectionId);
|
const serverDatabases = await azdata.connection.listDatabases(currentConnection.connectionId);
|
||||||
const notStartedMigrationCard = this.createStatusCard(
|
this._notStartedMigrationCard.count.value = (serverDatabases.length - migrationDatabases.size).toString();
|
||||||
IconPathHelper.notStartedMigration,
|
|
||||||
loc.MIGRATION_NOT_STARTED,
|
|
||||||
loc.CHOOSE_TO_MIGRATE_TO_AZURE_SQL,
|
|
||||||
serverDatabases.length - migrationDatabases.size
|
|
||||||
);
|
|
||||||
notStartedMigrationCard.onDidClick((e) => {
|
|
||||||
vscode.window.showInformationMessage('Feature coming soon');
|
|
||||||
});
|
|
||||||
this._migrationStatusCardsContainer.addItem(
|
|
||||||
notStartedMigrationCard
|
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
} finally {
|
} finally {
|
||||||
this._migrationStatusCardLoadingContainer.loading = false;
|
this._migrationStatusCardLoadingContainer.loading = false;
|
||||||
|
this._viewAllMigrationsButton.enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getMigrations(): Promise<MigrationContext[]> {
|
private async getMigrations(): Promise<MigrationContext[]> {
|
||||||
const currentConnection = (await azdata.connection.getCurrentConnection());
|
const currentConnection = (await azdata.connection.getCurrentConnection());
|
||||||
const localMigrations = MigrationLocalStorage.getMigrationsBySourceConnections(currentConnection);
|
return await MigrationLocalStorage.getMigrationsBySourceConnections(currentConnection, true);
|
||||||
for (let i = 0; i < localMigrations.length; i++) {
|
|
||||||
const localMigration = localMigrations[i];
|
|
||||||
try {
|
|
||||||
localMigration.migrationContext = await getDatabaseMigration(
|
|
||||||
localMigration.azureAccount,
|
|
||||||
localMigration.subscription,
|
|
||||||
localMigration.targetManagedInstance.location,
|
|
||||||
localMigration.migrationContext.id
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
localMigration.sourceConnectionProfile = currentConnection;
|
|
||||||
}
|
|
||||||
return localMigrations;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private createStatusCard(
|
private createStatusCard(
|
||||||
cardIconPath: IconPath,
|
cardIconPath: IconPath,
|
||||||
cardTitle: string,
|
cardTitle: string,
|
||||||
cardDescription: string,
|
cardDescription: string
|
||||||
count: number
|
): StatusCard {
|
||||||
): azdata.DivContainer {
|
|
||||||
|
|
||||||
const cardTitleText = this._view.modelBuilder.text().withProps({ value: cardTitle }).withProps({
|
const cardTitleText = this._view.modelBuilder.text().withProps({ value: cardTitle }).withProps({
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
@@ -312,7 +278,7 @@ export class DashboardWidget {
|
|||||||
}
|
}
|
||||||
}).component();
|
}).component();
|
||||||
const cardCount = this._view.modelBuilder.text().withProps({
|
const cardCount = this._view.modelBuilder.text().withProps({
|
||||||
value: count.toString(),
|
value: '0',
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
'font-size': '28px',
|
'font-size': '28px',
|
||||||
'line-height': '36px',
|
'line-height': '36px',
|
||||||
@@ -367,7 +333,10 @@ export class DashboardWidget {
|
|||||||
ariaLabel: 'show status',
|
ariaLabel: 'show status',
|
||||||
clickable: true
|
clickable: true
|
||||||
}).component();
|
}).component();
|
||||||
return compositeButton;
|
return {
|
||||||
|
container: compositeButton,
|
||||||
|
count: cardCount
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createFooter(view: azdata.ModelView): Promise<azdata.Component> {
|
private async createFooter(view: azdata.ModelView): Promise<azdata.Component> {
|
||||||
@@ -412,7 +381,7 @@ export class DashboardWidget {
|
|||||||
}
|
}
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const viewAllButton = view.modelBuilder.hyperlink().withProps({
|
this._viewAllMigrationsButton = view.modelBuilder.hyperlink().withProps({
|
||||||
label: loc.VIEW_ALL,
|
label: loc.VIEW_ALL,
|
||||||
url: '',
|
url: '',
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
@@ -420,8 +389,8 @@ export class DashboardWidget {
|
|||||||
}
|
}
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
viewAllButton.onDidClick(async (e) => {
|
this._viewAllMigrationsButton.onDidClick(async (e) => {
|
||||||
new MigrationStatusDialog(await this.getMigrations(), MigrationCategory.ALL).initialize();
|
new MigrationStatusDialog(this._migrationStatus ? this._migrationStatus : await this.getMigrations(), MigrationCategory.ALL).initialize();
|
||||||
});
|
});
|
||||||
|
|
||||||
const refreshButton = view.modelBuilder.hyperlink().withProps({
|
const refreshButton = view.modelBuilder.hyperlink().withProps({
|
||||||
@@ -433,15 +402,17 @@ export class DashboardWidget {
|
|||||||
}
|
}
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
refreshButton.onDidClick((e) => {
|
refreshButton.onDidClick(async (e) => {
|
||||||
this.refreshMigrations();
|
refreshButton.enabled = false;
|
||||||
|
await this.refreshMigrations();
|
||||||
|
refreshButton.enabled = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
const buttonContainer = view.modelBuilder.flexContainer().withLayout({
|
const buttonContainer = view.modelBuilder.flexContainer().withLayout({
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
buttonContainer.addItem(viewAllButton, {
|
buttonContainer.addItem(this._viewAllMigrationsButton, {
|
||||||
flex: 'auto',
|
flex: 'auto',
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
'border-right': '1px solid ',
|
'border-right': '1px solid ',
|
||||||
@@ -466,9 +437,46 @@ export class DashboardWidget {
|
|||||||
flexFlow: 'row'
|
flexFlow: 'row'
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this._migrationStatusCardsContainer = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component();
|
this._migrationStatusCardsContainer = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component();
|
||||||
|
|
||||||
|
this._inProgressMigrationButton = this.createStatusCard(
|
||||||
|
IconPathHelper.inProgressMigration,
|
||||||
|
loc.MIGRATION_IN_PROGRESS,
|
||||||
|
loc.LOG_SHIPPING_IN_PROGRESS
|
||||||
|
);
|
||||||
|
this._inProgressMigrationButton.container.onDidClick((e) => {
|
||||||
|
const dialog = new MigrationStatusDialog(this._migrationStatus, MigrationCategory.ONGOING);
|
||||||
|
dialog.initialize();
|
||||||
|
});
|
||||||
|
this._migrationStatusCardsContainer.addItem(
|
||||||
|
this._inProgressMigrationButton.container
|
||||||
|
);
|
||||||
|
|
||||||
|
this._successfulMigrationButton = this.createStatusCard(
|
||||||
|
IconPathHelper.completedMigration,
|
||||||
|
loc.MIGRATION_COMPLETED,
|
||||||
|
loc.SUCCESSFULLY_MIGRATED_TO_AZURE_SQL
|
||||||
|
);
|
||||||
|
this._successfulMigrationButton.container.onDidClick((e) => {
|
||||||
|
const dialog = new MigrationStatusDialog(this._migrationStatus, MigrationCategory.SUCCEEDED);
|
||||||
|
dialog.initialize();
|
||||||
|
});
|
||||||
|
this._migrationStatusCardsContainer.addItem(
|
||||||
|
this._successfulMigrationButton.container
|
||||||
|
);
|
||||||
|
|
||||||
|
this._notStartedMigrationCard = this.createStatusCard(
|
||||||
|
IconPathHelper.notStartedMigration,
|
||||||
|
loc.MIGRATION_NOT_STARTED,
|
||||||
|
loc.CHOOSE_TO_MIGRATE_TO_AZURE_SQL
|
||||||
|
);
|
||||||
|
this._notStartedMigrationCard.container.onDidClick((e) => {
|
||||||
|
vscode.window.showInformationMessage('Feature coming soon');
|
||||||
|
});
|
||||||
|
this._migrationStatusCardsContainer.addItem(
|
||||||
|
this._notStartedMigrationCard.container
|
||||||
|
);
|
||||||
|
|
||||||
this._migrationStatusCardLoadingContainer = view.modelBuilder.loadingComponent().withItem(this._migrationStatusCardsContainer).component();
|
this._migrationStatusCardLoadingContainer = view.modelBuilder.loadingComponent().withItem(this._migrationStatusCardsContainer).component();
|
||||||
|
|
||||||
statusContainer.addItem(
|
statusContainer.addItem(
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { createMigrationController, getMigrationControllerRegions, getMigrationController, getResourceGroups, getMigrationControllerAuthKeys, getMigrationControllerMonitoringData } from '../../api/azure';
|
import { createSqlMigrationService, getSqlMigrationServiceRegions, getSqlMigrationService, getResourceGroups, getSqlMigrationServiceAuthKeys, getSqlMigrationServiceMonitoringData, SqlMigrationService } from '../../api/azure';
|
||||||
import { MigrationStateModel } from '../../models/stateMachine';
|
import { MigrationStateModel } from '../../models/stateMachine';
|
||||||
import * as constants from '../../constants/strings';
|
import * as constants from '../../constants/strings';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
@@ -13,16 +13,16 @@ import { azureResource } from 'azureResource';
|
|||||||
import { IntergrationRuntimePage } from '../../wizard/integrationRuntimePage';
|
import { IntergrationRuntimePage } from '../../wizard/integrationRuntimePage';
|
||||||
import { IconPathHelper } from '../../constants/iconPathHelper';
|
import { IconPathHelper } from '../../constants/iconPathHelper';
|
||||||
|
|
||||||
export class CreateMigrationControllerDialog {
|
export class CreateSqlMigrationServiceDialog {
|
||||||
|
|
||||||
private migrationControllerSubscriptionDropdown!: azdata.DropDownComponent;
|
private migrationServiceSubscriptionDropdown!: azdata.DropDownComponent;
|
||||||
private migrationControllerResourceGroupDropdown!: azdata.DropDownComponent;
|
private migrationServiceResourceGroupDropdown!: azdata.DropDownComponent;
|
||||||
private migrationControllerRegionDropdown!: azdata.DropDownComponent;
|
private migrationServiceRegionDropdown!: azdata.DropDownComponent;
|
||||||
private migrationControllerNameText!: azdata.InputBoxComponent;
|
private migrationServiceNameText!: azdata.InputBoxComponent;
|
||||||
private _formSubmitButton!: azdata.ButtonComponent;
|
private _formSubmitButton!: azdata.ButtonComponent;
|
||||||
|
|
||||||
private _statusLoadingComponent!: azdata.LoadingComponent;
|
private _statusLoadingComponent!: azdata.LoadingComponent;
|
||||||
private migrationControllerAuthKeyTable!: azdata.DeclarativeTableComponent;
|
private migrationServiceAuthKeyTable!: azdata.DeclarativeTableComponent;
|
||||||
private _connectionStatus!: azdata.InfoBoxComponent;
|
private _connectionStatus!: azdata.InfoBoxComponent;
|
||||||
private _copyKey1Button!: azdata.ButtonComponent;
|
private _copyKey1Button!: azdata.ButtonComponent;
|
||||||
private _copyKey2Button!: azdata.ButtonComponent;
|
private _copyKey2Button!: azdata.ButtonComponent;
|
||||||
@@ -33,8 +33,11 @@ export class CreateMigrationControllerDialog {
|
|||||||
private _dialogObject!: azdata.window.Dialog;
|
private _dialogObject!: azdata.window.Dialog;
|
||||||
private _view!: azdata.ModelView;
|
private _view!: azdata.ModelView;
|
||||||
|
|
||||||
|
private createdMigrationService!: SqlMigrationService;
|
||||||
|
private createdMigrationServiceNodeNames!: string[];
|
||||||
|
|
||||||
constructor(private migrationStateModel: MigrationStateModel, private irPage: IntergrationRuntimePage) {
|
constructor(private migrationStateModel: MigrationStateModel, private irPage: IntergrationRuntimePage) {
|
||||||
this._dialogObject = azdata.window.createModelViewDialog(constants.IR_PAGE_TITLE, 'MigrationControllerDialog', 'medium');
|
this._dialogObject = azdata.window.createModelViewDialog(constants.CREATE_MIGRATION_SERVICE_TITLE, 'MigrationServiceDialog', 'medium');
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
@@ -51,15 +54,18 @@ export class CreateMigrationControllerDialog {
|
|||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this._formSubmitButton.onDidClick(async (e) => {
|
this._formSubmitButton.onDidClick(async (e) => {
|
||||||
|
this._dialogObject.message = {
|
||||||
|
text: ''
|
||||||
|
};
|
||||||
this._statusLoadingComponent.loading = true;
|
this._statusLoadingComponent.loading = true;
|
||||||
this._formSubmitButton.enabled = false;
|
this._formSubmitButton.enabled = false;
|
||||||
|
|
||||||
const subscription = this.migrationStateModel._targetSubscription;
|
const subscription = this.migrationStateModel._targetSubscription;
|
||||||
const resourceGroup = (this.migrationControllerResourceGroupDropdown.value as azdata.CategoryValue).name;
|
const resourceGroup = (this.migrationServiceResourceGroupDropdown.value as azdata.CategoryValue).name;
|
||||||
const region = (this.migrationControllerRegionDropdown.value as azdata.CategoryValue).name;
|
const region = (this.migrationServiceRegionDropdown.value as azdata.CategoryValue).name;
|
||||||
const controllerName = this.migrationControllerNameText.value;
|
const serviceName = this.migrationServiceNameText.value;
|
||||||
|
|
||||||
const formValidationErrors = this.validateCreateControllerForm(subscription, resourceGroup, region, controllerName);
|
const formValidationErrors = this.validateCreateServiceForm(subscription, resourceGroup, region, serviceName);
|
||||||
|
|
||||||
if (formValidationErrors.length > 0) {
|
if (formValidationErrors.length > 0) {
|
||||||
this.setDialogMessage(formValidationErrors);
|
this.setDialogMessage(formValidationErrors);
|
||||||
@@ -69,9 +75,9 @@ export class CreateMigrationControllerDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const createdController = await createMigrationController(this.migrationStateModel._azureAccount, subscription, resourceGroup, region, controllerName!);
|
this.createdMigrationService = await createSqlMigrationService(this.migrationStateModel._azureAccount, subscription, resourceGroup, region, serviceName!);
|
||||||
if (createdController.error) {
|
if (this.createdMigrationService.error) {
|
||||||
this.setDialogMessage(`${createdController.error.code} : ${createdController.error.message}`);
|
this.setDialogMessage(`${this.createdMigrationService.error.code} : ${this.createdMigrationService.error.message}`);
|
||||||
this._statusLoadingComponent.loading = false;
|
this._statusLoadingComponent.loading = false;
|
||||||
this._formSubmitButton.enabled = true;
|
this._formSubmitButton.enabled = true;
|
||||||
return;
|
return;
|
||||||
@@ -79,13 +85,13 @@ export class CreateMigrationControllerDialog {
|
|||||||
this._dialogObject.message = {
|
this._dialogObject.message = {
|
||||||
text: ''
|
text: ''
|
||||||
};
|
};
|
||||||
this.migrationStateModel._migrationController = createdController;
|
|
||||||
await this.refreshAuthTable();
|
await this.refreshAuthTable();
|
||||||
await this.refreshStatus();
|
await this.refreshStatus();
|
||||||
this._setupContainer.display = 'inline';
|
this._setupContainer.display = 'inline';
|
||||||
this._statusLoadingComponent.loading = false;
|
this._statusLoadingComponent.loading = false;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
|
this.setDialogMessage(e.message);
|
||||||
this._statusLoadingComponent.loading = false;
|
this._statusLoadingComponent.loading = false;
|
||||||
this._formSubmitButton.enabled = true;
|
this._formSubmitButton.enabled = true;
|
||||||
return;
|
return;
|
||||||
@@ -93,16 +99,16 @@ export class CreateMigrationControllerDialog {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this._statusLoadingComponent = view.modelBuilder.loadingComponent().withProps({
|
this._statusLoadingComponent = view.modelBuilder.loadingComponent().withProps({
|
||||||
loadingText: constants.CONTROLLER_DIALOG_CONTROLLER_CONTAINER_LOADING_HELP,
|
loadingText: constants.LOADING_MIGRATION_SERVICES,
|
||||||
loading: false
|
loading: false
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const creationStatusContainer = this.createControllerStatus();
|
const creationStatusContainer = this.createServiceStatus();
|
||||||
|
|
||||||
const formBuilder = view.modelBuilder.formContainer().withFormItems(
|
const formBuilder = view.modelBuilder.formContainer().withFormItems(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
component: this.migrationControllerDropdownsContainer()
|
component: this.migrationServiceDropdownContainer()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: this._formSubmitButton
|
component: this._formSubmitButton
|
||||||
@@ -132,36 +138,30 @@ export class CreateMigrationControllerDialog {
|
|||||||
this._dialogObject.cancelButton.onClick((e) => {
|
this._dialogObject.cancelButton.onClick((e) => {
|
||||||
});
|
});
|
||||||
this._dialogObject.okButton.onClick((e) => {
|
this._dialogObject.okButton.onClick((e) => {
|
||||||
this.irPage.populateMigrationController();
|
this.irPage.populateMigrationService(this.createdMigrationService, this.createdMigrationServiceNodeNames);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private migrationControllerDropdownsContainer(): azdata.FlexContainer {
|
private migrationServiceDropdownContainer(): azdata.FlexContainer {
|
||||||
const dialogDescription = this._view.modelBuilder.text().withProps({
|
const dialogDescription = this._view.modelBuilder.text().withProps({
|
||||||
value: constants.IR_PAGE_DESCRIPTION,
|
value: constants.MIGRATION_SERVICE_DIALOG_DESCRIPTION
|
||||||
links: [
|
|
||||||
{
|
|
||||||
text: constants.LEARN_MORE,
|
|
||||||
url: 'https://www.microsoft.com' // TODO: add a proper link to the docs.
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const formHeading = this._view.modelBuilder.text().withProps({
|
const formHeading = this._view.modelBuilder.text().withProps({
|
||||||
value: constants.CONTROLLER_DIALOG_CREATE_CONTROLLER_FORM_HEADING
|
value: constants.CREATE_SERVICE_FORM_HEADING
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const subscriptionDropdownLabel = this._view.modelBuilder.text().withProps({
|
const subscriptionDropdownLabel = this._view.modelBuilder.text().withProps({
|
||||||
value: constants.SUBSCRIPTION
|
value: constants.SUBSCRIPTION
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this.migrationControllerSubscriptionDropdown = this._view.modelBuilder.dropDown().withProps({
|
this.migrationServiceSubscriptionDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||||
required: true,
|
required: true,
|
||||||
enabled: false
|
enabled: false
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this.migrationControllerSubscriptionDropdown.onValueChanged((e) => {
|
this.migrationServiceSubscriptionDropdown.onValueChanged((e) => {
|
||||||
if (this.migrationControllerSubscriptionDropdown.value) {
|
if (this.migrationServiceSubscriptionDropdown.value) {
|
||||||
this.populateResourceGroups();
|
this.populateResourceGroups();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -170,43 +170,43 @@ export class CreateMigrationControllerDialog {
|
|||||||
value: constants.RESOURCE_GROUP
|
value: constants.RESOURCE_GROUP
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this.migrationControllerResourceGroupDropdown = this._view.modelBuilder.dropDown().withProps({
|
this.migrationServiceResourceGroupDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||||
required: true
|
required: true
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const controllerNameLabel = this._view.modelBuilder.text().withProps({
|
const migrationServiceNameLabel = this._view.modelBuilder.text().withProps({
|
||||||
value: constants.NAME
|
value: constants.NAME
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this.migrationControllerNameText = this._view.modelBuilder.inputBox().component();
|
this.migrationServiceNameText = this._view.modelBuilder.inputBox().component();
|
||||||
|
|
||||||
const regionsDropdownLabel = this._view.modelBuilder.text().withProps({
|
const regionsDropdownLabel = this._view.modelBuilder.text().withProps({
|
||||||
value: constants.REGION
|
value: constants.REGION
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this.migrationControllerRegionDropdown = this._view.modelBuilder.dropDown().withProps({
|
this.migrationServiceRegionDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||||
required: true,
|
required: true,
|
||||||
values: getMigrationControllerRegions()
|
values: getSqlMigrationServiceRegions()
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const flexContainer = this._view.modelBuilder.flexContainer().withItems([
|
const flexContainer = this._view.modelBuilder.flexContainer().withItems([
|
||||||
dialogDescription,
|
dialogDescription,
|
||||||
formHeading,
|
formHeading,
|
||||||
subscriptionDropdownLabel,
|
subscriptionDropdownLabel,
|
||||||
this.migrationControllerSubscriptionDropdown,
|
this.migrationServiceSubscriptionDropdown,
|
||||||
resourceGroupDropdownLabel,
|
resourceGroupDropdownLabel,
|
||||||
this.migrationControllerResourceGroupDropdown,
|
this.migrationServiceResourceGroupDropdown,
|
||||||
controllerNameLabel,
|
migrationServiceNameLabel,
|
||||||
this.migrationControllerNameText,
|
this.migrationServiceNameText,
|
||||||
regionsDropdownLabel,
|
regionsDropdownLabel,
|
||||||
this.migrationControllerRegionDropdown
|
this.migrationServiceRegionDropdown
|
||||||
]).withLayout({
|
]).withLayout({
|
||||||
flexFlow: 'column'
|
flexFlow: 'column'
|
||||||
}).component();
|
}).component();
|
||||||
return flexContainer;
|
return flexContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private validateCreateControllerForm(subscription: azureResource.AzureResourceSubscription, resourceGroup: string | undefined, region: string | undefined, controllerName: string | undefined): string {
|
private validateCreateServiceForm(subscription: azureResource.AzureResourceSubscription, resourceGroup: string | undefined, region: string | undefined, migrationServiceName: string | undefined): string {
|
||||||
const errors: string[] = [];
|
const errors: string[] = [];
|
||||||
if (!subscription) {
|
if (!subscription) {
|
||||||
errors.push(constants.INVALID_SUBSCRIPTION_ERROR);
|
errors.push(constants.INVALID_SUBSCRIPTION_ERROR);
|
||||||
@@ -217,29 +217,29 @@ export class CreateMigrationControllerDialog {
|
|||||||
if (!region) {
|
if (!region) {
|
||||||
errors.push(constants.INVALID_REGION_ERROR);
|
errors.push(constants.INVALID_REGION_ERROR);
|
||||||
}
|
}
|
||||||
if (!controllerName || controllerName.length === 0) {
|
if (!migrationServiceName || migrationServiceName.length === 0) {
|
||||||
errors.push(constants.INVALID_CONTROLLER_NAME_ERROR);
|
errors.push(constants.INVALID_SERVICE_NAME_ERROR);
|
||||||
}
|
}
|
||||||
return errors.join(os.EOL);
|
return errors.join(os.EOL);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async populateSubscriptions(): Promise<void> {
|
private async populateSubscriptions(): Promise<void> {
|
||||||
this.migrationControllerSubscriptionDropdown.loading = true;
|
this.migrationServiceSubscriptionDropdown.loading = true;
|
||||||
this.migrationControllerResourceGroupDropdown.loading = true;
|
this.migrationServiceResourceGroupDropdown.loading = true;
|
||||||
|
|
||||||
|
|
||||||
this.migrationControllerSubscriptionDropdown.values = [
|
this.migrationServiceSubscriptionDropdown.values = [
|
||||||
{
|
{
|
||||||
displayName: this.migrationStateModel._targetSubscription.name,
|
displayName: this.migrationStateModel._targetSubscription.name,
|
||||||
name: ''
|
name: ''
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
this.migrationControllerSubscriptionDropdown.loading = false;
|
this.migrationServiceSubscriptionDropdown.loading = false;
|
||||||
this.populateResourceGroups();
|
this.populateResourceGroups();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async populateResourceGroups(): Promise<void> {
|
private async populateResourceGroups(): Promise<void> {
|
||||||
this.migrationControllerResourceGroupDropdown.loading = true;
|
this.migrationServiceResourceGroupDropdown.loading = true;
|
||||||
let subscription = this.migrationStateModel._targetSubscription;
|
let subscription = this.migrationStateModel._targetSubscription;
|
||||||
const resourceGroups = await getResourceGroups(this.migrationStateModel._azureAccount, subscription);
|
const resourceGroups = await getResourceGroups(this.migrationStateModel._azureAccount, subscription);
|
||||||
let resourceGroupDropdownValues: azdata.CategoryValue[] = [];
|
let resourceGroupDropdownValues: azdata.CategoryValue[] = [];
|
||||||
@@ -258,39 +258,39 @@ export class CreateMigrationControllerDialog {
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
this.migrationControllerResourceGroupDropdown.values = resourceGroupDropdownValues;
|
this.migrationServiceResourceGroupDropdown.values = resourceGroupDropdownValues;
|
||||||
this.migrationControllerResourceGroupDropdown.loading = false;
|
this.migrationServiceResourceGroupDropdown.loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private createControllerStatus(): azdata.FlexContainer {
|
private createServiceStatus(): azdata.FlexContainer {
|
||||||
|
|
||||||
const setupIRHeadingText = this._view.modelBuilder.text().withProps({
|
const setupIRHeadingText = this._view.modelBuilder.text().withProps({
|
||||||
value: constants.CONTROLLER_DIALOG_CONTROLLER_CONTAINER_HEADING,
|
value: constants.SERVICE_CONTAINER_HEADING,
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
'font-weight': 'bold'
|
'font-weight': 'bold'
|
||||||
}
|
}
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const setupIRdescription = this._view.modelBuilder.text().withProps({
|
const setupIRdescription = this._view.modelBuilder.text().withProps({
|
||||||
value: constants.CONTROLLER_DIALOG_CONTROLLER_CONTAINER_DESCRIPTION,
|
value: constants.SERVICE_CONTAINER_DESCRIPTION,
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const irSetupStep1Text = this._view.modelBuilder.text().withProps({
|
const irSetupStep1Text = this._view.modelBuilder.text().withProps({
|
||||||
value: constants.CONTROLLER_STEP1,
|
value: constants.SERVICE_STEP1,
|
||||||
links: [
|
links: [
|
||||||
{
|
{
|
||||||
text: constants.CONTROLLER_STEP1_LINK,
|
text: constants.SERVICE_STEP1_LINK,
|
||||||
url: 'https://www.microsoft.com/download/details.aspx?id=39717'
|
url: 'https://www.microsoft.com/download/details.aspx?id=39717'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const irSetupStep2Text = this._view.modelBuilder.text().withProps({
|
const irSetupStep2Text = this._view.modelBuilder.text().withProps({
|
||||||
value: constants.CONTROLLER_STEP2
|
value: constants.SERVICE_STEP2
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const irSetupStep3Text = this._view.modelBuilder.hyperlink().withProps({
|
const irSetupStep3Text = this._view.modelBuilder.hyperlink().withProps({
|
||||||
label: constants.CONTROLLER_STEP3,
|
label: constants.SERVICE_STEP3,
|
||||||
url: '',
|
url: '',
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
'margin-top': '10px',
|
'margin-top': '10px',
|
||||||
@@ -326,7 +326,7 @@ export class CreateMigrationControllerDialog {
|
|||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
|
|
||||||
this.migrationControllerAuthKeyTable = this._view.modelBuilder.declarativeTable().withProps({
|
this.migrationServiceAuthKeyTable = this._view.modelBuilder.declarativeTable().withProps({
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
displayName: constants.NAME,
|
displayName: constants.NAME,
|
||||||
@@ -370,7 +370,7 @@ export class CreateMigrationControllerDialog {
|
|||||||
setupIRdescription,
|
setupIRdescription,
|
||||||
irSetupStep1Text,
|
irSetupStep1Text,
|
||||||
irSetupStep2Text,
|
irSetupStep2Text,
|
||||||
this.migrationControllerAuthKeyTable,
|
this.migrationServiceAuthKeyTable,
|
||||||
irSetupStep3Text,
|
irSetupStep3Text,
|
||||||
this._connectionStatus,
|
this._connectionStatus,
|
||||||
refreshLoadingIndicator
|
refreshLoadingIndicator
|
||||||
@@ -389,26 +389,26 @@ export class CreateMigrationControllerDialog {
|
|||||||
|
|
||||||
private async refreshStatus(): Promise<void> {
|
private async refreshStatus(): Promise<void> {
|
||||||
const subscription = this.migrationStateModel._targetSubscription;
|
const subscription = this.migrationStateModel._targetSubscription;
|
||||||
const resourceGroup = (this.migrationControllerResourceGroupDropdown.value as azdata.CategoryValue).name;
|
const resourceGroup = (this.migrationServiceResourceGroupDropdown.value as azdata.CategoryValue).name;
|
||||||
const region = (this.migrationControllerRegionDropdown.value as azdata.CategoryValue).name;
|
const region = (this.migrationServiceRegionDropdown.value as azdata.CategoryValue).name;
|
||||||
const controllerStatus = await getMigrationController(this.migrationStateModel._azureAccount, subscription, resourceGroup, region, this.migrationStateModel._migrationController!.name);
|
const migrationServiceStatus = await getSqlMigrationService(this.migrationStateModel._azureAccount, subscription, resourceGroup, region, this.createdMigrationService!.name);
|
||||||
const controllerMonitoringStatus = await getMigrationControllerMonitoringData(this.migrationStateModel._azureAccount, subscription, resourceGroup, region, this.migrationStateModel._migrationController!.name);
|
const migrationServiceMonitoringStatus = await getSqlMigrationServiceMonitoringData(this.migrationStateModel._azureAccount, subscription, resourceGroup, region, this.createdMigrationService!.name);
|
||||||
this.migrationStateModel._nodeNames = controllerMonitoringStatus.nodes.map((node) => {
|
this.createdMigrationServiceNodeNames = migrationServiceMonitoringStatus.nodes.map((node) => {
|
||||||
return node.nodeName;
|
return node.nodeName;
|
||||||
});
|
});
|
||||||
if (controllerStatus) {
|
if (migrationServiceStatus) {
|
||||||
const state = controllerStatus.properties.integrationRuntimeState;
|
const state = migrationServiceStatus.properties.integrationRuntimeState;
|
||||||
|
|
||||||
if (state === 'Online') {
|
if (state === 'Online') {
|
||||||
this._connectionStatus.updateProperties(<azdata.InfoBoxComponentProperties>{
|
this._connectionStatus.updateProperties(<azdata.InfoBoxComponentProperties>{
|
||||||
text: constants.CONTROLLER_READY(this.migrationStateModel._migrationController!.name, this.migrationStateModel._nodeNames.join(', ')),
|
text: constants.SERVICE_READY(this.createdMigrationService!.name, this.createdMigrationServiceNodeNames.join(', ')),
|
||||||
style: 'success'
|
style: 'success'
|
||||||
});
|
});
|
||||||
this._dialogObject.okButton.enabled = true;
|
this._dialogObject.okButton.enabled = true;
|
||||||
} else {
|
} else {
|
||||||
this._connectionStatus.text = constants.CONTROLLER_NOT_READY(this.migrationStateModel._migrationController!.name);
|
this._connectionStatus.text = constants.SERVICE_NOT_READY(this.createdMigrationService!.name);
|
||||||
this._connectionStatus.updateProperties(<azdata.InfoBoxComponentProperties>{
|
this._connectionStatus.updateProperties(<azdata.InfoBoxComponentProperties>{
|
||||||
text: constants.CONTROLLER_NOT_READY(this.migrationStateModel._migrationController!.name),
|
text: constants.SERVICE_NOT_READY(this.createdMigrationService!.name),
|
||||||
style: 'warning'
|
style: 'warning'
|
||||||
});
|
});
|
||||||
this._dialogObject.okButton.enabled = false;
|
this._dialogObject.okButton.enabled = false;
|
||||||
@@ -418,17 +418,17 @@ export class CreateMigrationControllerDialog {
|
|||||||
}
|
}
|
||||||
private async refreshAuthTable(): Promise<void> {
|
private async refreshAuthTable(): Promise<void> {
|
||||||
const subscription = this.migrationStateModel._targetSubscription;
|
const subscription = this.migrationStateModel._targetSubscription;
|
||||||
const resourceGroup = (this.migrationControllerResourceGroupDropdown.value as azdata.CategoryValue).name;
|
const resourceGroup = (this.migrationServiceResourceGroupDropdown.value as azdata.CategoryValue).name;
|
||||||
const region = (this.migrationControllerRegionDropdown.value as azdata.CategoryValue).name;
|
const region = (this.migrationServiceRegionDropdown.value as azdata.CategoryValue).name;
|
||||||
const keys = await getMigrationControllerAuthKeys(this.migrationStateModel._azureAccount, subscription, resourceGroup, region, this.migrationStateModel._migrationController!.name);
|
const keys = await getSqlMigrationServiceAuthKeys(this.migrationStateModel._azureAccount, subscription, resourceGroup, region, this.createdMigrationService!.name);
|
||||||
|
|
||||||
this._copyKey1Button = this._view.modelBuilder.button().withProps({
|
this._copyKey1Button = this._view.modelBuilder.button().withProps({
|
||||||
iconPath: IconPathHelper.copy
|
iconPath: IconPathHelper.copy
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this._copyKey1Button.onDidClick((e) => {
|
this._copyKey1Button.onDidClick((e) => {
|
||||||
vscode.env.clipboard.writeText(<string>this.migrationControllerAuthKeyTable.dataValues![0][1].value);
|
vscode.env.clipboard.writeText(<string>this.migrationServiceAuthKeyTable.dataValues![0][1].value);
|
||||||
vscode.window.showInformationMessage(constants.CONTROLLER_KEY_COPIED_HELP);
|
vscode.window.showInformationMessage(constants.SERVICE_KEY_COPIED_HELP);
|
||||||
});
|
});
|
||||||
|
|
||||||
this._copyKey2Button = this._view.modelBuilder.button().withProps({
|
this._copyKey2Button = this._view.modelBuilder.button().withProps({
|
||||||
@@ -436,8 +436,8 @@ export class CreateMigrationControllerDialog {
|
|||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this._copyKey2Button.onDidClick((e) => {
|
this._copyKey2Button.onDidClick((e) => {
|
||||||
vscode.env.clipboard.writeText(<string>this.migrationControllerAuthKeyTable.dataValues![1][1].value);
|
vscode.env.clipboard.writeText(<string>this.migrationServiceAuthKeyTable.dataValues![1][1].value);
|
||||||
vscode.window.showInformationMessage(constants.CONTROLLER_KEY_COPIED_HELP);
|
vscode.window.showInformationMessage(constants.SERVICE_KEY_COPIED_HELP);
|
||||||
});
|
});
|
||||||
|
|
||||||
this._refreshKey1Button = this._view.modelBuilder.button().withProps({
|
this._refreshKey1Button = this._view.modelBuilder.button().withProps({
|
||||||
@@ -454,11 +454,11 @@ export class CreateMigrationControllerDialog {
|
|||||||
this._refreshKey2Button.onDidClick((e) => { //TODO: add refresh logic
|
this._refreshKey2Button.onDidClick((e) => { //TODO: add refresh logic
|
||||||
});
|
});
|
||||||
|
|
||||||
this.migrationControllerAuthKeyTable.updateProperties({
|
this.migrationServiceAuthKeyTable.updateProperties({
|
||||||
dataValues: [
|
dataValues: [
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
value: constants.CONTROLLER_KEY1_LABEL
|
value: constants.SERVICE_KEY1_LABEL
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: keys.authKey1
|
value: keys.authKey1
|
||||||
@@ -472,7 +472,7 @@ export class CreateMigrationControllerDialog {
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
value: constants.CONTROLLER_KEY2_LABEL
|
value: constants.SERVICE_KEY2_LABEL
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: keys.authKey2
|
value: keys.authKey2
|
||||||
@@ -8,15 +8,17 @@ import { IconPathHelper } from '../../constants/iconPathHelper';
|
|||||||
import { MigrationContext } from '../../models/migrationLocalStorage';
|
import { MigrationContext } from '../../models/migrationLocalStorage';
|
||||||
import { MigrationCutoverDialogModel } from './migrationCutoverDialogModel';
|
import { MigrationCutoverDialogModel } from './migrationCutoverDialogModel';
|
||||||
import * as loc from '../../constants/strings';
|
import * as loc from '../../constants/strings';
|
||||||
|
import { getSqlServerName } from '../../api/utils';
|
||||||
export class MigrationCutoverDialog {
|
export class MigrationCutoverDialog {
|
||||||
private _dialogObject!: azdata.window.Dialog;
|
private _dialogObject!: azdata.window.Dialog;
|
||||||
private _view!: azdata.ModelView;
|
private _view!: azdata.ModelView;
|
||||||
private _model: MigrationCutoverDialogModel;
|
private _model: MigrationCutoverDialogModel;
|
||||||
|
|
||||||
private _databaseTitleName!: azdata.TextComponent;
|
private _databaseTitleName!: azdata.TextComponent;
|
||||||
private _databaseCutoverButton!: azdata.ButtonComponent;
|
private _cutoverButton!: azdata.ButtonComponent;
|
||||||
private _refresh!: azdata.ButtonComponent;
|
private _refreshButton!: azdata.ButtonComponent;
|
||||||
private _cancel!: azdata.ButtonComponent;
|
private _cancelButton!: azdata.ButtonComponent;
|
||||||
|
private _refreshLoader!: azdata.LoadingComponent;
|
||||||
|
|
||||||
private _serverName!: azdata.TextComponent;
|
private _serverName!: azdata.TextComponent;
|
||||||
private _serverVersion!: azdata.TextComponent;
|
private _serverVersion!: azdata.TextComponent;
|
||||||
@@ -43,7 +45,7 @@ export class MigrationCutoverDialog {
|
|||||||
let tab = azdata.window.createTab('');
|
let tab = azdata.window.createTab('');
|
||||||
tab.registerContent(async (view: azdata.ModelView) => {
|
tab.registerContent(async (view: azdata.ModelView) => {
|
||||||
this._view = view;
|
this._view = view;
|
||||||
const sourceDetails = this.createInfoField(loc.SOURCE_VERSION, '');
|
const sourceDetails = this.createInfoField(loc.SOURCE_SERVER, '');
|
||||||
const sourceVersion = this.createInfoField(loc.SOURCE_VERSION, '');
|
const sourceVersion = this.createInfoField(loc.SOURCE_VERSION, '');
|
||||||
|
|
||||||
this._serverName = sourceDetails.text;
|
this._serverName = sourceDetails.text;
|
||||||
@@ -183,30 +185,30 @@ export class MigrationCutoverDialog {
|
|||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
value: loc.ACTIVE_BACKUP_FILES,
|
value: loc.ACTIVE_BACKUP_FILES,
|
||||||
width: 150,
|
width: 280,
|
||||||
type: azdata.ColumnType.text
|
type: azdata.ColumnType.text
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: loc.TYPE,
|
value: loc.TYPE,
|
||||||
width: 100,
|
width: 90,
|
||||||
type: azdata.ColumnType.text
|
type: azdata.ColumnType.text
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: loc.STATUS,
|
value: loc.STATUS,
|
||||||
width: 100,
|
width: 60,
|
||||||
type: azdata.ColumnType.text
|
type: azdata.ColumnType.text
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: loc.BACKUP_START_TIME,
|
value: loc.BACKUP_START_TIME,
|
||||||
width: 150,
|
width: 130,
|
||||||
type: azdata.ColumnType.text
|
type: azdata.ColumnType.text
|
||||||
}, {
|
}, {
|
||||||
value: loc.FIRST_LSN,
|
value: loc.FIRST_LSN,
|
||||||
width: 150,
|
width: 120,
|
||||||
type: azdata.ColumnType.text
|
type: azdata.ColumnType.text
|
||||||
}, {
|
}, {
|
||||||
value: loc.LAST_LSN,
|
value: loc.LAST_LSN,
|
||||||
width: 150,
|
width: 120,
|
||||||
type: azdata.ColumnType.text
|
type: azdata.ColumnType.text
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -235,11 +237,12 @@ export class MigrationCutoverDialog {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
const form = formBuilder.withLayout({ width: '100%' }).component();
|
const form = formBuilder.withLayout({ width: '100%' }).component();
|
||||||
return view.initializeModel(form);
|
return view.initializeModel(form).then((value) => {
|
||||||
|
this.refreshStatus();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
this._dialogObject.content = [tab];
|
this._dialogObject.content = [tab];
|
||||||
azdata.window.openDialog(this._dialogObject);
|
azdata.window.openDialog(this._dialogObject);
|
||||||
this.refreshStatus();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -262,7 +265,7 @@ export class MigrationCutoverDialog {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this._databaseCutoverButton = this._view.modelBuilder.button().withProps({
|
this._cutoverButton = this._view.modelBuilder.button().withProps({
|
||||||
iconPath: IconPathHelper.cutover,
|
iconPath: IconPathHelper.cutover,
|
||||||
iconHeight: '14px',
|
iconHeight: '14px',
|
||||||
iconWidth: '12px',
|
iconWidth: '12px',
|
||||||
@@ -272,7 +275,7 @@ export class MigrationCutoverDialog {
|
|||||||
enabled: false
|
enabled: false
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this._databaseCutoverButton.onDidClick(async (e) => {
|
this._cutoverButton.onDidClick(async (e) => {
|
||||||
if (this._startCutover) {
|
if (this._startCutover) {
|
||||||
await this._model.startCutover();
|
await this._model.startCutover();
|
||||||
this.refreshStatus();
|
this.refreshStatus();
|
||||||
@@ -284,14 +287,14 @@ export class MigrationCutoverDialog {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
header.addItem(this._databaseCutoverButton, {
|
header.addItem(this._cutoverButton, {
|
||||||
flex: '0',
|
flex: '0',
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
'width': '100px'
|
'width': '100px'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this._cancel = this._view.modelBuilder.button().withProps({
|
this._cancelButton = this._view.modelBuilder.button().withProps({
|
||||||
iconPath: IconPathHelper.discard,
|
iconPath: IconPathHelper.discard,
|
||||||
iconHeight: '16px',
|
iconHeight: '16px',
|
||||||
iconWidth: '16px',
|
iconWidth: '16px',
|
||||||
@@ -300,11 +303,11 @@ export class MigrationCutoverDialog {
|
|||||||
width: '130px'
|
width: '130px'
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this._cancel.onDidClick((e) => {
|
this._cancelButton.onDidClick((e) => {
|
||||||
this.cancelMigration();
|
this.cancelMigration();
|
||||||
});
|
});
|
||||||
|
|
||||||
header.addItem(this._cancel, {
|
header.addItem(this._cancelButton, {
|
||||||
flex: '0',
|
flex: '0',
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
'width': '130px'
|
'width': '130px'
|
||||||
@@ -312,7 +315,7 @@ export class MigrationCutoverDialog {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
this._refresh = this._view.modelBuilder.button().withProps({
|
this._refreshButton = this._view.modelBuilder.button().withProps({
|
||||||
iconPath: IconPathHelper.refresh,
|
iconPath: IconPathHelper.refresh,
|
||||||
iconHeight: '16px',
|
iconHeight: '16px',
|
||||||
iconWidth: '16px',
|
iconWidth: '16px',
|
||||||
@@ -321,28 +324,39 @@ export class MigrationCutoverDialog {
|
|||||||
width: '100px'
|
width: '100px'
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this._refresh.onDidClick((e) => {
|
this._refreshButton.onDidClick((e) => {
|
||||||
this.refreshStatus();
|
this.refreshStatus();
|
||||||
});
|
});
|
||||||
|
|
||||||
header.addItem(this._refresh, {
|
header.addItem(this._refreshButton, {
|
||||||
flex: '0',
|
flex: '0',
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
'width': '100px'
|
'width': '100px'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._refreshLoader = this._view.modelBuilder.loadingComponent().withProps({
|
||||||
|
loading: false,
|
||||||
|
height: '55px'
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
header.addItem(this._refreshLoader, {
|
||||||
|
flex: '0'
|
||||||
|
});
|
||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async refreshStatus(): Promise<void> {
|
private async refreshStatus(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
|
this._refreshLoader.loading = true;
|
||||||
|
this._cutoverButton.enabled = false;
|
||||||
|
this._cancelButton.enabled = false;
|
||||||
await this._model.fetchStatus();
|
await this._model.fetchStatus();
|
||||||
const sqlServerInfo = await azdata.connection.getServerInfo(this._model._migration.sourceConnectionProfile.connectionId);
|
const sqlServerInfo = await azdata.connection.getServerInfo(this._model._migration.sourceConnectionProfile.connectionId);
|
||||||
const sqlServerName = this._model._migration.sourceConnectionProfile.serverName;
|
const sqlServerName = this._model._migration.sourceConnectionProfile.serverName;
|
||||||
const sqlServerVersion = sqlServerInfo.serverVersion;
|
const versionName = getSqlServerName(sqlServerInfo.serverMajorVersion!);
|
||||||
const sqlServerEdition = sqlServerInfo.serverEdition;
|
const sqlServerVersion = versionName ? versionName : sqlServerInfo.serverVersion;
|
||||||
const targetServerName = this._model._migration.targetManagedInstance.name;
|
const targetServerName = this._model._migration.targetManagedInstance.name;
|
||||||
let targetServerVersion;
|
let targetServerVersion;
|
||||||
if (this._model.migrationStatus.id.includes('managedInstances')) {
|
if (this._model.migrationStatus.id.includes('managedInstances')) {
|
||||||
@@ -382,7 +396,7 @@ export class MigrationCutoverDialog {
|
|||||||
|
|
||||||
this._serverName.value = sqlServerName;
|
this._serverName.value = sqlServerName;
|
||||||
this._serverVersion.value = `${sqlServerVersion}
|
this._serverVersion.value = `${sqlServerVersion}
|
||||||
${sqlServerEdition}`;
|
${sqlServerInfo.serverVersion}`;
|
||||||
|
|
||||||
this._targetServer.value = targetServerName;
|
this._targetServer.value = targetServerName;
|
||||||
this._targetVersion.value = targetServerVersion;
|
this._targetVersion.value = targetServerVersion;
|
||||||
@@ -396,6 +410,9 @@ export class MigrationCutoverDialog {
|
|||||||
|
|
||||||
this._fileCount.value = loc.ACTIVE_BACKUP_FILES_ITEMS(tableData.length);
|
this._fileCount.value = loc.ACTIVE_BACKUP_FILES_ITEMS(tableData.length);
|
||||||
|
|
||||||
|
//Sorting files in descending order of backupStartTime
|
||||||
|
tableData.sort((file1, file2) => new Date(file1.backupStartTime) > new Date(file2.backupStartTime) ? - 1 : 1);
|
||||||
|
|
||||||
this.fileTable.data = tableData.map((row) => {
|
this.fileTable.data = tableData.map((row) => {
|
||||||
return [
|
return [
|
||||||
row.fileName,
|
row.fileName,
|
||||||
@@ -411,14 +428,17 @@ export class MigrationCutoverDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (migrationStatusTextValue === 'InProgress') {
|
if (migrationStatusTextValue === 'InProgress') {
|
||||||
this._databaseCutoverButton.enabled = true;
|
const fileNotRestored = await tableData.some(file => file.status !== 'Restored');
|
||||||
|
this._cutoverButton.enabled = !fileNotRestored;
|
||||||
|
this._cancelButton.enabled = true;
|
||||||
} else {
|
} else {
|
||||||
this._databaseCutoverButton.enabled = false;
|
this._cutoverButton.enabled = false;
|
||||||
this._cancel.enabled = false;
|
this._cancelButton.enabled = false;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
}
|
||||||
|
this._refreshLoader.loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private createInfoField(label: string, value: string): {
|
private createInfoField(label: string, value: string): {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export class MigrationStatusDialog {
|
|||||||
private _refresh!: azdata.ButtonComponent;
|
private _refresh!: azdata.ButtonComponent;
|
||||||
private _statusDropdown!: azdata.DropDownComponent;
|
private _statusDropdown!: azdata.DropDownComponent;
|
||||||
private _statusTable!: azdata.DeclarativeTableComponent;
|
private _statusTable!: azdata.DeclarativeTableComponent;
|
||||||
|
private _refreshLoader!: azdata.LoadingComponent;
|
||||||
|
|
||||||
constructor(migrations: MigrationContext[], private _filter: MigrationCategory) {
|
constructor(migrations: MigrationContext[], private _filter: MigrationCategory) {
|
||||||
this._model = new MigrationStatusDialogModel(migrations);
|
this._model = new MigrationStatusDialogModel(migrations);
|
||||||
@@ -102,11 +103,19 @@ export class MigrationStatusDialog {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._refreshLoader = this._view.modelBuilder.loadingComponent().withProps({
|
||||||
|
loading: false,
|
||||||
|
height: '55px'
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
flexContainer.addItem(this._refreshLoader, {
|
||||||
|
flex: '0'
|
||||||
|
});
|
||||||
|
|
||||||
return flexContainer;
|
return flexContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private populateMigrationTable(): void {
|
private populateMigrationTable(): void {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const migrations = this._model.filterMigration(
|
const migrations = this._model.filterMigration(
|
||||||
this._searchBox.value!,
|
this._searchBox.value!,
|
||||||
@@ -115,6 +124,10 @@ export class MigrationStatusDialog {
|
|||||||
|
|
||||||
const data: azdata.DeclarativeTableCellValue[][] = [];
|
const data: azdata.DeclarativeTableCellValue[][] = [];
|
||||||
|
|
||||||
|
migrations.sort((m1, m2) => {
|
||||||
|
return new Date(m1.migrationContext.properties.startedOn) > new Date(m2.migrationContext.properties.startedOn) ? -1 : 1;
|
||||||
|
});
|
||||||
|
|
||||||
migrations.forEach((migration) => {
|
migrations.forEach((migration) => {
|
||||||
const migrationRow: azdata.DeclarativeTableCellValue[] = [];
|
const migrationRow: azdata.DeclarativeTableCellValue[] = [];
|
||||||
|
|
||||||
@@ -133,8 +146,8 @@ export class MigrationStatusDialog {
|
|||||||
value: migration.migrationContext.properties.migrationStatus
|
value: migration.migrationContext.properties.migrationStatus
|
||||||
});
|
});
|
||||||
|
|
||||||
const sqlMigrationIcon = this._view.modelBuilder.image().withProps({
|
const targetMigrationIcon = this._view.modelBuilder.image().withProps({
|
||||||
iconPath: IconPathHelper.sqlMigrationLogo,
|
iconPath: (migration.targetManagedInstance.type === 'microsoft.sql/managedinstances') ? IconPathHelper.sqlMiLogo : IconPathHelper.sqlVmLogo,
|
||||||
iconWidth: '16px',
|
iconWidth: '16px',
|
||||||
iconHeight: '16px',
|
iconHeight: '16px',
|
||||||
width: '32px',
|
width: '32px',
|
||||||
@@ -145,7 +158,7 @@ export class MigrationStatusDialog {
|
|||||||
url: ''
|
url: ''
|
||||||
}).component();
|
}).component();
|
||||||
sqlMigrationName.onDidClick((e) => {
|
sqlMigrationName.onDidClick((e) => {
|
||||||
vscode.window.showInformationMessage('Feature coming soon');
|
vscode.window.showInformationMessage(loc.COMING_SOON);
|
||||||
});
|
});
|
||||||
|
|
||||||
const sqlMigrationContainer = this._view.modelBuilder.flexContainer().withProps({
|
const sqlMigrationContainer = this._view.modelBuilder.flexContainer().withProps({
|
||||||
@@ -153,7 +166,7 @@ export class MigrationStatusDialog {
|
|||||||
'justify-content': 'center'
|
'justify-content': 'center'
|
||||||
}
|
}
|
||||||
}).component();
|
}).component();
|
||||||
sqlMigrationContainer.addItem(sqlMigrationIcon, {
|
sqlMigrationContainer.addItem(targetMigrationIcon, {
|
||||||
flex: '0',
|
flex: '0',
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
'width': '32px'
|
'width': '32px'
|
||||||
@@ -174,10 +187,10 @@ export class MigrationStatusDialog {
|
|||||||
});
|
});
|
||||||
|
|
||||||
migrationRow.push({
|
migrationRow.push({
|
||||||
value: '---'
|
value: (migration.migrationContext.properties.startedOn) ? new Date(migration.migrationContext.properties.startedOn).toLocaleString() : '---'
|
||||||
});
|
});
|
||||||
migrationRow.push({
|
migrationRow.push({
|
||||||
value: '---'
|
value: (migration.migrationContext.properties.endedOn) ? new Date(migration.migrationContext.properties.endedOn).toLocaleString() : '---'
|
||||||
});
|
});
|
||||||
|
|
||||||
data.push(migrationRow);
|
data.push(migrationRow);
|
||||||
@@ -190,6 +203,7 @@ export class MigrationStatusDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private refreshTable(): void {
|
private refreshTable(): void {
|
||||||
|
this._refreshLoader.loading = true;
|
||||||
this._model._migrations.forEach(async (migration) => {
|
this._model._migrations.forEach(async (migration) => {
|
||||||
migration.migrationContext = await getDatabaseMigration(
|
migration.migrationContext = await getDatabaseMigration(
|
||||||
migration.azureAccount,
|
migration.azureAccount,
|
||||||
@@ -198,8 +212,8 @@ export class MigrationStatusDialog {
|
|||||||
migration.migrationContext.id
|
migration.migrationContext.id
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.populateMigrationTable();
|
this.populateMigrationTable();
|
||||||
|
this._refreshLoader.loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private createStatusTable(): azdata.DeclarativeTableComponent {
|
private createStatusTable(): azdata.DeclarativeTableComponent {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { azureResource } from 'azureResource';
|
import { azureResource } from 'azureResource';
|
||||||
import { DatabaseMigration, SqlMigrationController, SqlManagedInstance } from '../api/azure';
|
import { DatabaseMigration, SqlMigrationService, SqlManagedInstance, getDatabaseMigration } from '../api/azure';
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
|
|
||||||
|
|
||||||
@@ -16,23 +16,39 @@ export class MigrationLocalStorage {
|
|||||||
MigrationLocalStorage.context = context;
|
MigrationLocalStorage.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getMigrationsBySourceConnections(connectionProfile: azdata.connection.ConnectionProfile): MigrationContext[] {
|
public static async getMigrationsBySourceConnections(connectionProfile: azdata.connection.ConnectionProfile, refreshStatus?: boolean): Promise<MigrationContext[]> {
|
||||||
|
|
||||||
let dataBaseMigrations: MigrationContext[] = [];
|
const result: MigrationContext[] = [];
|
||||||
try {
|
const validMigrations: MigrationContext[] = [];
|
||||||
const migrationMementos: MigrationContext[] = this.context.globalState.get(this.mementoToken) || [];
|
|
||||||
|
|
||||||
dataBaseMigrations = migrationMementos.filter((memento) => {
|
const migrationMementos: MigrationContext[] = this.context.globalState.get(this.mementoToken) || [];
|
||||||
return memento.sourceConnectionProfile.serverName === connectionProfile.serverName;
|
for (let i = 0; i < migrationMementos.length; i++) {
|
||||||
}).map((memento) => {
|
const migration = migrationMementos[i];
|
||||||
return memento;
|
if (migration.sourceConnectionProfile.serverName === connectionProfile.serverName) {
|
||||||
});
|
if (refreshStatus) {
|
||||||
} catch (e) {
|
try {
|
||||||
console.log(e);
|
migration.migrationContext = await getDatabaseMigration(
|
||||||
|
migration.azureAccount,
|
||||||
|
migration.subscription,
|
||||||
|
migration.targetManagedInstance.location,
|
||||||
|
migration.migrationContext.id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
// Keeping only valid migrations in cache. Clearing all the migrations which return ResourceDoesNotExit error.
|
||||||
|
if (e.message === 'ResourceDoesNotExist') {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.push(migration);
|
||||||
|
}
|
||||||
|
validMigrations.push(migration);
|
||||||
}
|
}
|
||||||
|
this.context.globalState.update(this.mementoToken, validMigrations);
|
||||||
|
return result;
|
||||||
return dataBaseMigrations;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static saveMigration(
|
public static saveMigration(
|
||||||
@@ -41,7 +57,7 @@ export class MigrationLocalStorage {
|
|||||||
targetMI: SqlManagedInstance,
|
targetMI: SqlManagedInstance,
|
||||||
azureAccount: azdata.Account,
|
azureAccount: azdata.Account,
|
||||||
subscription: azureResource.AzureResourceSubscription,
|
subscription: azureResource.AzureResourceSubscription,
|
||||||
controller: SqlMigrationController): void {
|
controller: SqlMigrationService): void {
|
||||||
try {
|
try {
|
||||||
const migrationMementos: MigrationContext[] = this.context.globalState.get(this.mementoToken) || [];
|
const migrationMementos: MigrationContext[] = this.context.globalState.get(this.mementoToken) || [];
|
||||||
migrationMementos.push({
|
migrationMementos.push({
|
||||||
@@ -69,5 +85,5 @@ export interface MigrationContext {
|
|||||||
targetManagedInstance: SqlManagedInstance,
|
targetManagedInstance: SqlManagedInstance,
|
||||||
azureAccount: azdata.Account,
|
azureAccount: azdata.Account,
|
||||||
subscription: azureResource.AzureResourceSubscription,
|
subscription: azureResource.AzureResourceSubscription,
|
||||||
controller: SqlMigrationController
|
controller: SqlMigrationService
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
import { IconPath } from 'azdata';
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
@@ -23,11 +24,11 @@ export interface Checks {
|
|||||||
export interface Product extends MigrationProduct {
|
export interface Product extends MigrationProduct {
|
||||||
readonly name: string;
|
readonly name: string;
|
||||||
readonly learnMoreLink?: string;
|
readonly learnMoreLink?: string;
|
||||||
readonly icon?: string;
|
readonly icon?: IconPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Product implements Product {
|
export class Product implements Product {
|
||||||
constructor(public readonly type: MigrationProductType, public readonly name: string, public readonly icon?: string, public readonly learnMoreLink?: string) {
|
constructor(public readonly type: MigrationProductType, public readonly name: string, public readonly icon?: IconPath, public readonly learnMoreLink?: string) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { azureResource } from 'azureResource';
|
|||||||
import * as azurecore from 'azurecore';
|
import * as azurecore from 'azurecore';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import * as mssql from '../../../mssql';
|
import * as mssql from '../../../mssql';
|
||||||
import { getAvailableManagedInstanceProducts, getAvailableStorageAccounts, getBlobContainers, getFileShares, getMigrationControllers, getSubscriptions, SqlMigrationController, SqlManagedInstance, startDatabaseMigration, StartDatabaseMigrationRequest, StorageAccount, getAvailableSqlVMs, SqlVMServer } from '../api/azure';
|
import { getAvailableManagedInstanceProducts, getAvailableStorageAccounts, getBlobContainers, getFileShares, getSqlMigrationServices, getSubscriptions, SqlMigrationService, SqlManagedInstance, startDatabaseMigration, StartDatabaseMigrationRequest, StorageAccount, getAvailableSqlVMs, SqlVMServer } from '../api/azure';
|
||||||
import { SKURecommendations } from './externalContract';
|
import { SKURecommendations } from './externalContract';
|
||||||
import * as constants from '../constants/strings';
|
import * as constants from '../constants/strings';
|
||||||
import { MigrationLocalStorage } from './migrationLocalStorage';
|
import { MigrationLocalStorage } from './migrationLocalStorage';
|
||||||
@@ -96,8 +96,8 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
public _refreshNetworkShareLocation!: azureResource.BlobContainer[];
|
public _refreshNetworkShareLocation!: azureResource.BlobContainer[];
|
||||||
public _targetDatabaseNames!: string[];
|
public _targetDatabaseNames!: string[];
|
||||||
|
|
||||||
public _migrationController!: SqlMigrationController;
|
public _sqlMigrationService!: SqlMigrationService;
|
||||||
public _migrationControllers!: SqlMigrationController[];
|
public _sqlMigrationServices!: SqlMigrationService[];
|
||||||
public _nodeNames!: string[];
|
public _nodeNames!: string[];
|
||||||
|
|
||||||
private _stateChangeEventEmitter = new vscode.EventEmitter<StateChangeEvent>();
|
private _stateChangeEventEmitter = new vscode.EventEmitter<StateChangeEvent>();
|
||||||
@@ -426,39 +426,39 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async getMigrationControllerValues(subscription: azureResource.AzureResourceSubscription, managedInstance: SqlManagedInstance): Promise<azdata.CategoryValue[]> {
|
public async getSqlMigrationServiceValues(subscription: azureResource.AzureResourceSubscription, managedInstance: SqlManagedInstance): Promise<azdata.CategoryValue[]> {
|
||||||
let migrationControllerValues: azdata.CategoryValue[] = [];
|
let sqlMigrationServiceValues: azdata.CategoryValue[] = [];
|
||||||
try {
|
try {
|
||||||
this._migrationControllers = await getMigrationControllers(this._azureAccount, subscription, managedInstance.resourceGroup!, managedInstance.location);
|
this._sqlMigrationServices = await getSqlMigrationServices(this._azureAccount, subscription, managedInstance.location);
|
||||||
this._migrationControllers.forEach((migrationController) => {
|
this._sqlMigrationServices.forEach((sqlMigrationService) => {
|
||||||
migrationControllerValues.push({
|
sqlMigrationServiceValues.push({
|
||||||
name: migrationController.id,
|
name: sqlMigrationService.id,
|
||||||
displayName: `${migrationController.name}`
|
displayName: `${sqlMigrationService.name}`
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (migrationControllerValues.length === 0) {
|
if (sqlMigrationServiceValues.length === 0) {
|
||||||
migrationControllerValues = [
|
sqlMigrationServiceValues = [
|
||||||
{
|
{
|
||||||
displayName: constants.MIGRATION_CONTROLLER_NOT_FOUND_ERROR,
|
displayName: constants.SQL_MIGRATION_SERVICE_NOT_FOUND_ERROR,
|
||||||
name: ''
|
name: ''
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
migrationControllerValues = [
|
sqlMigrationServiceValues = [
|
||||||
{
|
{
|
||||||
displayName: constants.MIGRATION_CONTROLLER_NOT_FOUND_ERROR,
|
displayName: constants.SQL_MIGRATION_SERVICE_NOT_FOUND_ERROR,
|
||||||
name: ''
|
name: ''
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
return migrationControllerValues;
|
return sqlMigrationServiceValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getMigrationController(index: number): SqlMigrationController {
|
public getMigrationService(index: number): SqlMigrationService {
|
||||||
return this._migrationControllers[index];
|
return this._sqlMigrationServices[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
public async startMigration() {
|
public async startMigration() {
|
||||||
@@ -473,41 +473,41 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
const connectionPassword = await azdata.connection.getCredentials(this.sourceConnectionId);
|
const connectionPassword = await azdata.connection.getCredentials(this.sourceConnectionId);
|
||||||
|
|
||||||
const requestBody: StartDatabaseMigrationRequest = {
|
const requestBody: StartDatabaseMigrationRequest = {
|
||||||
location: this._migrationController?.properties.location!,
|
location: this._sqlMigrationService?.properties.location!,
|
||||||
properties: {
|
properties: {
|
||||||
SourceDatabaseName: '',
|
sourceDatabaseName: '',
|
||||||
MigrationController: this._migrationController?.id!,
|
migrationService: this._sqlMigrationService?.id!,
|
||||||
BackupConfiguration: {
|
backupConfiguration: {
|
||||||
TargetLocation: {
|
targetLocation: {
|
||||||
StorageAccountResourceId: this._databaseBackup.storageAccount.id,
|
storageAccountResourceId: this._databaseBackup.storageAccount.id,
|
||||||
AccountKey: this._databaseBackup.storageKey,
|
accountKey: this._databaseBackup.storageKey,
|
||||||
},
|
},
|
||||||
SourceLocation: {
|
sourceLocation: {
|
||||||
FileShare: {
|
fileShare: {
|
||||||
Path: '',
|
path: '',
|
||||||
Username: this._databaseBackup.windowsUser,
|
username: this._databaseBackup.windowsUser,
|
||||||
Password: this._databaseBackup.password,
|
password: this._databaseBackup.password,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
SourceSqlConnection: {
|
sourceSqlConnection: {
|
||||||
DataSource: currentConnection?.serverName!,
|
dataSource: currentConnection?.serverName!,
|
||||||
Username: currentConnection?.userName!,
|
username: currentConnection?.userName!,
|
||||||
Password: connectionPassword.password
|
password: connectionPassword.password
|
||||||
},
|
},
|
||||||
Scope: this._targetServerInstance.id
|
scope: this._targetServerInstance.id
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this._migrationDbs.forEach(async (db, index) => {
|
this._migrationDbs.forEach(async (db, index) => {
|
||||||
|
|
||||||
requestBody.properties.SourceDatabaseName = db;
|
requestBody.properties.sourceDatabaseName = db;
|
||||||
try {
|
try {
|
||||||
requestBody.properties.BackupConfiguration.SourceLocation.FileShare.Path = this._databaseBackup.networkShareLocations[index];
|
requestBody.properties.backupConfiguration.sourceLocation.fileShare!.path = this._databaseBackup.networkShareLocations[index];
|
||||||
const response = await startDatabaseMigration(
|
const response = await startDatabaseMigration(
|
||||||
this._azureAccount,
|
this._azureAccount,
|
||||||
this._targetSubscription,
|
this._targetSubscription,
|
||||||
this._migrationController?.properties.location!,
|
this._sqlMigrationService?.properties.location!,
|
||||||
this._targetServerInstance,
|
this._targetServerInstance,
|
||||||
this._targetDatabaseNames[index],
|
this._targetDatabaseNames[index],
|
||||||
requestBody
|
requestBody
|
||||||
@@ -519,9 +519,9 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
this._targetServerInstance,
|
this._targetServerInstance,
|
||||||
this._azureAccount,
|
this._azureAccount,
|
||||||
this._targetSubscription,
|
this._targetSubscription,
|
||||||
this._migrationController
|
this._sqlMigrationService
|
||||||
);
|
);
|
||||||
vscode.window.showInformationMessage(localize("sql.migration.starting.migration.message", 'Starting migration for database {0} to {1}', db, this._targetServerInstance.name));
|
vscode.window.showInformationMessage(localize("sql.migration.starting.migration.message", 'Starting migration for database {0} to {1} - {2}', db, this._targetServerInstance.name, this._targetDatabaseNames[index]));
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
|
|||||||
@@ -47,6 +47,15 @@ export class AccountsSelectionPage extends MigrationWizardPage {
|
|||||||
};
|
};
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (this.migrationStateModel._azureAccount?.isStale) {
|
||||||
|
this.wizard.message = {
|
||||||
|
text: constants.ACCOUNT_STALE_ERROR(this.migrationStateModel._azureAccount)
|
||||||
|
};
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.wizard.message = {
|
||||||
|
text: ''
|
||||||
|
};
|
||||||
return true;
|
return true;
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
@@ -69,7 +78,7 @@ export class AccountsSelectionPage extends MigrationWizardPage {
|
|||||||
this.migrationStateModel._subscriptions = undefined!;
|
this.migrationStateModel._subscriptions = undefined!;
|
||||||
this.migrationStateModel._targetSubscription = undefined!;
|
this.migrationStateModel._targetSubscription = undefined!;
|
||||||
this.migrationStateModel._databaseBackup.subscription = undefined!;
|
this.migrationStateModel._databaseBackup.subscription = undefined!;
|
||||||
|
this._azureAccountsDropdown.validate();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -83,6 +92,9 @@ export class AccountsSelectionPage extends MigrationWizardPage {
|
|||||||
linkAccountButton.onDidClick(async (event) => {
|
linkAccountButton.onDidClick(async (event) => {
|
||||||
await vscode.commands.executeCommand('workbench.actions.modal.linkedAccount');
|
await vscode.commands.executeCommand('workbench.actions.modal.linkedAccount');
|
||||||
await this.populateAzureAccountsDropdown();
|
await this.populateAzureAccountsDropdown();
|
||||||
|
this.wizard.message = {
|
||||||
|
text: ''
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const flexContainer = view.modelBuilder.flexContainer()
|
const flexContainer = view.modelBuilder.flexContainer()
|
||||||
@@ -155,6 +167,15 @@ export class AccountsSelectionPage extends MigrationWizardPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async onPageEnter(): Promise<void> {
|
public async onPageEnter(): Promise<void> {
|
||||||
|
this.wizard.registerNavigationValidator(pageChangeInfo => {
|
||||||
|
if (this.migrationStateModel._azureAccount.isStale === true) {
|
||||||
|
this.wizard.message = {
|
||||||
|
text: constants.ACCOUNT_STALE_ERROR(this.migrationStateModel._azureAccount)
|
||||||
|
};
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async onPageLeave(): Promise<void> {
|
public async onPageLeave(): Promise<void> {
|
||||||
|
|||||||
@@ -7,15 +7,15 @@ import * as azdata from 'azdata';
|
|||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { MigrationWizardPage } from '../models/migrationWizardPage';
|
import { MigrationWizardPage } from '../models/migrationWizardPage';
|
||||||
import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
|
import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
|
||||||
import { CreateMigrationControllerDialog } from '../dialog/createMigrationDialog/createMigrationControllerDialog';
|
import { CreateSqlMigrationServiceDialog } from '../dialog/createSqlMigrationService/createSqlMigrationServiceDialog';
|
||||||
import * as constants from '../constants/strings';
|
import * as constants from '../constants/strings';
|
||||||
import { createInformationRow, WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
|
import { createInformationRow, WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
|
||||||
import { getMigrationController, getMigrationControllerAuthKeys, getMigrationControllerMonitoringData } from '../api/azure';
|
import { getSqlMigrationService, getSqlMigrationServiceAuthKeys, getSqlMigrationServiceMonitoringData, SqlMigrationService } from '../api/azure';
|
||||||
import { IconPathHelper } from '../constants/iconPathHelper';
|
import { IconPathHelper } from '../constants/iconPathHelper';
|
||||||
|
|
||||||
export class IntergrationRuntimePage extends MigrationWizardPage {
|
export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||||
|
|
||||||
private migrationControllerDropdown!: azdata.DropDownComponent;
|
private migrationServiceDropdown!: azdata.DropDownComponent;
|
||||||
private _view!: azdata.ModelView;
|
private _view!: azdata.ModelView;
|
||||||
private _form!: azdata.FormBuilder;
|
private _form!: azdata.FormBuilder;
|
||||||
private _statusLoadingComponent!: azdata.LoadingComponent;
|
private _statusLoadingComponent!: azdata.LoadingComponent;
|
||||||
@@ -28,13 +28,13 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
|||||||
protected async registerContent(view: azdata.ModelView): Promise<void> {
|
protected async registerContent(view: azdata.ModelView): Promise<void> {
|
||||||
this._view = view;
|
this._view = view;
|
||||||
|
|
||||||
const createNewController = view.modelBuilder.hyperlink().withProps({
|
const createNewMigrationService = view.modelBuilder.hyperlink().withProps({
|
||||||
label: constants.CREATE_NEW,
|
label: constants.CREATE_NEW,
|
||||||
url: ''
|
url: ''
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
createNewController.onDidClick((e) => {
|
createNewMigrationService.onDidClick((e) => {
|
||||||
const dialog = new CreateMigrationControllerDialog(this.migrationStateModel, this);
|
const dialog = new CreateSqlMigrationServiceDialog(this.migrationStateModel, this);
|
||||||
dialog.initialize();
|
dialog.initialize();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -47,10 +47,10 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
|||||||
.withFormItems(
|
.withFormItems(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
component: this.migrationControllerDropdownsContainer()
|
component: this.migrationServiceDropdownContainer()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: createNewController
|
component: createNewMigrationService
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: this._statusLoadingComponent
|
component: this._statusLoadingComponent
|
||||||
@@ -62,7 +62,7 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async onPageEnter(): Promise<void> {
|
public async onPageEnter(): Promise<void> {
|
||||||
this.populateMigrationController();
|
this.populateMigrationService();
|
||||||
this.wizard.registerNavigationValidator((pageChangeInfo) => {
|
this.wizard.registerNavigationValidator((pageChangeInfo) => {
|
||||||
if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
|
if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
|
||||||
this.wizard.message = {
|
this.wizard.message = {
|
||||||
@@ -70,18 +70,18 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
|||||||
};
|
};
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const state = this.migrationStateModel._migrationController.properties.integrationRuntimeState;
|
const state = this.migrationStateModel._sqlMigrationService.properties.integrationRuntimeState;
|
||||||
if (!this.migrationStateModel._migrationController) {
|
if (!this.migrationStateModel._sqlMigrationService) {
|
||||||
this.wizard.message = {
|
this.wizard.message = {
|
||||||
level: azdata.window.MessageLevel.Error,
|
level: azdata.window.MessageLevel.Error,
|
||||||
text: constants.INVALID_CONTROLLER_ERROR
|
text: constants.INVALID_SERVICE_ERROR
|
||||||
};
|
};
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (state !== 'Online') {
|
if (state !== 'Online') {
|
||||||
this.wizard.message = {
|
this.wizard.message = {
|
||||||
level: azdata.window.MessageLevel.Error,
|
level: azdata.window.MessageLevel.Error,
|
||||||
text: constants.CONTROLLER_OFFLINE_ERROR
|
text: constants.SERVICE_OFFLINE_ERROR
|
||||||
};
|
};
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
@@ -102,7 +102,7 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
|||||||
protected async handleStateChange(e: StateChangeEvent): Promise<void> {
|
protected async handleStateChange(e: StateChangeEvent): Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private migrationControllerDropdownsContainer(): azdata.FlexContainer {
|
private migrationServiceDropdownContainer(): azdata.FlexContainer {
|
||||||
const descriptionText = this._view.modelBuilder.text().withProps({
|
const descriptionText = this._view.modelBuilder.text().withProps({
|
||||||
value: constants.IR_PAGE_DESCRIPTION,
|
value: constants.IR_PAGE_DESCRIPTION,
|
||||||
links: [
|
links: [
|
||||||
@@ -117,23 +117,23 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
|||||||
value: constants.IR_PAGE_NOTE
|
value: constants.IR_PAGE_NOTE
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const migrationControllerDropdownLabel = this._view.modelBuilder.text().withProps({
|
const migrationServcieDropdownLabel = this._view.modelBuilder.text().withProps({
|
||||||
value: constants.SELECT_A_MIGRATION_CONTROLLER
|
value: constants.SELECT_A_SQL_MIGRATION_SERVICE
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this.migrationControllerDropdown = this._view.modelBuilder.dropDown().withProps({
|
this.migrationServiceDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||||
required: true,
|
required: true,
|
||||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this.migrationControllerDropdown.onValueChanged(async (value) => {
|
this.migrationServiceDropdown.onValueChanged(async (value) => {
|
||||||
if (value.selected) {
|
if (value.selected) {
|
||||||
this.wizard.message = {
|
this.wizard.message = {
|
||||||
text: ''
|
text: ''
|
||||||
};
|
};
|
||||||
this.migrationStateModel._migrationController = this.migrationStateModel.getMigrationController(value.index);
|
this.migrationStateModel._sqlMigrationService = this.migrationStateModel.getMigrationService(value.index);
|
||||||
if (value !== constants.MIGRATION_CONTROLLER_NOT_FOUND_ERROR) {
|
if (value !== constants.SQL_MIGRATION_SERVICE_NOT_FOUND_ERROR) {
|
||||||
await this.loadControllerStatus();
|
await this.loadMigrationServiceStatus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -141,73 +141,77 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
|||||||
const flexContainer = this._view.modelBuilder.flexContainer().withItems([
|
const flexContainer = this._view.modelBuilder.flexContainer().withItems([
|
||||||
descriptionText,
|
descriptionText,
|
||||||
noteText,
|
noteText,
|
||||||
migrationControllerDropdownLabel,
|
migrationServcieDropdownLabel,
|
||||||
this.migrationControllerDropdown
|
this.migrationServiceDropdown
|
||||||
]).withLayout({
|
]).withLayout({
|
||||||
flexFlow: 'column'
|
flexFlow: 'column'
|
||||||
}).component();
|
}).component();
|
||||||
return flexContainer;
|
return flexContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async populateMigrationController(): Promise<void> {
|
public async populateMigrationService(sqlMigrationService?: SqlMigrationService, serviceNodes?: string[]): Promise<void> {
|
||||||
this.migrationControllerDropdown.loading = true;
|
this.migrationServiceDropdown.loading = true;
|
||||||
|
if (sqlMigrationService && serviceNodes) {
|
||||||
|
this.migrationStateModel._sqlMigrationService = sqlMigrationService;
|
||||||
|
this.migrationStateModel._nodeNames = serviceNodes;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
this.migrationControllerDropdown.values = await this.migrationStateModel.getMigrationControllerValues(this.migrationStateModel._targetSubscription, this.migrationStateModel._targetServerInstance);
|
this.migrationServiceDropdown.values = await this.migrationStateModel.getSqlMigrationServiceValues(this.migrationStateModel._targetSubscription, this.migrationStateModel._targetServerInstance);
|
||||||
if (this.migrationStateModel._migrationController) {
|
if (this.migrationStateModel._sqlMigrationService) {
|
||||||
this.migrationControllerDropdown.value = {
|
this.migrationServiceDropdown.value = {
|
||||||
name: this.migrationStateModel._migrationController.id,
|
name: this.migrationStateModel._sqlMigrationService.id,
|
||||||
displayName: this.migrationStateModel._migrationController.name
|
displayName: this.migrationStateModel._sqlMigrationService.name
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
this.migrationStateModel._migrationController = this.migrationStateModel.getMigrationController(0);
|
this.migrationStateModel._sqlMigrationService = this.migrationStateModel.getMigrationService(0);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
} finally {
|
} finally {
|
||||||
this.migrationControllerDropdown.loading = false;
|
this.migrationServiceDropdown.loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadControllerStatus(): Promise<void> {
|
private async loadMigrationServiceStatus(): Promise<void> {
|
||||||
this._statusLoadingComponent.loading = true;
|
this._statusLoadingComponent.loading = true;
|
||||||
try {
|
try {
|
||||||
this._migrationDetailsContainer.clearItems();
|
this._migrationDetailsContainer.clearItems();
|
||||||
|
|
||||||
if (this.migrationStateModel._migrationController) {
|
if (this.migrationStateModel._sqlMigrationService) {
|
||||||
const controller = await getMigrationController(
|
const migrationService = await getSqlMigrationService(
|
||||||
this.migrationStateModel._azureAccount,
|
this.migrationStateModel._azureAccount,
|
||||||
this.migrationStateModel._targetSubscription,
|
this.migrationStateModel._targetSubscription,
|
||||||
this.migrationStateModel._migrationController.properties.resourceGroup,
|
this.migrationStateModel._sqlMigrationService.properties.resourceGroup,
|
||||||
this.migrationStateModel._migrationController.properties.location,
|
this.migrationStateModel._sqlMigrationService.properties.location,
|
||||||
this.migrationStateModel._migrationController.name);
|
this.migrationStateModel._sqlMigrationService.name);
|
||||||
this.migrationStateModel._migrationController = controller;
|
this.migrationStateModel._sqlMigrationService = migrationService;
|
||||||
const controllerMonitoringStatus = await getMigrationControllerMonitoringData(
|
const migrationServiceMonitoringStatus = await getSqlMigrationServiceMonitoringData(
|
||||||
this.migrationStateModel._azureAccount,
|
this.migrationStateModel._azureAccount,
|
||||||
this.migrationStateModel._targetSubscription,
|
this.migrationStateModel._targetSubscription,
|
||||||
this.migrationStateModel._migrationController.properties.resourceGroup,
|
this.migrationStateModel._sqlMigrationService.properties.resourceGroup,
|
||||||
this.migrationStateModel._migrationController.properties.location,
|
this.migrationStateModel._sqlMigrationService.properties.location,
|
||||||
this.migrationStateModel._migrationController!.name);
|
this.migrationStateModel._sqlMigrationService!.name);
|
||||||
this.migrationStateModel._nodeNames = controllerMonitoringStatus.nodes.map((node) => {
|
this.migrationStateModel._nodeNames = migrationServiceMonitoringStatus.nodes.map((node) => {
|
||||||
return node.nodeName;
|
return node.nodeName;
|
||||||
});
|
});
|
||||||
const migrationControllerAuthKeys = await getMigrationControllerAuthKeys(
|
const migrationServiceAuthKeys = await getSqlMigrationServiceAuthKeys(
|
||||||
this.migrationStateModel._azureAccount,
|
this.migrationStateModel._azureAccount,
|
||||||
this.migrationStateModel._targetSubscription,
|
this.migrationStateModel._targetSubscription,
|
||||||
this.migrationStateModel._migrationController.properties.resourceGroup,
|
this.migrationStateModel._sqlMigrationService.properties.resourceGroup,
|
||||||
this.migrationStateModel._migrationController.properties.location,
|
this.migrationStateModel._sqlMigrationService.properties.location,
|
||||||
this.migrationStateModel._migrationController!.name
|
this.migrationStateModel._sqlMigrationService!.name
|
||||||
);
|
);
|
||||||
|
|
||||||
const migrationControllerTitle = this._view.modelBuilder.text().withProps({
|
const migrationServiceTitle = this._view.modelBuilder.text().withProps({
|
||||||
value: constants.CONTROLLER_DETAILS_HEADER(controller.name),
|
value: constants.SQL_MIGRATION_SERVICE_DETAILS_HEADER(migrationService.name),
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
'font-weight': 'bold'
|
'font-weight': 'bold'
|
||||||
}
|
}
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const connectionStatusLabel = this._view.modelBuilder.text().withProps({
|
const connectionStatusLabel = this._view.modelBuilder.text().withProps({
|
||||||
value: constants.CONTROLLER_CONNECTION_STATUS,
|
value: constants.SERVICE_CONNECTION_STATUS,
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
'font-weight': 'bold',
|
'font-weight': 'bold',
|
||||||
'width': '150px'
|
'width': '150px'
|
||||||
@@ -242,32 +246,32 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
|||||||
refreshStatus.onDidClick(async (e) => {
|
refreshStatus.onDidClick(async (e) => {
|
||||||
connectionStatusLoader.loading = true;
|
connectionStatusLoader.loading = true;
|
||||||
|
|
||||||
const controller = await getMigrationController(
|
const migrationService = await getSqlMigrationService(
|
||||||
this.migrationStateModel._azureAccount,
|
this.migrationStateModel._azureAccount,
|
||||||
this.migrationStateModel._targetSubscription,
|
this.migrationStateModel._targetSubscription,
|
||||||
this.migrationStateModel._migrationController.properties.resourceGroup,
|
this.migrationStateModel._sqlMigrationService.properties.resourceGroup,
|
||||||
this.migrationStateModel._migrationController.properties.location,
|
this.migrationStateModel._sqlMigrationService.properties.location,
|
||||||
this.migrationStateModel._migrationController.name);
|
this.migrationStateModel._sqlMigrationService.name);
|
||||||
this.migrationStateModel._migrationController = controller;
|
this.migrationStateModel._sqlMigrationService = migrationService;
|
||||||
const controllerMonitoringStatus = await getMigrationControllerMonitoringData(
|
const migrationServiceMonitoringStatus = await getSqlMigrationServiceMonitoringData(
|
||||||
this.migrationStateModel._azureAccount,
|
this.migrationStateModel._azureAccount,
|
||||||
this.migrationStateModel._targetSubscription,
|
this.migrationStateModel._targetSubscription,
|
||||||
this.migrationStateModel._migrationController.properties.resourceGroup,
|
this.migrationStateModel._sqlMigrationService.properties.resourceGroup,
|
||||||
this.migrationStateModel._migrationController.properties.location,
|
this.migrationStateModel._sqlMigrationService.properties.location,
|
||||||
this.migrationStateModel._migrationController!.name);
|
this.migrationStateModel._sqlMigrationService!.name);
|
||||||
this.migrationStateModel._nodeNames = controllerMonitoringStatus.nodes.map((node) => {
|
this.migrationStateModel._nodeNames = migrationServiceMonitoringStatus.nodes.map((node) => {
|
||||||
return node.nodeName;
|
return node.nodeName;
|
||||||
});
|
});
|
||||||
|
|
||||||
const state = controller.properties.integrationRuntimeState;
|
const state = migrationService.properties.integrationRuntimeState;
|
||||||
if (state === 'Online') {
|
if (state === 'Online') {
|
||||||
connectionStatus.updateProperties(<azdata.InfoBoxComponentProperties>{
|
connectionStatus.updateProperties(<azdata.InfoBoxComponentProperties>{
|
||||||
text: constants.CONTROLLER_READY(this.migrationStateModel._migrationController!.name, this.migrationStateModel._nodeNames.join(', ')),
|
text: constants.SERVICE_READY(this.migrationStateModel._sqlMigrationService!.name, this.migrationStateModel._nodeNames.join(', ')),
|
||||||
style: 'success'
|
style: 'success'
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
connectionStatus.updateProperties(<azdata.InfoBoxComponentProperties>{
|
connectionStatus.updateProperties(<azdata.InfoBoxComponentProperties>{
|
||||||
text: constants.CONTROLLER_NOT_READY(this.migrationStateModel._migrationController!.name),
|
text: constants.SERVICE_NOT_READY(this.migrationStateModel._sqlMigrationService!.name),
|
||||||
style: 'error'
|
style: 'error'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -275,16 +279,16 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
|||||||
connectionStatusLoader.loading = false;
|
connectionStatusLoader.loading = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (controller) {
|
const state = migrationService.properties.integrationRuntimeState;
|
||||||
const state = controller.properties.integrationRuntimeState;
|
if (migrationService) {
|
||||||
if (state === 'Online') {
|
if (state === 'Online') {
|
||||||
connectionStatus.updateProperties(<azdata.InfoBoxComponentProperties>{
|
connectionStatus.updateProperties(<azdata.InfoBoxComponentProperties>{
|
||||||
text: constants.CONTROLLER_READY(this.migrationStateModel._migrationController!.name, this.migrationStateModel._nodeNames.join(', ')),
|
text: constants.SERVICE_READY(this.migrationStateModel._sqlMigrationService!.name, this.migrationStateModel._nodeNames.join(', ')),
|
||||||
style: 'success'
|
style: 'success'
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
connectionStatus.updateProperties(<azdata.InfoBoxComponentProperties>{
|
connectionStatus.updateProperties(<azdata.InfoBoxComponentProperties>{
|
||||||
text: constants.CONTROLLER_NOT_READY(this.migrationStateModel._migrationController!.name),
|
text: constants.SERVICE_NOT_READY(this.migrationStateModel._sqlMigrationService!.name),
|
||||||
style: 'error'
|
style: 'error'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -297,7 +301,7 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
|||||||
}
|
}
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const migrationControllerAuthKeyTable = this._view.modelBuilder.declarativeTable().withProps({
|
const migrationServiceAuthKeyTable = this._view.modelBuilder.declarativeTable().withProps({
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
displayName: constants.NAME,
|
displayName: constants.NAME,
|
||||||
@@ -341,8 +345,8 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
|||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
copyKey1Button.onDidClick((e) => {
|
copyKey1Button.onDidClick((e) => {
|
||||||
vscode.env.clipboard.writeText(<string>migrationControllerAuthKeyTable.dataValues![0][1].value);
|
vscode.env.clipboard.writeText(<string>migrationServiceAuthKeyTable.dataValues![0][1].value);
|
||||||
vscode.window.showInformationMessage(constants.CONTROLLER_KEY_COPIED_HELP);
|
vscode.window.showInformationMessage(constants.SERVICE_KEY_COPIED_HELP);
|
||||||
});
|
});
|
||||||
|
|
||||||
const copyKey2Button = this._view.modelBuilder.button().withProps({
|
const copyKey2Button = this._view.modelBuilder.button().withProps({
|
||||||
@@ -350,8 +354,8 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
|||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
copyKey2Button.onDidClick((e) => {
|
copyKey2Button.onDidClick((e) => {
|
||||||
vscode.env.clipboard.writeText(<string>migrationControllerAuthKeyTable.dataValues![1][1].value);
|
vscode.env.clipboard.writeText(<string>migrationServiceAuthKeyTable.dataValues![1][1].value);
|
||||||
vscode.window.showInformationMessage(constants.CONTROLLER_KEY_COPIED_HELP);
|
vscode.window.showInformationMessage(constants.SERVICE_KEY_COPIED_HELP);
|
||||||
});
|
});
|
||||||
|
|
||||||
const refreshKey1Button = this._view.modelBuilder.button().withProps({
|
const refreshKey1Button = this._view.modelBuilder.button().withProps({
|
||||||
@@ -368,15 +372,14 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
|||||||
refreshKey2Button.onDidClick((e) => {//TODO: add refresh logic
|
refreshKey2Button.onDidClick((e) => {//TODO: add refresh logic
|
||||||
});
|
});
|
||||||
|
|
||||||
|
migrationServiceAuthKeyTable.updateProperties({
|
||||||
migrationControllerAuthKeyTable.updateProperties({
|
|
||||||
dataValues: [
|
dataValues: [
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
value: constants.CONTROLLER_KEY1_LABEL
|
value: constants.SERVICE_KEY1_LABEL
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: migrationControllerAuthKeys.authKey1
|
value: migrationServiceAuthKeys.authKey1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: copyKey1Button
|
value: copyKey1Button
|
||||||
@@ -387,10 +390,10 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
value: constants.CONTROLLER_KEY2_LABEL
|
value: constants.SERVICE_KEY2_LABEL
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: migrationControllerAuthKeys.authKey2
|
value: migrationServiceAuthKeys.authKey2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: copyKey2Button
|
value: copyKey2Button
|
||||||
@@ -404,14 +407,14 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
|||||||
|
|
||||||
this._migrationDetailsContainer.addItems(
|
this._migrationDetailsContainer.addItems(
|
||||||
[
|
[
|
||||||
migrationControllerTitle,
|
migrationServiceTitle,
|
||||||
createInformationRow(this._view, constants.SUBSCRIPTION, this.migrationStateModel._targetSubscription.name),
|
createInformationRow(this._view, constants.SUBSCRIPTION, this.migrationStateModel._targetSubscription.name),
|
||||||
createInformationRow(this._view, constants.RESOURCE_GROUP, controller.properties.resourceGroup),
|
createInformationRow(this._view, constants.RESOURCE_GROUP, migrationService.properties.resourceGroup),
|
||||||
createInformationRow(this._view, constants.LOCATION, controller.properties.location),
|
createInformationRow(this._view, constants.LOCATION, migrationService.properties.location),
|
||||||
connectionLabelContainer,
|
connectionLabelContainer,
|
||||||
connectionStatusLoader,
|
connectionStatusLoader,
|
||||||
authenticationKeysLabel,
|
authenticationKeysLabel,
|
||||||
migrationControllerAuthKeyTable
|
migrationServiceAuthKeyTable
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,18 +4,32 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as path from 'path';
|
|
||||||
import { MigrationWizardPage } from '../models/migrationWizardPage';
|
import { MigrationWizardPage } from '../models/migrationWizardPage';
|
||||||
import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
|
import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
|
||||||
import { Product, ProductLookupTable } from '../models/product';
|
import { Product } from '../models/product';
|
||||||
import { AssessmentResultsDialog } from '../dialog/assessmentResults/assessmentResultsDialog';
|
import { AssessmentResultsDialog } from '../dialog/assessmentResults/assessmentResultsDialog';
|
||||||
import * as constants from '../constants/strings';
|
import * as constants from '../constants/strings';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { EOL } from 'os';
|
import { EOL } from 'os';
|
||||||
|
import { IconPathHelper } from '../constants/iconPathHelper';
|
||||||
|
|
||||||
// import { SqlMigrationService } from '../../../../extensions/mssql/src/sqlMigration/sqlMigrationService';
|
// import { SqlMigrationService } from '../../../../extensions/mssql/src/sqlMigration/sqlMigrationService';
|
||||||
|
|
||||||
export class SKURecommendationPage extends MigrationWizardPage {
|
export class SKURecommendationPage extends MigrationWizardPage {
|
||||||
|
|
||||||
|
private supportedProducts: Product[] = [
|
||||||
|
{
|
||||||
|
type: 'AzureSQLMI',
|
||||||
|
name: constants.SKU_RECOMMENDATION_MI_CARD_TEXT,
|
||||||
|
icon: IconPathHelper.sqlMiLogo
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'AzureSQLVM',
|
||||||
|
name: constants.SKU_RECOMMENDATION_VM_CARD_TEXT,
|
||||||
|
icon: IconPathHelper.sqlVmLogo
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
// For future reference: DO NOT EXPOSE WIZARD DIRECTLY THROUGH HERE.
|
// For future reference: DO NOT EXPOSE WIZARD DIRECTLY THROUGH HERE.
|
||||||
constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) {
|
constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) {
|
||||||
super(wizard, azdata.window.createWizardPage(constants.SKU_RECOMMENDATION_PAGE_TITLE), migrationStateModel);
|
super(wizard, azdata.window.createWizardPage(constants.SKU_RECOMMENDATION_PAGE_TITLE), migrationStateModel);
|
||||||
@@ -53,7 +67,7 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
if (e.selected) {
|
if (e.selected) {
|
||||||
this.migrationStateModel._targetSubscription = this.migrationStateModel.getSubscription(e.index);
|
this.migrationStateModel._targetSubscription = this.migrationStateModel.getSubscription(e.index);
|
||||||
this.migrationStateModel._targetServerInstance = undefined!;
|
this.migrationStateModel._targetServerInstance = undefined!;
|
||||||
this.migrationStateModel._migrationController = undefined!;
|
this.migrationStateModel._sqlMigrationService = undefined!;
|
||||||
this.populateResourceInstanceDropdown();
|
this.populateResourceInstanceDropdown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -66,7 +80,7 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
if (e.selected &&
|
if (e.selected &&
|
||||||
e.selected !== constants.NO_MANAGED_INSTANCE_FOUND &&
|
e.selected !== constants.NO_MANAGED_INSTANCE_FOUND &&
|
||||||
e.selected !== constants.NO_VIRTUAL_MACHINE_FOUND) {
|
e.selected !== constants.NO_VIRTUAL_MACHINE_FOUND) {
|
||||||
this.migrationStateModel._migrationControllers = undefined!;
|
this.migrationStateModel._sqlMigrationServices = undefined!;
|
||||||
if (this._rbg.selectedCardId === 'AzureSQLVM') {
|
if (this._rbg.selectedCardId === 'AzureSQLVM') {
|
||||||
this.migrationStateModel._targetServerInstance = this.migrationStateModel.getVirtualMachine(e.index);
|
this.migrationStateModel._targetServerInstance = this.migrationStateModel.getVirtualMachine(e.index);
|
||||||
} else {
|
} else {
|
||||||
@@ -179,7 +193,7 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private constructTargets(): void {
|
private constructTargets(): void {
|
||||||
const products: Product[] = Object.values(ProductLookupTable);
|
const products: Product[] = this.supportedProducts;
|
||||||
|
|
||||||
this._rbg = this._view!.modelBuilder.radioCardGroup().withProperties<azdata.RadioCardGroupComponentProperties>({
|
this._rbg = this._view!.modelBuilder.radioCardGroup().withProperties<azdata.RadioCardGroupComponentProperties>({
|
||||||
cards: [],
|
cards: [],
|
||||||
@@ -191,7 +205,6 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
products.forEach((product) => {
|
products.forEach((product) => {
|
||||||
const imagePath = path.resolve(this.migrationStateModel.getExtensionPath(), 'media', product.icon ?? 'ads.svg');
|
|
||||||
let dbCount = 0;
|
let dbCount = 0;
|
||||||
if (product.type === 'AzureSQLVM') {
|
if (product.type === 'AzureSQLVM') {
|
||||||
dbCount = this._dbCount;
|
dbCount = this._dbCount;
|
||||||
@@ -238,7 +251,7 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
|
|
||||||
this._rbg.cards.push({
|
this._rbg.cards.push({
|
||||||
id: product.type,
|
id: product.type,
|
||||||
icon: imagePath,
|
icon: product.icon,
|
||||||
descriptions
|
descriptions
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -248,7 +261,6 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
this._rbg.onLinkClick(async (value) => {
|
this._rbg.onLinkClick(async (value) => {
|
||||||
|
|
||||||
//check which card is being selected, and open correct dialog based on link
|
//check which card is being selected, and open correct dialog based on link
|
||||||
console.log(value);
|
|
||||||
if (value.description.linkDisplayValue === 'View/Change') {
|
if (value.description.linkDisplayValue === 'View/Change') {
|
||||||
if (value.cardId === 'AzureSQLVM') {
|
if (value.cardId === 'AzureSQLVM') {
|
||||||
await vmDialog.openDialog();
|
await vmDialog.openDialog();
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export class SummaryPage extends MigrationWizardPage {
|
|||||||
createHeadingTextComponent(this._view, constants.DATABASE_BACKUP_PAGE_TITLE),
|
createHeadingTextComponent(this._view, constants.DATABASE_BACKUP_PAGE_TITLE),
|
||||||
this.createNetworkContainerRows(),
|
this.createNetworkContainerRows(),
|
||||||
createHeadingTextComponent(this._view, constants.IR_PAGE_TITLE),
|
createHeadingTextComponent(this._view, constants.IR_PAGE_TITLE),
|
||||||
createInformationRow(this._view, constants.IR_PAGE_TITLE, this.migrationStateModel._migrationController?.name!),
|
createInformationRow(this._view, constants.IR_PAGE_TITLE, this.migrationStateModel._sqlMigrationService?.name!),
|
||||||
createInformationRow(this._view, constants.SUMMARY_IR_NODE, this.migrationStateModel._nodeNames.join(', ')),
|
createInformationRow(this._view, constants.SUMMARY_IR_NODE, this.migrationStateModel._nodeNames.join(', ')),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export class TempTargetSelectionPage extends MigrationWizardPage {
|
|||||||
if (e.selected) {
|
if (e.selected) {
|
||||||
this.migrationStateModel._targetSubscription = this.migrationStateModel.getSubscription(e.index);
|
this.migrationStateModel._targetSubscription = this.migrationStateModel.getSubscription(e.index);
|
||||||
this.migrationStateModel._targetServerInstance = undefined!;
|
this.migrationStateModel._targetServerInstance = undefined!;
|
||||||
this.migrationStateModel._migrationController = undefined!;
|
this.migrationStateModel._sqlMigrationService = undefined!;
|
||||||
this.populateManagedInstanceDropdown();
|
this.populateManagedInstanceDropdown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -48,7 +48,7 @@ export class TempTargetSelectionPage extends MigrationWizardPage {
|
|||||||
}).component();
|
}).component();
|
||||||
this._managedInstanceDropdown.onValueChanged((e) => {
|
this._managedInstanceDropdown.onValueChanged((e) => {
|
||||||
if (e.selected) {
|
if (e.selected) {
|
||||||
this.migrationStateModel._migrationControllers = undefined!;
|
this.migrationStateModel._sqlMigrationServices = undefined!;
|
||||||
this.migrationStateModel._targetServerInstance = this.migrationStateModel.getManagedInstance(e.index);
|
this.migrationStateModel._targetServerInstance = this.migrationStateModel.getManagedInstance(e.index);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user