mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Fixing bugs for migration extension private preview 1. (#14872)
* Fixing Database backup page target layout * Filtering out Azure sql db issues from assessment results Correcting the database count for issued databases in sku rec page. * Adding copy migration details button to migration status * Adding start migration button to toolbar * Fixing a syntax error in package.json * Adding rg and location to target selection page Filtering storage account by target location. * Fixing dashboard title to azure sql migration * Not making assessment targets selected by default. * Adding tooltip for database and instance table items. * Fixing duplicate task widget * Some fixes mentioned in the PR Localizing button text renaming a var changing null to undefined. * Adding enum for Migration target types * Fixing a critical multi db migration bug because of unhandled race condition * Adding Azure location api to azure core * Adding source database info in status
This commit is contained in:
@@ -71,6 +71,25 @@ declare module 'azureResource' {
|
|||||||
export interface AzureResourceResourceGroup extends AzureResource {
|
export interface AzureResourceResourceGroup extends AzureResource {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AzureLocation {
|
||||||
|
id: string,
|
||||||
|
name: string,
|
||||||
|
displayName: string,
|
||||||
|
regionalDisplayName: string,
|
||||||
|
metadata: {
|
||||||
|
regionType: string,
|
||||||
|
regionCategory: string,
|
||||||
|
geographyGroup: string,
|
||||||
|
longitude: number,
|
||||||
|
latitude: number,
|
||||||
|
physicalLocation: string,
|
||||||
|
pairedRegion: {
|
||||||
|
name: string,
|
||||||
|
id: string,
|
||||||
|
}[],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
export interface AzureResourceDatabase extends AzureSqlResource {
|
export interface AzureResourceDatabase extends AzureSqlResource {
|
||||||
serverName: string;
|
serverName: string;
|
||||||
serverFullName: string;
|
serverFullName: string;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { ResourceGraphClient } from '@azure/arm-resourcegraph';
|
|||||||
import { TokenCredentials } from '@azure/ms-rest-js';
|
import { TokenCredentials } from '@azure/ms-rest-js';
|
||||||
import axios, { AxiosRequestConfig } from 'axios';
|
import axios, { AxiosRequestConfig } from 'axios';
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import { AzureRestResponse, GetResourceGroupsResult, GetSubscriptionsResult, ResourceQueryResult, GetBlobContainersResult, GetFileSharesResult, HttpRequestMethod } from 'azurecore';
|
import { AzureRestResponse, GetResourceGroupsResult, GetSubscriptionsResult, ResourceQueryResult, GetBlobContainersResult, GetFileSharesResult, HttpRequestMethod, GetLocationsResult } from 'azurecore';
|
||||||
import { azureResource } from 'azureResource';
|
import { azureResource } from 'azureResource';
|
||||||
import { EOL } from 'os';
|
import { EOL } from 'os';
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
@@ -142,6 +142,40 @@ export async function getResourceGroups(appContext: AppContext, account?: azdata
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getLocations(appContext: AppContext, account?: azdata.Account, subscription?: azureResource.AzureResourceSubscription, ignoreErrors: boolean = false): Promise<GetLocationsResult> {
|
||||||
|
const result: GetLocationsResult = { locations: [], errors: [] };
|
||||||
|
if (!account?.properties?.tenants || !Array.isArray(account.properties.tenants) || !subscription) {
|
||||||
|
const error = new Error(invalidAzureAccount);
|
||||||
|
if (!ignoreErrors) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
result.errors.push(error);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
await Promise.all(account.properties.tenants.map(async (tenant: { id: string; }) => {
|
||||||
|
try {
|
||||||
|
const path = `/subscriptions/${subscription.id}/locations?api-version=2020-01-01`;
|
||||||
|
const response = await makeHttpRequest(account, subscription, path, HttpRequestMethod.GET, undefined, ignoreErrors);
|
||||||
|
result.locations.push(...response.response.data.value);
|
||||||
|
result.errors.push(...response.errors);
|
||||||
|
} catch (err) {
|
||||||
|
const error = new Error(localize('azure.accounts.getLocations.queryError', "Error fetching locations for account {0} ({1}) subscription {2} ({3}) tenant {4} : {5}",
|
||||||
|
account.displayInfo.displayName,
|
||||||
|
account.displayInfo.userId,
|
||||||
|
subscription.id,
|
||||||
|
subscription.name,
|
||||||
|
tenant.id,
|
||||||
|
err instanceof Error ? err.message : err));
|
||||||
|
console.warn(error);
|
||||||
|
if (!ignoreErrors) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
result.errors.push(error);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
export async function runResourceQuery<T extends azureResource.AzureGraphResource>(
|
export async function runResourceQuery<T extends azureResource.AzureGraphResource>(
|
||||||
account: azdata.Account,
|
account: azdata.Account,
|
||||||
subscriptions: azureResource.AzureResourceSubscription[],
|
subscriptions: azureResource.AzureResourceSubscription[],
|
||||||
|
|||||||
2
extensions/azurecore/src/azurecore.d.ts
vendored
2
extensions/azurecore/src/azurecore.d.ts
vendored
@@ -245,6 +245,7 @@ declare module 'azurecore' {
|
|||||||
export interface IExtension {
|
export interface IExtension {
|
||||||
getSubscriptions(account?: azdata.Account, ignoreErrors?: boolean, selectedOnly?: boolean): Promise<GetSubscriptionsResult>;
|
getSubscriptions(account?: azdata.Account, ignoreErrors?: boolean, selectedOnly?: boolean): Promise<GetSubscriptionsResult>;
|
||||||
getResourceGroups(account?: azdata.Account, subscription?: azureResource.AzureResourceSubscription, ignoreErrors?: boolean): Promise<GetResourceGroupsResult>;
|
getResourceGroups(account?: azdata.Account, subscription?: azureResource.AzureResourceSubscription, ignoreErrors?: boolean): Promise<GetResourceGroupsResult>;
|
||||||
|
getLocations(account?: azdata.Account, subscription?: azureResource.AzureResourceSubscription, ignoreErrors?: boolean): Promise<GetLocationsResult>;
|
||||||
getSqlManagedInstances(account: azdata.Account, subscriptions: azureResource.AzureResourceSubscription[], ignoreErrors?: boolean): Promise<GetSqlManagedInstancesResult>;
|
getSqlManagedInstances(account: azdata.Account, subscriptions: azureResource.AzureResourceSubscription[], ignoreErrors?: boolean): Promise<GetSqlManagedInstancesResult>;
|
||||||
getSqlServers(account: azdata.Account, subscriptions: azureResource.AzureResourceSubscription[], ignoreErrors?: boolean): Promise<GetSqlServersResult>;
|
getSqlServers(account: azdata.Account, subscriptions: azureResource.AzureResourceSubscription[], ignoreErrors?: boolean): Promise<GetSqlServersResult>;
|
||||||
getSqlVMServers(account: azdata.Account, subscriptions: azureResource.AzureResourceSubscription[], ignoreErrors?: boolean): Promise<GetSqlVMServersResult>;
|
getSqlVMServers(account: azdata.Account, subscriptions: azureResource.AzureResourceSubscription[], ignoreErrors?: boolean): Promise<GetSqlVMServersResult>;
|
||||||
@@ -275,6 +276,7 @@ declare module 'azurecore' {
|
|||||||
|
|
||||||
export type GetSubscriptionsResult = { subscriptions: azureResource.AzureResourceSubscription[], errors: Error[] };
|
export type GetSubscriptionsResult = { subscriptions: azureResource.AzureResourceSubscription[], errors: Error[] };
|
||||||
export type GetResourceGroupsResult = { resourceGroups: azureResource.AzureResourceResourceGroup[], errors: Error[] };
|
export type GetResourceGroupsResult = { resourceGroups: azureResource.AzureResourceResourceGroup[], errors: Error[] };
|
||||||
|
export type GetLocationsResult = {locations: azureResource.AzureLocation[], errors: Error[] };
|
||||||
export type GetSqlManagedInstancesResult = { resources: azureResource.AzureGraphResource[], errors: Error[] };
|
export type GetSqlManagedInstancesResult = { resources: azureResource.AzureGraphResource[], errors: Error[] };
|
||||||
export type GetSqlServersResult = { resources: azureResource.AzureGraphResource[], errors: Error[] };
|
export type GetSqlServersResult = { resources: azureResource.AzureGraphResource[], errors: Error[] };
|
||||||
export type GetSqlVMServersResult = { resources: azureResource.AzureGraphResource[], errors: Error[] };
|
export type GetSqlVMServersResult = { resources: azureResource.AzureGraphResource[], errors: Error[] };
|
||||||
|
|||||||
@@ -147,6 +147,11 @@ export async function activate(context: vscode.ExtensionContext): Promise<azurec
|
|||||||
: azureResourceUtils.getSubscriptions(appContext, account, ignoreErrors);
|
: azureResourceUtils.getSubscriptions(appContext, account, ignoreErrors);
|
||||||
},
|
},
|
||||||
getResourceGroups(account?: azdata.Account, subscription?: azureResource.AzureResourceSubscription, ignoreErrors?: boolean): Promise<azurecore.GetResourceGroupsResult> { return azureResourceUtils.getResourceGroups(appContext, account, subscription, ignoreErrors); },
|
getResourceGroups(account?: azdata.Account, subscription?: azureResource.AzureResourceSubscription, ignoreErrors?: boolean): Promise<azurecore.GetResourceGroupsResult> { return azureResourceUtils.getResourceGroups(appContext, account, subscription, ignoreErrors); },
|
||||||
|
getLocations(account?: azdata.Account,
|
||||||
|
subscription?: azureResource.AzureResourceSubscription,
|
||||||
|
ignoreErrors?: boolean): Promise<azurecore.GetLocationsResult> {
|
||||||
|
return azureResourceUtils.getLocations(appContext, account, subscription, ignoreErrors);
|
||||||
|
},
|
||||||
provideResources(): azureResource.IAzureResourceProvider[] {
|
provideResources(): azureResource.IAzureResourceProvider[] {
|
||||||
const arcFeaturedEnabled = vscode.workspace.getConfiguration(constants.extensionConfigSectionName).get('enableArcFeatures');
|
const arcFeaturedEnabled = vscode.workspace.getConfiguration(constants.extensionConfigSectionName).get('enableArcFeatures');
|
||||||
const providers: azureResource.IAzureResourceProvider[] = [
|
const providers: azureResource.IAzureResourceProvider[] = [
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ import * as azurecore from 'azurecore';
|
|||||||
import { azureResource } from 'azureResource';
|
import { azureResource } from 'azureResource';
|
||||||
|
|
||||||
export class AzurecoreApiStub implements azurecore.IExtension {
|
export class AzurecoreApiStub implements azurecore.IExtension {
|
||||||
|
getLocations(_account?: azdata.Account, _subscription?: azureResource.AzureResourceSubscription, _ignoreErrors?: boolean): Promise<azurecore.GetLocationsResult> {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
makeAzureRestRequest(_account: azdata.Account, _subscription: azureResource.AzureResourceSubscription, _serviceUrl: string, _requestType: azurecore.HttpRequestMethod, _requestBody?: any, _ignoreErrors?: boolean): Promise<azurecore.AzureRestResponse> {
|
makeAzureRestRequest(_account: azdata.Account, _subscription: azureResource.AzureResourceSubscription, _serviceUrl: string, _requestType: azurecore.HttpRequestMethod, _requestBody?: any, _ignoreErrors?: boolean): Promise<azurecore.AzureRestResponse> {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "sql-migration",
|
"name": "sql-migration",
|
||||||
"displayName": "%displayName%",
|
"displayName": "%displayName%",
|
||||||
"description": "%description%",
|
"description": "%description%",
|
||||||
"version": "0.0.4",
|
"version": "0.0.5",
|
||||||
"publisher": "Microsoft",
|
"publisher": "Microsoft",
|
||||||
"preview": true,
|
"preview": true,
|
||||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt",
|
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt",
|
||||||
|
|||||||
@@ -26,6 +26,20 @@ export async function getSubscriptions(account: azdata.Account): Promise<Subscri
|
|||||||
return subscriptions.subscriptions;
|
return subscriptions.subscriptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getLocations(account: azdata.Account, subscription: Subscription): Promise<azureResource.AzureLocation[]> {
|
||||||
|
const api = await getAzureCoreAPI();
|
||||||
|
const response = await api.getLocations(account, subscription, true);
|
||||||
|
if (response.errors.length > 0) {
|
||||||
|
throw new Error(response.errors.toString());
|
||||||
|
}
|
||||||
|
sortResourceArrayByName(response.locations);
|
||||||
|
const supportedLocations = ['eastus2', 'eastus2euap'];
|
||||||
|
const filteredLocations = response.locations.filter(loc => {
|
||||||
|
return supportedLocations.includes(loc.name);
|
||||||
|
});
|
||||||
|
return filteredLocations;
|
||||||
|
}
|
||||||
|
|
||||||
export type AzureProduct = azureResource.AzureGraphResource;
|
export type AzureProduct = azureResource.AzureGraphResource;
|
||||||
|
|
||||||
export async function getResourceGroups(account: azdata.Account, subscription: Subscription): Promise<azureResource.AzureResourceResourceGroup[]> {
|
export async function getResourceGroups(account: azdata.Account, subscription: Subscription): Promise<azureResource.AzureResourceResourceGroup[]> {
|
||||||
@@ -65,9 +79,9 @@ export type SqlVMServer = {
|
|||||||
tenantId: string,
|
tenantId: string,
|
||||||
subscriptionId: string
|
subscriptionId: string
|
||||||
};
|
};
|
||||||
export async function getAvailableSqlVMs(account: azdata.Account, subscription: Subscription): Promise<SqlVMServer[]> {
|
export async function getAvailableSqlVMs(account: azdata.Account, subscription: Subscription, resourceGroup: azureResource.AzureResourceResourceGroup): Promise<SqlVMServer[]> {
|
||||||
const api = await getAzureCoreAPI();
|
const api = await getAzureCoreAPI();
|
||||||
const path = `/subscriptions/${subscription.id}/providers/Microsoft.SqlVirtualMachine/sqlVirtualMachines?api-version=2017-03-01-preview`;
|
const path = `/subscriptions/${subscription.id}/resourceGroups/${resourceGroup.name}/providers/Microsoft.SqlVirtualMachine/sqlVirtualMachines?api-version=2017-03-01-preview`;
|
||||||
const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true);
|
const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true);
|
||||||
if (response.errors.length > 0) {
|
if (response.errors.length > 0) {
|
||||||
throw new Error(response.errors.toString());
|
throw new Error(response.errors.toString());
|
||||||
|
|||||||
@@ -87,10 +87,12 @@ export const DATABASE_BACKUP_MIGRATION_MODE_OFFLINE_LABEL = localize('sql.migrat
|
|||||||
export const DATABASE_BACKUP_EMAIL_NOTIFICATION_LABEL = localize('sql.migration.database.backup.email.notification.label', "Email notifications");
|
export const DATABASE_BACKUP_EMAIL_NOTIFICATION_LABEL = localize('sql.migration.database.backup.email.notification.label', "Email notifications");
|
||||||
export const DATABASE_BACKUP_EMAIL_NOTIFICATION_CHECKBOX_LABEL = localize('sql.migration.database.backup.email.notification.checkbox.label', "Notify me when migration is complete");
|
export const DATABASE_BACKUP_EMAIL_NOTIFICATION_CHECKBOX_LABEL = localize('sql.migration.database.backup.email.notification.checkbox.label', "Notify me when migration is complete");
|
||||||
export const NO_SUBSCRIPTIONS_FOUND = localize('sql.migration.no.subscription.found', "No subscription found");
|
export const NO_SUBSCRIPTIONS_FOUND = localize('sql.migration.no.subscription.found', "No subscription found");
|
||||||
|
export const NO_LOCATION_FOUND = localize('sql.migration.no.location.found', "No location found");
|
||||||
export const NO_STORAGE_ACCOUNT_FOUND = localize('sql.migration.no.storageAccount.found', "No storage account found");
|
export const NO_STORAGE_ACCOUNT_FOUND = localize('sql.migration.no.storageAccount.found', "No storage account found");
|
||||||
export const NO_FILESHARES_FOUND = localize('sql.migration.no.fileShares.found', "No file shares found");
|
export const NO_FILESHARES_FOUND = localize('sql.migration.no.fileShares.found', "No file shares found");
|
||||||
export const NO_BLOBCONTAINERS_FOUND = localize('sql.migration.no.blobContainers.found', "No blob containers found");
|
export const NO_BLOBCONTAINERS_FOUND = localize('sql.migration.no.blobContainers.found', "No blob containers found");
|
||||||
export const INVALID_SUBSCRIPTION_ERROR = localize('sql.migration.invalid.subscription.error', "Please select a valid subscription to proceed.");
|
export const INVALID_SUBSCRIPTION_ERROR = localize('sql.migration.invalid.subscription.error', "Please select a valid subscription to proceed.");
|
||||||
|
export const INVALID_LOCATION_ERROR = localize('sql.migration.invalid.location.error', "Please select a valid location to proceed.");
|
||||||
export const INVALID_STORAGE_ACCOUNT_ERROR = localize('sql.migration.invalid.storageAccount.error', "Please select a valid storage account to proceed.");
|
export const INVALID_STORAGE_ACCOUNT_ERROR = localize('sql.migration.invalid.storageAccount.error', "Please select a valid storage account to proceed.");
|
||||||
export const INVALID_FILESHARE_ERROR = localize('sql.migration.invalid.fileShare.error', "Please select a valid file share to proceed.");
|
export const INVALID_FILESHARE_ERROR = localize('sql.migration.invalid.fileShare.error', "Please select a valid file share to proceed.");
|
||||||
export const INVALID_BLOBCONTAINER_ERROR = localize('sql.migration.invalid.blobContainer.error', "Please select a valid blob container to proceed.");
|
export const INVALID_BLOBCONTAINER_ERROR = localize('sql.migration.invalid.blobContainer.error', "Please select a valid blob container to proceed.");
|
||||||
@@ -108,7 +110,7 @@ export function TARGET_FILE_SHARE(dbName: string): string {
|
|||||||
export function TARGET_BLOB_CONTAINER(dbName: string): string {
|
export function TARGET_BLOB_CONTAINER(dbName: string): string {
|
||||||
return localize('sql.migration.blob.container', "Select the container that contains the backup files for ‘{0}’", dbName);
|
return localize('sql.migration.blob.container', "Select the container that contains the backup files for ‘{0}’", dbName);
|
||||||
}
|
}
|
||||||
export const ENTER_NETWORK_SHARE_INFORMATION = localize('sql.migration.enter.network.share.information', "Enter network share path information for selected databases");
|
export const ENTER_NETWORK_SHARE_INFORMATION = localize('sql.migration.enter.network.share.information', "Enter target names for selected databases");
|
||||||
export const ENTER_BLOB_CONTAINER_INFORMATION = localize('sql.migration.blob.container.information', "Enter the target name and select the blob container location for selected databases");
|
export const ENTER_BLOB_CONTAINER_INFORMATION = localize('sql.migration.blob.container.information', "Enter the target name and select the blob container location for 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");
|
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");
|
||||||
|
|
||||||
@@ -152,7 +154,7 @@ export function SERVICE_NOT_READY(serviceName: string): string {
|
|||||||
export function SERVICE_READY(serviceName: string, host: string): string {
|
export function SERVICE_READY(serviceName: string, host: string): string {
|
||||||
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);
|
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_SERVICE_NAME_ERROR = localize('sql.migration.invalid.service.name.error', "Please enter a valid name for the Migration Service.");
|
export const INVALID_SERVICE_NAME_ERROR = localize('sql.migration.invalid.service.name.error', "Please enter a valid name for the Migration Service.");
|
||||||
@@ -230,6 +232,7 @@ 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_DATABASE = localize('sql.migration.source.database', "Source database");
|
||||||
export const SOURCE_SERVER = localize('sql.migration.source.server', "Source server");
|
export const SOURCE_SERVER = localize('sql.migration.source.server', "Source server");
|
||||||
export const SOURCE_VERSION = localize('sql.migration.source.version', "Source version");
|
export const SOURCE_VERSION = localize('sql.migration.source.version', "Source version");
|
||||||
export const TARGET_SERVER = localize('sql.migration.target.server', "Target server");
|
export const TARGET_SERVER = localize('sql.migration.target.server', "Target server");
|
||||||
@@ -255,7 +258,7 @@ export function ACTIVE_BACKUP_FILES_ITEMS(fileCount: number) {
|
|||||||
return localize('sql.migration.active.backup.files.multiple.items', "Active Backup files ({0} items)", fileCount);
|
return localize('sql.migration.active.backup.files.multiple.items', "Active Backup files ({0} items)", fileCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export const COPY_MIGRATION_DETAILS = localize('sql.migration.copy.migration.details', "Copy Migration Details");
|
||||||
|
|
||||||
//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");
|
||||||
|
|||||||
@@ -166,14 +166,14 @@ export class SqlDatabaseTree {
|
|||||||
{
|
{
|
||||||
displayName: constants.INSTANCE,
|
displayName: constants.INSTANCE,
|
||||||
valueType: azdata.DeclarativeDataType.component,
|
valueType: azdata.DeclarativeDataType.component,
|
||||||
width: 150,
|
width: 130,
|
||||||
isReadOnly: true,
|
isReadOnly: true,
|
||||||
headerCssStyles: headerLeft
|
headerCssStyles: headerLeft
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: constants.WARNINGS,
|
displayName: constants.WARNINGS,
|
||||||
valueType: azdata.DeclarativeDataType.string,
|
valueType: azdata.DeclarativeDataType.string,
|
||||||
width: 50,
|
width: 30,
|
||||||
isReadOnly: true,
|
isReadOnly: true,
|
||||||
headerCssStyles: headerRight
|
headerCssStyles: headerRight
|
||||||
}
|
}
|
||||||
@@ -595,9 +595,9 @@ export class SqlDatabaseTree {
|
|||||||
|
|
||||||
public selectedDbs(): string[] {
|
public selectedDbs(): string[] {
|
||||||
let result: string[] = [];
|
let result: string[] = [];
|
||||||
this._databaseTable.dataValues?.forEach((arr) => {
|
this._databaseTable.dataValues?.forEach((arr, index) => {
|
||||||
if (arr[0].value === true) {
|
if (arr[0].value === true) {
|
||||||
result.push(arr[1].value.toString());
|
result.push(this._dbNames[index]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
@@ -615,8 +615,6 @@ export class SqlDatabaseTree {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
this._assessmentResultsTable.dataValues = assessmentResults;
|
this._assessmentResultsTable.dataValues = assessmentResults;
|
||||||
this._selectedIssue = this._activeIssues[0];
|
|
||||||
this.refreshAssessmentDetails();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public refreshAssessmentDetails(): void {
|
public refreshAssessmentDetails(): void {
|
||||||
@@ -734,14 +732,8 @@ export class SqlDatabaseTree {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this._dbName.value = this._serverName;
|
|
||||||
this._instanceTable.dataValues = instanceTableValues;
|
this._instanceTable.dataValues = instanceTableValues;
|
||||||
this._databaseTable.dataValues = databaseTableValues;
|
this._databaseTable.dataValues = databaseTableValues;
|
||||||
if (this._targetType === MigrationTargetType.SQLMI) {
|
|
||||||
this._activeIssues = this._model._assessmentResults.issues;
|
|
||||||
this._selectedIssue = this._model._assessmentResults?.issues[0];
|
|
||||||
this.refreshResults();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private createIconTextCell(icon: IconPath, text: string): azdata.FlexContainer {
|
private createIconTextCell(icon: IconPath, text: string): azdata.FlexContainer {
|
||||||
@@ -755,8 +747,10 @@ export class SqlDatabaseTree {
|
|||||||
}).component();
|
}).component();
|
||||||
const textComponent = this._view.modelBuilder.text().withProps({
|
const textComponent = this._view.modelBuilder.text().withProps({
|
||||||
value: text,
|
value: text,
|
||||||
|
title: text,
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
'margin': '0px'
|
'margin': '0px',
|
||||||
|
'width': '110px'
|
||||||
}
|
}
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import { MigrationCutoverDialogModel } from './migrationCutoverDialogModel';
|
|||||||
import * as loc from '../../constants/strings';
|
import * as loc from '../../constants/strings';
|
||||||
import { getSqlServerName } from '../../api/utils';
|
import { getSqlServerName } from '../../api/utils';
|
||||||
import { EOL } from 'os';
|
import { EOL } from 'os';
|
||||||
|
import * as vscode from 'vscode';
|
||||||
|
|
||||||
export class MigrationCutoverDialog {
|
export class MigrationCutoverDialog {
|
||||||
private _dialogObject!: azdata.window.Dialog;
|
private _dialogObject!: azdata.window.Dialog;
|
||||||
private _view!: azdata.ModelView;
|
private _view!: azdata.ModelView;
|
||||||
@@ -20,9 +22,11 @@ export class MigrationCutoverDialog {
|
|||||||
private _refreshButton!: azdata.ButtonComponent;
|
private _refreshButton!: azdata.ButtonComponent;
|
||||||
private _cancelButton!: azdata.ButtonComponent;
|
private _cancelButton!: azdata.ButtonComponent;
|
||||||
private _refreshLoader!: azdata.LoadingComponent;
|
private _refreshLoader!: azdata.LoadingComponent;
|
||||||
|
private _copyDatabaseMigrationDetails!: azdata.ButtonComponent;
|
||||||
|
|
||||||
private _serverName!: azdata.TextComponent;
|
private _serverName!: azdata.TextComponent;
|
||||||
private _serverVersion!: azdata.TextComponent;
|
private _serverVersion!: azdata.TextComponent;
|
||||||
|
private _sourceDatabase!: azdata.TextComponent;
|
||||||
private _targetServer!: azdata.TextComponent;
|
private _targetServer!: azdata.TextComponent;
|
||||||
private _targetVersion!: azdata.TextComponent;
|
private _targetVersion!: azdata.TextComponent;
|
||||||
private _migrationStatus!: azdata.TextComponent;
|
private _migrationStatus!: azdata.TextComponent;
|
||||||
@@ -46,9 +50,11 @@ 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 sourceDatabase = this.createInfoField(loc.SOURCE_DATABASE, '');
|
||||||
const sourceDetails = this.createInfoField(loc.SOURCE_SERVER, '');
|
const sourceDetails = this.createInfoField(loc.SOURCE_SERVER, '');
|
||||||
const sourceVersion = this.createInfoField(loc.SOURCE_VERSION, '');
|
const sourceVersion = this.createInfoField(loc.SOURCE_VERSION, '');
|
||||||
|
|
||||||
|
this._sourceDatabase = sourceDatabase.text;
|
||||||
this._serverName = sourceDetails.text;
|
this._serverName = sourceDetails.text;
|
||||||
this._serverVersion = sourceVersion.text;
|
this._serverVersion = sourceVersion.text;
|
||||||
|
|
||||||
@@ -56,6 +62,11 @@ export class MigrationCutoverDialog {
|
|||||||
flexFlow: 'column'
|
flexFlow: 'column'
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
|
flexServer.addItem(sourceDatabase.flexContainer, {
|
||||||
|
CSSStyles: {
|
||||||
|
'width': '150px'
|
||||||
|
}
|
||||||
|
});
|
||||||
flexServer.addItem(sourceDetails.flexContainer, {
|
flexServer.addItem(sourceDetails.flexContainer, {
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
'width': '150px'
|
'width': '150px'
|
||||||
@@ -336,6 +347,27 @@ export class MigrationCutoverDialog {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._copyDatabaseMigrationDetails = this._view.modelBuilder.button().withProps({
|
||||||
|
iconPath: IconPathHelper.copy,
|
||||||
|
iconHeight: '16px',
|
||||||
|
iconWidth: '16px',
|
||||||
|
label: loc.COPY_MIGRATION_DETAILS,
|
||||||
|
height: '55px',
|
||||||
|
width: '100px'
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
this._copyDatabaseMigrationDetails.onDidClick(async (e) => {
|
||||||
|
await this.refreshStatus();
|
||||||
|
vscode.env.clipboard.writeText(JSON.stringify(this._model.migrationStatus, undefined, 2));
|
||||||
|
});
|
||||||
|
|
||||||
|
header.addItem(this._copyDatabaseMigrationDetails, {
|
||||||
|
flex: '0',
|
||||||
|
CSSStyles: {
|
||||||
|
'width': '100px'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this._refreshLoader = this._view.modelBuilder.loadingComponent().withProps({
|
this._refreshLoader = this._view.modelBuilder.loadingComponent().withProps({
|
||||||
loading: false,
|
loading: false,
|
||||||
height: '55px'
|
height: '55px'
|
||||||
@@ -344,6 +376,7 @@ export class MigrationCutoverDialog {
|
|||||||
header.addItem(this._refreshLoader, {
|
header.addItem(this._refreshLoader, {
|
||||||
flex: '0'
|
flex: '0'
|
||||||
});
|
});
|
||||||
|
|
||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,6 +396,7 @@ export class MigrationCutoverDialog {
|
|||||||
};
|
};
|
||||||
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 sourceDatabaseName = this._model._migration.migrationContext.properties.sourceDatabaseName;
|
||||||
const versionName = getSqlServerName(sqlServerInfo.serverMajorVersion!);
|
const versionName = getSqlServerName(sqlServerInfo.serverMajorVersion!);
|
||||||
const sqlServerVersion = versionName ? versionName : sqlServerInfo.serverVersion;
|
const sqlServerVersion = versionName ? versionName : sqlServerInfo.serverVersion;
|
||||||
const targetServerName = this._model._migration.targetManagedInstance.name;
|
const targetServerName = this._model._migration.targetManagedInstance.name;
|
||||||
@@ -402,6 +436,7 @@ export class MigrationCutoverDialog {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._sourceDatabase.value = sourceDatabaseName;
|
||||||
this._serverName.value = sqlServerName;
|
this._serverName.value = sqlServerName;
|
||||||
this._serverVersion.value = `${sqlServerVersion}
|
this._serverVersion.value = `${sqlServerVersion}
|
||||||
${sqlServerInfo.serverVersion}`;
|
${sqlServerInfo.serverVersion}`;
|
||||||
@@ -414,7 +449,7 @@ export class MigrationCutoverDialog {
|
|||||||
|
|
||||||
this._lastAppliedLSN.value = lastAppliedSSN!;
|
this._lastAppliedLSN.value = lastAppliedSSN!;
|
||||||
this._lastAppliedBackupFile.value = this._model.migrationStatus.properties.migrationStatusDetails?.lastRestoredFilename;
|
this._lastAppliedBackupFile.value = this._model.migrationStatus.properties.migrationStatusDetails?.lastRestoredFilename;
|
||||||
this._lastAppliedBackupTakenOn.value = new Date(lastAppliedBackupFileTakenOn!).toLocaleString();
|
this._lastAppliedBackupTakenOn.value = lastAppliedBackupFileTakenOn! ? new Date(lastAppliedBackupFileTakenOn).toLocaleString() : '';
|
||||||
|
|
||||||
this._fileCount.value = loc.ACTIVE_BACKUP_FILES_ITEMS(tableData.length);
|
this._fileCount.value = loc.ACTIVE_BACKUP_FILES_ITEMS(tableData.length);
|
||||||
|
|
||||||
|
|||||||
@@ -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, getSqlMigrationServices, getSubscriptions, SqlMigrationService, SqlManagedInstance, startDatabaseMigration, StartDatabaseMigrationRequest, StorageAccount, getAvailableSqlVMs, SqlVMServer } from '../api/azure';
|
import { getAvailableManagedInstanceProducts, getAvailableStorageAccounts, getBlobContainers, getFileShares, getSqlMigrationServices, getSubscriptions, SqlMigrationService, SqlManagedInstance, startDatabaseMigration, StartDatabaseMigrationRequest, StorageAccount, getAvailableSqlVMs, SqlVMServer, getLocations, getResourceGroups } from '../api/azure';
|
||||||
import { SKURecommendations } from './externalContract';
|
import { SKURecommendations } from './externalContract';
|
||||||
import * as constants from '../constants/strings';
|
import * as constants from '../constants/strings';
|
||||||
import { MigrationLocalStorage } from './migrationLocalStorage';
|
import { MigrationLocalStorage } from './migrationLocalStorage';
|
||||||
@@ -34,8 +34,9 @@ export enum State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum MigrationTargetType {
|
export enum MigrationTargetType {
|
||||||
SQLVM = 'sqlvm',
|
SQLVM = 'AzureSqlVirtualMachine',
|
||||||
SQLMI = 'sqlmi'
|
SQLMI = 'AzureSqlManagedInstance',
|
||||||
|
SQLDB = 'AzureSqlDatabase'
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum MigrationSourceAuthenticationType {
|
export enum MigrationSourceAuthenticationType {
|
||||||
@@ -63,7 +64,7 @@ export interface NetworkShare {
|
|||||||
export interface DatabaseBackupModel {
|
export interface DatabaseBackupModel {
|
||||||
migrationCutover: MigrationCutover;
|
migrationCutover: MigrationCutover;
|
||||||
networkContainerType: NetworkContainerType;
|
networkContainerType: NetworkContainerType;
|
||||||
networkShareLocations: string[];
|
networkShareLocation: string;
|
||||||
windowsUser: string;
|
windowsUser: string;
|
||||||
password: string;
|
password: string;
|
||||||
subscription: azureResource.AzureResourceSubscription;
|
subscription: azureResource.AzureResourceSubscription;
|
||||||
@@ -100,6 +101,10 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
public _subscriptions!: azureResource.AzureResourceSubscription[];
|
public _subscriptions!: azureResource.AzureResourceSubscription[];
|
||||||
|
|
||||||
public _targetSubscription!: azureResource.AzureResourceSubscription;
|
public _targetSubscription!: azureResource.AzureResourceSubscription;
|
||||||
|
public _locations!: azureResource.AzureLocation[];
|
||||||
|
public _location!: azureResource.AzureLocation;
|
||||||
|
public _resourceGroups!: azureResource.AzureResourceResourceGroup[];
|
||||||
|
public _resourceGroup!: azureResource.AzureResourceResourceGroup;
|
||||||
public _targetManagedInstances!: SqlManagedInstance[];
|
public _targetManagedInstances!: SqlManagedInstance[];
|
||||||
public _targetSqlVirtualMachines!: SqlVMServer[];
|
public _targetSqlVirtualMachines!: SqlVMServer[];
|
||||||
public _targetServerInstance!: SqlManagedInstance;
|
public _targetServerInstance!: SqlManagedInstance;
|
||||||
@@ -173,11 +178,13 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
});
|
});
|
||||||
|
|
||||||
assessmentResults?.items.forEach((item) => {
|
assessmentResults?.items.forEach((item) => {
|
||||||
const dbIndex = serverDatabases.indexOf(item.databaseName);
|
if (item.appliesToMigrationTargetPlatform === MigrationTargetType.SQLMI) {
|
||||||
if (dbIndex === -1) {
|
const dbIndex = serverDatabases.indexOf(item.databaseName);
|
||||||
serverLevelAssessments.push(item);
|
if (dbIndex === -1) {
|
||||||
} else {
|
serverLevelAssessments.push(item);
|
||||||
databaseLevelAssessments[dbIndex].issues.push(item);
|
} else {
|
||||||
|
databaseLevelAssessments[dbIndex].issues.push(item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -313,10 +320,88 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
return this._subscriptions[index];
|
return this._subscriptions[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getManagedInstanceValues(subscription: azureResource.AzureResourceSubscription): Promise<azdata.CategoryValue[]> {
|
public async getAzureLocationDropdownValues(subscription: azureResource.AzureResourceSubscription): Promise<azdata.CategoryValue[]> {
|
||||||
|
let locationValues: azdata.CategoryValue[] = [];
|
||||||
|
try {
|
||||||
|
this._locations = await getLocations(this._azureAccount, subscription);
|
||||||
|
this._locations.forEach((loc) => {
|
||||||
|
locationValues.push({
|
||||||
|
name: loc.name,
|
||||||
|
displayName: loc.displayName
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (locationValues.length === 0) {
|
||||||
|
locationValues = [
|
||||||
|
{
|
||||||
|
displayName: constants.INVALID_LOCATION_ERROR,
|
||||||
|
name: ''
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
locationValues = [
|
||||||
|
{
|
||||||
|
displayName: constants.INVALID_LOCATION_ERROR,
|
||||||
|
name: ''
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return locationValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLocation(index: number): azureResource.AzureLocation {
|
||||||
|
return this._locations[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getAzureResourceGroupDropdownValues(subscription: azureResource.AzureResourceSubscription): Promise<azdata.CategoryValue[]> {
|
||||||
|
let resourceGroupValues: azdata.CategoryValue[] = [];
|
||||||
|
try {
|
||||||
|
this._resourceGroups = await getResourceGroups(this._azureAccount, subscription);
|
||||||
|
this._resourceGroups.forEach((rg) => {
|
||||||
|
resourceGroupValues.push({
|
||||||
|
name: rg.id,
|
||||||
|
displayName: rg.name
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (resourceGroupValues.length === 0) {
|
||||||
|
resourceGroupValues = [
|
||||||
|
{
|
||||||
|
displayName: constants.RESOURCE_GROUP_NOT_FOUND,
|
||||||
|
name: ''
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
resourceGroupValues = [
|
||||||
|
{
|
||||||
|
displayName: constants.RESOURCE_GROUP_NOT_FOUND,
|
||||||
|
name: ''
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceGroupValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getAzureResourceGroup(index: number): azureResource.AzureResourceResourceGroup {
|
||||||
|
return this._resourceGroups[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async getManagedInstanceValues(subscription: azureResource.AzureResourceSubscription, location: azureResource.AzureLocation, resourceGroup: azureResource.AzureResourceResourceGroup): Promise<azdata.CategoryValue[]> {
|
||||||
let managedInstanceValues: azdata.CategoryValue[] = [];
|
let managedInstanceValues: azdata.CategoryValue[] = [];
|
||||||
try {
|
try {
|
||||||
this._targetManagedInstances = await getAvailableManagedInstanceProducts(this._azureAccount, subscription);
|
this._targetManagedInstances = (await getAvailableManagedInstanceProducts(this._azureAccount, subscription)).filter((mi) => {
|
||||||
|
if (mi.location === location.name && mi.resourceGroup?.toLocaleLowerCase() === resourceGroup.name.toLowerCase()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
this._targetManagedInstances.forEach((managedInstance) => {
|
this._targetManagedInstances.forEach((managedInstance) => {
|
||||||
managedInstanceValues.push({
|
managedInstanceValues.push({
|
||||||
name: managedInstance.id,
|
name: managedInstance.id,
|
||||||
@@ -348,13 +433,19 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
return this._targetManagedInstances[index];
|
return this._targetManagedInstances[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getSqlVirtualMachineValues(subscription: azureResource.AzureResourceSubscription): Promise<azdata.CategoryValue[]> {
|
public async getSqlVirtualMachineValues(subscription: azureResource.AzureResourceSubscription, location: azureResource.AzureLocation, resourceGroup: azureResource.AzureResourceResourceGroup): Promise<azdata.CategoryValue[]> {
|
||||||
let virtualMachineValues: azdata.CategoryValue[] = [];
|
let virtualMachineValues: azdata.CategoryValue[] = [];
|
||||||
try {
|
try {
|
||||||
this._targetSqlVirtualMachines = await getAvailableSqlVMs(this._azureAccount, subscription);
|
this._targetSqlVirtualMachines = (await getAvailableSqlVMs(this._azureAccount, subscription, resourceGroup)).filter((virtualMachine) => {
|
||||||
virtualMachineValues = this._targetSqlVirtualMachines.filter((virtualMachine) => {
|
if (virtualMachine.location === location.name) {
|
||||||
return virtualMachine.properties.sqlImageOffer.toLowerCase().includes('-ws'); //filtering out all non windows sql vms.
|
if (virtualMachine.properties.sqlImageOffer) {
|
||||||
}).map((virtualMachine) => {
|
return virtualMachine.properties.sqlImageOffer.toLowerCase().includes('-ws'); //filtering out all non windows sql vms.
|
||||||
|
}
|
||||||
|
return true; // Returning all VMs that don't have this property as we don't want to accidentally skip valid vms.
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
virtualMachineValues = this._targetSqlVirtualMachines.map((virtualMachine) => {
|
||||||
return {
|
return {
|
||||||
name: virtualMachine.id,
|
name: virtualMachine.id,
|
||||||
displayName: `${virtualMachine.name}`
|
displayName: `${virtualMachine.name}`
|
||||||
@@ -388,7 +479,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
public async getStorageAccountValues(subscription: azureResource.AzureResourceSubscription): Promise<azdata.CategoryValue[]> {
|
public async getStorageAccountValues(subscription: azureResource.AzureResourceSubscription): Promise<azdata.CategoryValue[]> {
|
||||||
let storageAccountValues: azdata.CategoryValue[] = [];
|
let storageAccountValues: azdata.CategoryValue[] = [];
|
||||||
try {
|
try {
|
||||||
this._storageAccounts = await getAvailableStorageAccounts(this._azureAccount, subscription);
|
this._storageAccounts = (await getAvailableStorageAccounts(this._azureAccount, subscription)).filter(sa => sa.location === this._targetServerInstance.location);
|
||||||
this._storageAccounts.forEach((storageAccount) => {
|
this._storageAccounts.forEach((storageAccount) => {
|
||||||
storageAccountValues.push({
|
storageAccountValues.push({
|
||||||
name: storageAccount.id,
|
name: storageAccount.id,
|
||||||
@@ -564,17 +655,16 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this._migrationDbs.forEach(async (db, index) => {
|
for (let i = 0; i < this._migrationDbs.length; i++) {
|
||||||
|
requestBody.properties.sourceDatabaseName = this._migrationDbs[i];
|
||||||
requestBody.properties.sourceDatabaseName = db;
|
|
||||||
try {
|
try {
|
||||||
requestBody.properties.backupConfiguration.sourceLocation.fileShare!.path = this._databaseBackup.networkShareLocations[index];
|
requestBody.properties.backupConfiguration.sourceLocation.fileShare!.path = this._databaseBackup.networkShareLocation;
|
||||||
const response = await startDatabaseMigration(
|
const response = await startDatabaseMigration(
|
||||||
this._azureAccount,
|
this._azureAccount,
|
||||||
this._targetSubscription,
|
this._targetSubscription,
|
||||||
this._sqlMigrationService?.properties.location!,
|
this._sqlMigrationService?.properties.location!,
|
||||||
this._targetServerInstance,
|
this._targetServerInstance,
|
||||||
this._targetDatabaseNames[index],
|
this._targetDatabaseNames[i],
|
||||||
requestBody
|
requestBody
|
||||||
);
|
);
|
||||||
if (response.status === 201) {
|
if (response.status === 201) {
|
||||||
@@ -586,14 +676,13 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
this._targetSubscription,
|
this._targetSubscription,
|
||||||
this._sqlMigrationService
|
this._sqlMigrationService
|
||||||
);
|
);
|
||||||
vscode.window.showInformationMessage(localize("sql.migration.starting.migration.message", 'Starting migration for database {0} to {1} - {2}', db, this._targetServerInstance.name, this._targetDatabaseNames[index]));
|
vscode.window.showInformationMessage(localize("sql.migration.starting.migration.message", 'Starting migration for database {0} to {1} - {2}', this._migrationDbs[i], this._targetServerInstance.name, this._targetDatabaseNames[i]));
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
vscode.window.showInformationMessage(e);
|
vscode.window.showInformationMessage(e);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -332,12 +332,32 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
this.migrationStateModel._databaseBackup.storageAccount = this.migrationStateModel.getStorageAccount(value.index);
|
this.migrationStateModel._databaseBackup.storageAccount = this.migrationStateModel.getStorageAccount(value.index);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const networkLocationInputBoxLabel = this._view.modelBuilder.text().withProps({
|
||||||
|
value: constants.DATABASE_BACKUP_NETWORK_SHARE_LOCATION_LABEL,
|
||||||
|
requiredIndicator: true
|
||||||
|
}).component();
|
||||||
|
const networkLocationInputBox = this._view.modelBuilder.inputBox().withProps({
|
||||||
|
placeHolder: '\\\\Servername.domainname.com\\Backupfolder',
|
||||||
|
required: true,
|
||||||
|
validationErrorMessage: constants.INVALID_NETWORK_SHARE_LOCATION
|
||||||
|
}).withValidation((component) => {
|
||||||
|
if (this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE) {
|
||||||
|
if (component.value) {
|
||||||
|
if (!/(?<=\\\\)[^\\]*/.test(component.value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}).component();
|
||||||
|
networkLocationInputBox.onTextChanged((value) => {
|
||||||
|
this.validateFields();
|
||||||
|
this.migrationStateModel._databaseBackup.networkShareLocation = value;
|
||||||
|
});
|
||||||
|
|
||||||
const networkShareDatabaseConfigHeader = view.modelBuilder.text().withProps({
|
const networkShareDatabaseConfigHeader = view.modelBuilder.text().withProps({
|
||||||
value: constants.ENTER_NETWORK_SHARE_INFORMATION
|
value: constants.ENTER_NETWORK_SHARE_INFORMATION
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this._networkShareDatabaseConfigContainer = view.modelBuilder.flexContainer().withLayout({
|
this._networkShareDatabaseConfigContainer = view.modelBuilder.flexContainer().withLayout({
|
||||||
flexFlow: 'column'
|
flexFlow: 'column'
|
||||||
}).component();
|
}).component();
|
||||||
@@ -347,14 +367,16 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
[
|
[
|
||||||
azureAccountHelpText,
|
azureAccountHelpText,
|
||||||
networkShareHelpText,
|
networkShareHelpText,
|
||||||
subscriptionLabel,
|
networkLocationInputBoxLabel,
|
||||||
this._networkShareContainerSubscriptionDropdown,
|
networkLocationInputBox,
|
||||||
storageAccountLabel,
|
|
||||||
this._networkShareContainerStorageAccountDropdown,
|
|
||||||
windowsUserAccountLabel,
|
windowsUserAccountLabel,
|
||||||
this._windowsUserAccountText,
|
this._windowsUserAccountText,
|
||||||
passwordLabel,
|
passwordLabel,
|
||||||
this._passwordText,
|
this._passwordText,
|
||||||
|
subscriptionLabel,
|
||||||
|
this._networkShareContainerSubscriptionDropdown,
|
||||||
|
storageAccountLabel,
|
||||||
|
this._networkShareContainerStorageAccountDropdown,
|
||||||
networkShareDatabaseConfigHeader,
|
networkShareDatabaseConfigHeader,
|
||||||
this._networkShareDatabaseConfigContainer
|
this._networkShareDatabaseConfigContainer
|
||||||
]
|
]
|
||||||
@@ -374,7 +396,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
this._fileShareDropdowns = [];
|
this._fileShareDropdowns = [];
|
||||||
this._blobContainerDropdowns = [];
|
this._blobContainerDropdowns = [];
|
||||||
this.migrationStateModel._targetDatabaseNames = [];
|
this.migrationStateModel._targetDatabaseNames = [];
|
||||||
this.migrationStateModel._databaseBackup.networkShareLocations = [];
|
|
||||||
this.migrationStateModel._databaseBackup.fileShares = [];
|
this.migrationStateModel._databaseBackup.fileShares = [];
|
||||||
this.migrationStateModel._databaseBackup.blobContainers = [];
|
this.migrationStateModel._databaseBackup.blobContainers = [];
|
||||||
this._networkShareDatabaseConfigContainer.clearItems();
|
this._networkShareDatabaseConfigContainer.clearItems();
|
||||||
@@ -389,42 +410,16 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
requiredIndicator: true
|
requiredIndicator: true
|
||||||
}).component();
|
}).component();
|
||||||
const targetNameNetworkInputBox = this._view.modelBuilder.inputBox().withProps({
|
const targetNameNetworkInputBox = this._view.modelBuilder.inputBox().withProps({
|
||||||
required: true
|
required: true,
|
||||||
|
value: db
|
||||||
}).component();
|
}).component();
|
||||||
targetNameNetworkInputBox.onTextChanged((value) => {
|
targetNameNetworkInputBox.onTextChanged((value) => {
|
||||||
this.migrationStateModel._targetDatabaseNames[index] = value;
|
this.migrationStateModel._targetDatabaseNames[index] = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
const networkLocationInputBoxLabel = this._view.modelBuilder.text().withProps({
|
|
||||||
value: constants.TARGET_NETWORK_SHARE_LOCATION(db),
|
|
||||||
requiredIndicator: true
|
|
||||||
}).component();
|
|
||||||
const networkLocationInputBox = this._view.modelBuilder.inputBox().withProps({
|
|
||||||
placeHolder: '\\\\Servername.domainname.com\\Backupfolder',
|
|
||||||
required: true,
|
|
||||||
validationErrorMessage: constants.INVALID_NETWORK_SHARE_LOCATION
|
|
||||||
}).withValidation((component) => {
|
|
||||||
if (this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE) {
|
|
||||||
if (component.value) {
|
|
||||||
if (!/(?<=\\\\)[^\\]*/.test(component.value)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}).component();
|
|
||||||
networkLocationInputBox.onTextChanged((value) => {
|
|
||||||
this.validateFields();
|
|
||||||
this.migrationStateModel._databaseBackup.networkShareLocations[index] = value;
|
|
||||||
});
|
|
||||||
this.migrationStateModel._databaseBackup.networkShareLocations.push(undefined!);
|
|
||||||
this._networkShareLocations.push(networkLocationInputBox);
|
|
||||||
this._networkShareDatabaseConfigContainer.addItems(
|
this._networkShareDatabaseConfigContainer.addItems(
|
||||||
[
|
[
|
||||||
targetNameNetworkInputBoxLabel,
|
targetNameNetworkInputBoxLabel,
|
||||||
targetNameNetworkInputBox,
|
targetNameNetworkInputBox
|
||||||
networkLocationInputBoxLabel,
|
|
||||||
networkLocationInputBox
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
private _chooseTargetComponent!: azdata.DivContainer;
|
private _chooseTargetComponent!: azdata.DivContainer;
|
||||||
private _azureSubscriptionText!: azdata.TextComponent;
|
private _azureSubscriptionText!: azdata.TextComponent;
|
||||||
private _managedInstanceSubscriptionDropdown!: azdata.DropDownComponent;
|
private _managedInstanceSubscriptionDropdown!: azdata.DropDownComponent;
|
||||||
|
private _azureLocationDropdown!: azdata.DropDownComponent;
|
||||||
|
private _azureResourceGroupDropdown!: azdata.DropDownComponent;
|
||||||
private _resourceDropdownLabel!: azdata.TextComponent;
|
private _resourceDropdownLabel!: azdata.TextComponent;
|
||||||
private _resourceDropdown!: azdata.DropDownComponent;
|
private _resourceDropdown!: azdata.DropDownComponent;
|
||||||
private _rbg!: azdata.RadioCardGroupComponent;
|
private _rbg!: azdata.RadioCardGroupComponent;
|
||||||
@@ -66,6 +68,35 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
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._sqlMigrationService = undefined!;
|
this.migrationStateModel._sqlMigrationService = undefined!;
|
||||||
|
this.populateLocationAndResourceGroupDropdown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this._resourceDropdownLabel = view.modelBuilder.text().withProps({
|
||||||
|
value: constants.MANAGED_INSTANCE
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
const azureLocationLabel = view.modelBuilder.text().withProps({
|
||||||
|
value: constants.LOCATION
|
||||||
|
}).component();
|
||||||
|
this._azureLocationDropdown = view.modelBuilder.dropDown().component();
|
||||||
|
this._azureLocationDropdown.onValueChanged((e) => {
|
||||||
|
if (e.selected) {
|
||||||
|
this.migrationStateModel._location = this.migrationStateModel.getLocation(e.index);
|
||||||
|
this.populateResourceInstanceDropdown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this._resourceDropdownLabel = view.modelBuilder.text().withProps({
|
||||||
|
value: constants.MANAGED_INSTANCE
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
|
||||||
|
const azureResourceGroupLabel = view.modelBuilder.text().withProps({
|
||||||
|
value: constants.RESOURCE_GROUP
|
||||||
|
}).component();
|
||||||
|
this._azureResourceGroupDropdown = view.modelBuilder.dropDown().component();
|
||||||
|
this._azureResourceGroupDropdown.onValueChanged((e) => {
|
||||||
|
if (e.selected) {
|
||||||
|
this.migrationStateModel._resourceGroup = this.migrationStateModel.getAzureResourceGroup(e.index);
|
||||||
this.populateResourceInstanceDropdown();
|
this.populateResourceInstanceDropdown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -91,6 +122,10 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
[
|
[
|
||||||
managedInstanceSubscriptionDropdownLabel,
|
managedInstanceSubscriptionDropdownLabel,
|
||||||
this._managedInstanceSubscriptionDropdown,
|
this._managedInstanceSubscriptionDropdown,
|
||||||
|
azureLocationLabel,
|
||||||
|
this._azureLocationDropdown,
|
||||||
|
azureResourceGroupLabel,
|
||||||
|
this._azureResourceGroupDropdown,
|
||||||
this._resourceDropdownLabel,
|
this._resourceDropdownLabel,
|
||||||
this._resourceDropdown
|
this._resourceDropdown
|
||||||
]
|
]
|
||||||
@@ -284,16 +319,30 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async populateLocationAndResourceGroupDropdown(): Promise<void> {
|
||||||
|
this._azureResourceGroupDropdown.loading = true;
|
||||||
|
this._azureLocationDropdown.loading = true;
|
||||||
|
try {
|
||||||
|
this._azureResourceGroupDropdown.values = await this.migrationStateModel.getAzureResourceGroupDropdownValues(this.migrationStateModel._targetSubscription);
|
||||||
|
this._azureLocationDropdown.values = await this.migrationStateModel.getAzureLocationDropdownValues(this.migrationStateModel._targetSubscription);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
} finally {
|
||||||
|
this._azureResourceGroupDropdown.loading = false;
|
||||||
|
this._azureLocationDropdown.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async populateResourceInstanceDropdown(): Promise<void> {
|
private async populateResourceInstanceDropdown(): Promise<void> {
|
||||||
this._resourceDropdown.loading = true;
|
this._resourceDropdown.loading = true;
|
||||||
try {
|
try {
|
||||||
if (this._rbg.selectedCardId === MigrationTargetType.SQLVM) {
|
if (this._rbg.selectedCardId === MigrationTargetType.SQLVM) {
|
||||||
this._resourceDropdownLabel.value = constants.AZURE_SQL_DATABASE_VIRTUAL_MACHINE;
|
this._resourceDropdownLabel.value = constants.AZURE_SQL_DATABASE_VIRTUAL_MACHINE;
|
||||||
this._resourceDropdown.values = await this.migrationStateModel.getSqlVirtualMachineValues(this.migrationStateModel._targetSubscription);
|
this._resourceDropdown.values = await this.migrationStateModel.getSqlVirtualMachineValues(this.migrationStateModel._targetSubscription, this.migrationStateModel._location, this.migrationStateModel._resourceGroup);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this._resourceDropdownLabel.value = constants.AZURE_SQL_DATABASE_MANAGED_INSTANCE;
|
this._resourceDropdownLabel.value = constants.AZURE_SQL_DATABASE_MANAGED_INSTANCE;
|
||||||
this._resourceDropdown.values = await this.migrationStateModel.getManagedInstanceValues(this.migrationStateModel._targetSubscription);
|
this._resourceDropdown.values = await this.migrationStateModel.getManagedInstanceValues(this.migrationStateModel._targetSubscription, this.migrationStateModel._location, this.migrationStateModel._resourceGroup);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
@@ -322,6 +371,13 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
if ((<azdata.CategoryValue>this._managedInstanceSubscriptionDropdown.value).displayName === constants.NO_SUBSCRIPTIONS_FOUND) {
|
if ((<azdata.CategoryValue>this._managedInstanceSubscriptionDropdown.value).displayName === constants.NO_SUBSCRIPTIONS_FOUND) {
|
||||||
errors.push(constants.INVALID_SUBSCRIPTION_ERROR);
|
errors.push(constants.INVALID_SUBSCRIPTION_ERROR);
|
||||||
}
|
}
|
||||||
|
if ((<azdata.CategoryValue>this._azureLocationDropdown.value).displayName === constants.NO_LOCATION_FOUND) {
|
||||||
|
errors.push(constants.INVALID_LOCATION_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((<azdata.CategoryValue>this._managedInstanceSubscriptionDropdown.value).displayName === constants.RESOURCE_GROUP_NOT_FOUND) {
|
||||||
|
errors.push(constants.INVALID_RESOURCE_GROUP_ERROR);
|
||||||
|
}
|
||||||
const resourceDropdownValue = (<azdata.CategoryValue>this._resourceDropdown.value).displayName;
|
const resourceDropdownValue = (<azdata.CategoryValue>this._resourceDropdown.value).displayName;
|
||||||
if (resourceDropdownValue === constants.NO_MANAGED_INSTANCE_FOUND) {
|
if (resourceDropdownValue === constants.NO_MANAGED_INSTANCE_FOUND) {
|
||||||
errors.push(constants.NO_MANAGED_INSTANCE_FOUND);
|
errors.push(constants.NO_MANAGED_INSTANCE_FOUND);
|
||||||
@@ -377,8 +433,8 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
if (this.migrationStateModel._assessmentResults) {
|
if (this.migrationStateModel._assessmentResults) {
|
||||||
const dbCount = this.migrationStateModel._assessmentResults.databaseAssessments.length;
|
const dbCount = this.migrationStateModel._assessmentResults.databaseAssessments.length;
|
||||||
|
|
||||||
const dbWithIssuesCount = this.migrationStateModel._assessmentResults.databaseAssessments.filter(db => db.issues.length > 0).length;
|
const dbWithoutIssuesCount = this.migrationStateModel._assessmentResults.databaseAssessments.filter(db => db.issues.length === 0).length;
|
||||||
const miCardText = `${dbWithIssuesCount} out of ${dbCount} databases can be migrated (${this.migrationStateModel._miDbs.length} selected)`;
|
const miCardText = `${dbWithoutIssuesCount} out of ${dbCount} databases can be migrated (${this.migrationStateModel._miDbs.length} selected)`;
|
||||||
this._rbg.cards[0].descriptions[1].textValue = miCardText;
|
this._rbg.cards[0].descriptions[1].textValue = miCardText;
|
||||||
|
|
||||||
const vmCardText = `${dbCount} out of ${dbCount} databases can be migrated (${this.migrationStateModel._vmDbs.length} selected)`;
|
const vmCardText = `${dbCount} out of ${dbCount} databases can be migrated (${this.migrationStateModel._vmDbs.length} selected)`;
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ export class SummaryPage extends MigrationWizardPage {
|
|||||||
flexContainer.addItems(
|
flexContainer.addItems(
|
||||||
[
|
[
|
||||||
createInformationRow(this._view, constants.TYPE, constants.NETWORK_SHARE),
|
createInformationRow(this._view, constants.TYPE, constants.NETWORK_SHARE),
|
||||||
|
createInformationRow(this._view, constants.DATABASE_BACKUP_NETWORK_SHARE_LOCATION_LABEL, this.migrationStateModel._databaseBackup.networkShareLocation),
|
||||||
createInformationRow(this._view, constants.USER_ACCOUNT, this.migrationStateModel._databaseBackup.windowsUser),
|
createInformationRow(this._view, constants.USER_ACCOUNT, this.migrationStateModel._databaseBackup.windowsUser),
|
||||||
createInformationRow(this._view, constants.SUMMARY_AZURE_STORAGE_SUBSCRIPTION, this.migrationStateModel._databaseBackup.subscription.name),
|
createInformationRow(this._view, constants.SUMMARY_AZURE_STORAGE_SUBSCRIPTION, this.migrationStateModel._databaseBackup.subscription.name),
|
||||||
createInformationRow(this._view, constants.SUMMARY_AZURE_STORAGE, this.migrationStateModel._databaseBackup.storageAccount.name),
|
createInformationRow(this._view, constants.SUMMARY_AZURE_STORAGE, this.migrationStateModel._databaseBackup.storageAccount.name),
|
||||||
@@ -80,7 +81,6 @@ export class SummaryPage extends MigrationWizardPage {
|
|||||||
);
|
);
|
||||||
this.migrationStateModel._migrationDbs.forEach((db, index) => {
|
this.migrationStateModel._migrationDbs.forEach((db, index) => {
|
||||||
flexContainer.addItem(createInformationRow(this._view, constants.TARGET_NAME_FOR_DATABASE(db), this.migrationStateModel._targetDatabaseNames[index]));
|
flexContainer.addItem(createInformationRow(this._view, constants.TARGET_NAME_FOR_DATABASE(db), this.migrationStateModel._targetDatabaseNames[index]));
|
||||||
flexContainer.addItem(createInformationRow(this._view, constants.TARGET_NETWORK_SHARE_LOCATION(db), this.migrationStateModel._databaseBackup.networkShareLocations[index]));
|
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case NetworkContainerType.FILE_SHARE:
|
case NetworkContainerType.FILE_SHARE:
|
||||||
|
|||||||
@@ -1,111 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import * as azdata from 'azdata';
|
|
||||||
import { MigrationWizardPage } from '../models/migrationWizardPage';
|
|
||||||
import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
|
|
||||||
import * as constants from '../constants/strings';
|
|
||||||
import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
|
|
||||||
|
|
||||||
export class TempTargetSelectionPage extends MigrationWizardPage {
|
|
||||||
|
|
||||||
private _managedInstanceSubscriptionDropdown!: azdata.DropDownComponent;
|
|
||||||
private _managedInstanceDropdown!: azdata.DropDownComponent;
|
|
||||||
|
|
||||||
constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) {
|
|
||||||
super(wizard, azdata.window.createWizardPage(constants.TARGET_SELECTION_PAGE_TITLE), migrationStateModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async registerContent(view: azdata.ModelView): Promise<void> {
|
|
||||||
|
|
||||||
const managedInstanceSubscriptionDropdownLabel = view.modelBuilder.text()
|
|
||||||
.withProps({
|
|
||||||
value: constants.SUBSCRIPTION
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
this._managedInstanceSubscriptionDropdown = view.modelBuilder.dropDown()
|
|
||||||
.withProps({
|
|
||||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
|
||||||
}).component();
|
|
||||||
this._managedInstanceSubscriptionDropdown.onValueChanged((e) => {
|
|
||||||
if (e.selected) {
|
|
||||||
this.migrationStateModel._targetSubscription = this.migrationStateModel.getSubscription(e.index);
|
|
||||||
this.migrationStateModel._targetServerInstance = undefined!;
|
|
||||||
this.migrationStateModel._sqlMigrationService = undefined!;
|
|
||||||
this.populateManagedInstanceDropdown();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const managedInstanceDropdownLabel = view.modelBuilder.text().withProps({
|
|
||||||
value: constants.MANAGED_INSTANCE
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
this._managedInstanceDropdown = view.modelBuilder.dropDown()
|
|
||||||
.withProps({
|
|
||||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
|
||||||
}).component();
|
|
||||||
this._managedInstanceDropdown.onValueChanged((e) => {
|
|
||||||
if (e.selected) {
|
|
||||||
this.migrationStateModel._sqlMigrationServices = undefined!;
|
|
||||||
this.migrationStateModel._targetServerInstance = this.migrationStateModel.getManagedInstance(e.index);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const targetContainer = view.modelBuilder.flexContainer().withItems(
|
|
||||||
[
|
|
||||||
managedInstanceSubscriptionDropdownLabel,
|
|
||||||
this._managedInstanceSubscriptionDropdown,
|
|
||||||
managedInstanceDropdownLabel,
|
|
||||||
this._managedInstanceDropdown
|
|
||||||
]
|
|
||||||
).withLayout({
|
|
||||||
flexFlow: 'column'
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
const form = view.modelBuilder.formContainer()
|
|
||||||
.withFormItems(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
component: targetContainer
|
|
||||||
}
|
|
||||||
]
|
|
||||||
);
|
|
||||||
await view.initializeModel(form.component());
|
|
||||||
}
|
|
||||||
public async onPageEnter(): Promise<void> {
|
|
||||||
this.populateSubscriptionDropdown();
|
|
||||||
}
|
|
||||||
public async onPageLeave(): Promise<void> {
|
|
||||||
}
|
|
||||||
protected async handleStateChange(e: StateChangeEvent): Promise<void> {
|
|
||||||
}
|
|
||||||
|
|
||||||
private async populateSubscriptionDropdown(): Promise<void> {
|
|
||||||
if (!this.migrationStateModel._targetSubscription) {
|
|
||||||
this._managedInstanceSubscriptionDropdown.loading = true;
|
|
||||||
this._managedInstanceDropdown.loading = true;
|
|
||||||
try {
|
|
||||||
this._managedInstanceSubscriptionDropdown.values = await this.migrationStateModel.getSubscriptionsDropdownValues();
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
} finally {
|
|
||||||
this._managedInstanceSubscriptionDropdown.loading = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async populateManagedInstanceDropdown(): Promise<void> {
|
|
||||||
if (!this.migrationStateModel._targetServerInstance) {
|
|
||||||
this._managedInstanceDropdown.loading = true;
|
|
||||||
try {
|
|
||||||
this._managedInstanceDropdown.values = await this.migrationStateModel.getManagedInstanceValues(this.migrationStateModel._targetSubscription);
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
} finally {
|
|
||||||
this._managedInstanceDropdown.loading = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user