mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-18 09:35:39 -05:00
Add SQL DB offline migration wizard experience (#20403)
* sql db wizard with target selection * add database table selection * add sqldb to service and IR page * Code complete * navigation bug fixes * fix target db selection * improve sqldb error and status reporting * fix error count bug * remove table status inference * address review feedback * update resource strings and content * fix migraton status string, use localized value * fix ux navigation issues * fix back/fwd w/o changes from changing data
This commit is contained in:
@@ -7,13 +7,14 @@ import * as azdata from 'azdata';
|
||||
import * as azurecore from 'azurecore';
|
||||
import * as vscode from 'vscode';
|
||||
import * as mssql from 'mssql';
|
||||
import { SqlMigrationService, SqlManagedInstance, startDatabaseMigration, StartDatabaseMigrationRequest, StorageAccount, SqlVMServer, getLocationDisplayName, getSqlManagedInstanceDatabases } from '../api/azure';
|
||||
import { SqlMigrationService, SqlManagedInstance, startDatabaseMigration, StartDatabaseMigrationRequest, StorageAccount, SqlVMServer, getLocationDisplayName, getSqlManagedInstanceDatabases, AzureSqlDatabaseServer } from '../api/azure';
|
||||
import * as constants from '../constants/strings';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { sendSqlMigrationActionEvent, TelemetryAction, TelemetryViews, logError } from '../telemtery';
|
||||
import { hashString, deepClone } from '../api/utils';
|
||||
import { SKURecommendationPage } from '../wizard/skuRecommendationPage';
|
||||
import { excludeDatabses, TargetDatabaseInfo } from '../api/sqlUtils';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export enum State {
|
||||
@@ -136,7 +137,7 @@ export interface SavedInfo {
|
||||
subscription: azurecore.azureResource.AzureResourceSubscription | null;
|
||||
location: azurecore.azureResource.AzureLocation | null;
|
||||
resourceGroup: azurecore.azureResource.AzureResourceResourceGroup | null;
|
||||
targetServerInstance: azurecore.azureResource.AzureSqlManagedInstance | SqlVMServer | null;
|
||||
targetServerInstance: azurecore.azureResource.AzureSqlManagedInstance | SqlVMServer | AzureSqlDatabaseServer | null;
|
||||
migrationMode: MigrationMode | null;
|
||||
networkContainerType: NetworkContainerType | null;
|
||||
networkShares: NetworkShare[];
|
||||
@@ -176,7 +177,8 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
public _resourceGroup!: azurecore.azureResource.AzureResourceResourceGroup;
|
||||
public _targetManagedInstances!: SqlManagedInstance[];
|
||||
public _targetSqlVirtualMachines!: SqlVMServer[];
|
||||
public _targetServerInstance!: SqlManagedInstance | SqlVMServer;
|
||||
public _targetSqlDatabaseServers!: AzureSqlDatabaseServer[];
|
||||
public _targetServerInstance!: SqlManagedInstance | SqlVMServer | AzureSqlDatabaseServer;
|
||||
public _databaseBackup!: DatabaseBackupModel;
|
||||
public _storageAccounts!: StorageAccount[];
|
||||
public _fileShares!: azurecore.azureResource.FileShare[];
|
||||
@@ -185,15 +187,15 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
public _sourceDatabaseNames!: string[];
|
||||
public _targetDatabaseNames!: string[];
|
||||
|
||||
public _targetUserName!: string;
|
||||
public _targetPassword!: string;
|
||||
public _sourceTargetMapping: Map<string, TargetDatabaseInfo | undefined> = new Map();
|
||||
|
||||
public _sqlMigrationServiceResourceGroup!: azurecore.azureResource.AzureResourceResourceGroup;
|
||||
public _sqlMigrationService!: SqlMigrationService | undefined;
|
||||
public _sqlMigrationServices!: SqlMigrationService[];
|
||||
public _nodeNames!: string[];
|
||||
|
||||
private _stateChangeEventEmitter = new vscode.EventEmitter<StateChangeEvent>();
|
||||
private _currentState: State;
|
||||
private _gatheringInformationError: string | undefined;
|
||||
|
||||
public _databasesForAssessment!: string[];
|
||||
public _assessmentResults!: ServerAssessment;
|
||||
public _assessedDatabaseList!: string[];
|
||||
@@ -204,8 +206,10 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
|
||||
public _databasesForMigration: string[] = [];
|
||||
public _didUpdateDatabasesForMigration: boolean = false;
|
||||
public _didDatabaseMappingChange: boolean = false;
|
||||
public _vmDbs: string[] = [];
|
||||
public _miDbs: string[] = [];
|
||||
public _sqldbDbs: string[] = [];
|
||||
public _targetType!: MigrationTargetType;
|
||||
|
||||
public _skuRecommendationResults!: SkuRecommendation;
|
||||
@@ -213,10 +217,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
private _skuRecommendationApiResponse!: mssql.SkuRecommendationResult;
|
||||
public _skuRecommendationReportFilePaths: string[];
|
||||
public _skuRecommendationPerformanceLocation!: string;
|
||||
private _skuRecommendationRecommendedDatabaseList!: string[];
|
||||
private _startPerfDataCollectionApiResponse!: mssql.StartPerfDataCollectionResult;
|
||||
private _stopPerfDataCollectionApiResponse!: mssql.StopPerfDataCollectionResult;
|
||||
private _refreshPerfDataCollectionApiResponse!: mssql.RefreshPerfDataCollectionResult;
|
||||
|
||||
public _perfDataCollectionStartDate!: Date | undefined;
|
||||
public _perfDataCollectionStopDate!: Date | undefined;
|
||||
public _perfDataCollectionLastRefreshedDate!: Date;
|
||||
@@ -232,9 +233,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
public readonly _recommendationTargetPlatforms = [MigrationTargetType.SQLDB, MigrationTargetType.SQLMI, MigrationTargetType.SQLVM];
|
||||
|
||||
public refreshPerfDataCollectionFrequency = this._performanceDataQueryIntervalInSeconds * 1000;
|
||||
private _autoRefreshPerfDataCollectionHandle!: NodeJS.Timeout;
|
||||
public refreshGetSkuRecommendationFrequency = constants.TIME_IN_MINUTES(10);
|
||||
private _autoRefreshGetSkuRecommendationHandle!: NodeJS.Timeout;
|
||||
|
||||
public _skuScalingFactor!: number;
|
||||
public _skuTargetPercentile!: number;
|
||||
@@ -246,15 +245,18 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
public savedInfo!: SavedInfo;
|
||||
public closedPage!: number;
|
||||
public _sessionId: string = uuidv4();
|
||||
|
||||
public excludeDbs: string[] = [
|
||||
'master',
|
||||
'tempdb',
|
||||
'msdb',
|
||||
'model'
|
||||
];
|
||||
public serverName!: string;
|
||||
|
||||
private _stateChangeEventEmitter = new vscode.EventEmitter<StateChangeEvent>();
|
||||
private _currentState: State;
|
||||
private _gatheringInformationError: string | undefined;
|
||||
private _skuRecommendationRecommendedDatabaseList!: string[];
|
||||
private _startPerfDataCollectionApiResponse!: mssql.StartPerfDataCollectionResult;
|
||||
private _stopPerfDataCollectionApiResponse!: mssql.StopPerfDataCollectionResult;
|
||||
private _refreshPerfDataCollectionApiResponse!: mssql.RefreshPerfDataCollectionResult;
|
||||
private _autoRefreshPerfDataCollectionHandle!: NodeJS.Timeout;
|
||||
private _autoRefreshGetSkuRecommendationHandle!: NodeJS.Timeout;
|
||||
|
||||
constructor(
|
||||
public extensionContext: vscode.ExtensionContext,
|
||||
private readonly _sourceConnectionId: string,
|
||||
@@ -288,8 +290,8 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
this._stateChangeEventEmitter.fire({ oldState, newState: this.currentState });
|
||||
}
|
||||
public async getDatabases(): Promise<string[]> {
|
||||
let temp = await azdata.connection.listDatabases(this.sourceConnectionId);
|
||||
let finalResult = temp.filter((name) => !this.excludeDbs.includes(name));
|
||||
const temp = await azdata.connection.listDatabases(this.sourceConnectionId);
|
||||
const finalResult = temp.filter((name) => !excludeDatabses.includes(name));
|
||||
return finalResult;
|
||||
}
|
||||
public hasRecommendedDatabaseListChanged(): boolean {
|
||||
@@ -304,7 +306,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
}));
|
||||
}
|
||||
|
||||
public async getDatabaseAssessments(targetType: MigrationTargetType): Promise<ServerAssessment> {
|
||||
public async getDatabaseAssessments(targetType: MigrationTargetType[]): Promise<ServerAssessment> {
|
||||
const ownerUri = await azdata.connection.getUriForConnection(this.sourceConnectionId);
|
||||
try {
|
||||
const response = (await this.migrationService.getAssessments(ownerUri, this._databasesForAssessment))!;
|
||||
@@ -313,11 +315,14 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
|
||||
if (response?.assessmentResult) {
|
||||
response.assessmentResult.items = response.assessmentResult.items?.filter(
|
||||
issue => issue.appliesToMigrationTargetPlatform === targetType);
|
||||
issue => targetType.includes(
|
||||
<MigrationTargetType>issue.appliesToMigrationTargetPlatform));
|
||||
|
||||
response.assessmentResult.databases?.forEach(
|
||||
database => database.items = database.items?.filter(
|
||||
issue => issue.appliesToMigrationTargetPlatform === targetType));
|
||||
issue => targetType.includes(
|
||||
<MigrationTargetType>issue.appliesToMigrationTargetPlatform)));
|
||||
|
||||
this._assessmentResults = {
|
||||
issues: this._assessmentApiResponse?.assessmentResult?.items || [],
|
||||
databaseAssessments: this._assessmentApiResponse?.assessmentResult?.databases?.map(d => {
|
||||
@@ -447,8 +452,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
'sessionId': this._sessionId,
|
||||
'recommendedSku': JSON.stringify(resultItem?.targetSku)
|
||||
},
|
||||
{}
|
||||
);
|
||||
{});
|
||||
});
|
||||
|
||||
this._skuRecommendationResults?.recommendations?.sqlVmRecommendationResults?.forEach(resultItem => {
|
||||
@@ -460,8 +464,19 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
'sessionId': this._sessionId,
|
||||
'recommendedSku': JSON.stringify(resultItem?.targetSku)
|
||||
},
|
||||
{}
|
||||
);
|
||||
{});
|
||||
});
|
||||
|
||||
this._skuRecommendationResults?.recommendations?.sqlDbRecommendationResults?.forEach(resultItem => {
|
||||
// Send telemetry for recommended SQLDB SKU
|
||||
sendSqlMigrationActionEvent(
|
||||
TelemetryViews.SkuRecommendationWizard,
|
||||
TelemetryAction.GetSqlDbSkuRecommendation,
|
||||
{
|
||||
'sessionId': this._sessionId,
|
||||
'recommendedSku': JSON.stringify(resultItem?.targetSku)
|
||||
},
|
||||
{});
|
||||
});
|
||||
|
||||
// Send Instance requirements used for calculating recommendations
|
||||
@@ -513,8 +528,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
'tempDBSizeInMB': this._skuRecommendationResults?.recommendations?.instanceRequirements?.tempDBSizeInMB,
|
||||
'aggregationTargetPercentile': this._skuRecommendationResults?.recommendations?.instanceRequirements?.aggregationTargetPercentile,
|
||||
'numberOfDataPointsAnalyzed': this._skuRecommendationResults?.recommendations?.instanceRequirements?.numberOfDataPointsAnalyzed,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
logError(TelemetryViews.SkuRecommendationWizard, 'GetSkuRecommendationTelemetryFailed', e);
|
||||
@@ -530,7 +544,12 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
try {
|
||||
if (!this.performanceCollectionInProgress()) {
|
||||
const ownerUri = await azdata.connection.getUriForConnection(this.sourceConnectionId);
|
||||
const response = await this.migrationService.startPerfDataCollection(ownerUri, dataFolder, perfQueryIntervalInSec, staticQueryIntervalInSec, numberOfIterations);
|
||||
const response = await this.migrationService.startPerfDataCollection(
|
||||
ownerUri,
|
||||
dataFolder,
|
||||
perfQueryIntervalInSec,
|
||||
staticQueryIntervalInSec,
|
||||
numberOfIterations);
|
||||
|
||||
this._startPerfDataCollectionApiResponse = response!;
|
||||
this._perfDataCollectionStartDate = this._startPerfDataCollectionApiResponse.dateTimeStarted;
|
||||
@@ -560,8 +579,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
'sessionId': this._sessionId,
|
||||
'timeDataCollectionStarted': this._perfDataCollectionStartDate?.toString() || ''
|
||||
},
|
||||
{}
|
||||
);
|
||||
{});
|
||||
|
||||
} catch (e) {
|
||||
logError(TelemetryViews.DataCollectionWizard, 'StartDataCollectionTelemetryFailed', e);
|
||||
@@ -574,13 +592,14 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
if (!this._autoRefreshPerfDataCollectionHandle) {
|
||||
clearInterval(this._autoRefreshPerfDataCollectionHandle);
|
||||
if (this.refreshPerfDataCollectionFrequency !== -1) {
|
||||
this._autoRefreshPerfDataCollectionHandle = setInterval(async function () {
|
||||
await classVariable.refreshPerfDataCollection();
|
||||
|
||||
if (await classVariable.isWaitingForFirstTimeRefresh()) {
|
||||
await page.refreshSkuRecommendationComponents(); // update timer
|
||||
}
|
||||
}, refreshIntervalInMs);
|
||||
this._autoRefreshPerfDataCollectionHandle = setInterval(
|
||||
async function () {
|
||||
await classVariable.refreshPerfDataCollection();
|
||||
if (await classVariable.isWaitingForFirstTimeRefresh()) {
|
||||
await page.refreshSkuRecommendationComponents(); // update timer
|
||||
}
|
||||
},
|
||||
refreshIntervalInMs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -588,9 +607,11 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
// start one-time timer to get SKU recommendation
|
||||
clearTimeout(this._autoRefreshGetSkuRecommendationHandle);
|
||||
if (this.refreshGetSkuRecommendationFrequency !== -1) {
|
||||
this._autoRefreshGetSkuRecommendationHandle = setTimeout(async function () {
|
||||
await page.refreshAzureRecommendation();
|
||||
}, this.refreshGetSkuRecommendationFrequency);
|
||||
this._autoRefreshGetSkuRecommendationHandle = setTimeout(
|
||||
async function () {
|
||||
await page.refreshAzureRecommendation();
|
||||
},
|
||||
this.refreshGetSkuRecommendationFrequency);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -612,7 +633,8 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
}
|
||||
|
||||
// Generate telemetry for stop data collection request
|
||||
this.generateStopDataCollectionTelemetry().catch(e => console.error(e));
|
||||
this.generateStopDataCollectionTelemetry()
|
||||
.catch(e => console.error(e));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -625,8 +647,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
'sessionId': this._sessionId,
|
||||
'timeDataCollectionStopped': this._perfDataCollectionStopDate?.toString() || ''
|
||||
},
|
||||
{}
|
||||
);
|
||||
{});
|
||||
|
||||
} catch (e) {
|
||||
logError(TelemetryViews.DataCollectionWizard, 'StopDataCollectionTelemetryFailed', e);
|
||||
@@ -643,7 +664,10 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
this._perfDataCollectionIsCollecting = this._refreshPerfDataCollectionApiResponse.isCollecting;
|
||||
|
||||
if (this._perfDataCollectionErrors?.length > 0) {
|
||||
void vscode.window.showInformationMessage(constants.PERF_DATA_COLLECTION_ERROR(this._assessmentApiResponse?.assessmentResult?.name, this._perfDataCollectionErrors));
|
||||
void vscode.window.showInformationMessage(
|
||||
constants.PERF_DATA_COLLECTION_ERROR(
|
||||
this._assessmentApiResponse?.assessmentResult?.name,
|
||||
this._perfDataCollectionErrors));
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
@@ -661,33 +685,24 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
}
|
||||
|
||||
public performanceCollectionNotStarted(): boolean {
|
||||
if (!this._perfDataCollectionStartDate
|
||||
&& !this._perfDataCollectionStopDate) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return !this._perfDataCollectionStartDate
|
||||
&& !this._perfDataCollectionStopDate;
|
||||
}
|
||||
|
||||
public performanceCollectionInProgress(): boolean {
|
||||
if (this._perfDataCollectionStartDate
|
||||
&& !this._perfDataCollectionStopDate) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return this._perfDataCollectionStartDate !== undefined
|
||||
&& this._perfDataCollectionStopDate === undefined;
|
||||
}
|
||||
|
||||
public performanceCollectionStopped(): boolean {
|
||||
if (this._perfDataCollectionStartDate
|
||||
&& this._perfDataCollectionStopDate) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return this._perfDataCollectionStartDate !== undefined
|
||||
&& this._perfDataCollectionStopDate !== undefined;
|
||||
}
|
||||
|
||||
private async generateAssessmentTelemetry(): Promise<void> {
|
||||
try {
|
||||
|
||||
let serverIssues = this._assessmentResults?.issues.map(i => {
|
||||
const serverIssues = this._assessmentResults?.issues.map(i => {
|
||||
return {
|
||||
ruleId: i.ruleId,
|
||||
count: i.impactedObjects.length
|
||||
@@ -696,18 +711,15 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
|
||||
const serverAssessmentErrorsMap: Map<number, number> = new Map();
|
||||
this._assessmentApiResponse?.assessmentResult?.errors?.forEach(e => {
|
||||
serverAssessmentErrorsMap.set(e.errorId, serverAssessmentErrorsMap.get(e.errorId) ?? 0 + 1);
|
||||
serverAssessmentErrorsMap.set(
|
||||
e.errorId,
|
||||
serverAssessmentErrorsMap.get(e.errorId) ?? 0 + 1);
|
||||
});
|
||||
|
||||
let serverErrors: { errorId: number, count: number }[] = [];
|
||||
serverAssessmentErrorsMap.forEach((v, k) => {
|
||||
serverErrors.push(
|
||||
{
|
||||
errorId: k,
|
||||
count: v
|
||||
}
|
||||
);
|
||||
});
|
||||
const serverErrors: { errorId: number, count: number }[] = [];
|
||||
serverAssessmentErrorsMap.forEach(
|
||||
(v, k) => serverErrors.push(
|
||||
{ errorId: k, count: v }));
|
||||
|
||||
const startTime = new Date(this._assessmentApiResponse?.startTime);
|
||||
const endTime = new Date(this._assessmentApiResponse?.endedTime);
|
||||
@@ -759,27 +771,26 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
'assessmentTimeMs': d.assessmentTimeInMilliseconds,
|
||||
'numberOfBlockerIssues': d.sqlManagedInstanceTargetReadiness.numOfBlockerIssues,
|
||||
'databaseSizeInMb': d.databaseSize
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
d.items.forEach(i => {
|
||||
databaseWarningsMap.set(i.ruleId, databaseWarningsMap.get(i.ruleId) ?? 0 + i.impactedObjects.length);
|
||||
databaseWarningsMap.set(
|
||||
i.ruleId,
|
||||
databaseWarningsMap.get(i.ruleId) ?? 0 + i.impactedObjects.length);
|
||||
});
|
||||
|
||||
d.errors.forEach(e => {
|
||||
databaseErrorsMap.set(e.errorId, databaseErrorsMap.get(e.errorId) ?? 0 + 1);
|
||||
});
|
||||
d.errors.forEach(
|
||||
e => databaseErrorsMap.set(
|
||||
e.errorId,
|
||||
databaseErrorsMap.get(e.errorId) ?? 0 + 1));
|
||||
|
||||
});
|
||||
|
||||
let databaseWarnings: { warningId: string, count: number }[] = [];
|
||||
const databaseWarnings: { warningId: string, count: number }[] = [];
|
||||
|
||||
databaseWarningsMap.forEach((v, k) => {
|
||||
databaseWarnings.push({
|
||||
warningId: k,
|
||||
count: v
|
||||
});
|
||||
});
|
||||
databaseWarningsMap.forEach(
|
||||
(v, k) => databaseWarnings.push(
|
||||
{ warningId: k, count: v }));
|
||||
|
||||
sendSqlMigrationActionEvent(
|
||||
TelemetryViews.MigrationWizardTargetSelectionPage,
|
||||
@@ -788,16 +799,12 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
'sessionId': this._sessionId,
|
||||
'warnings': JSON.stringify(databaseWarnings)
|
||||
},
|
||||
{}
|
||||
);
|
||||
{});
|
||||
|
||||
let databaseErrors: { errorId: number, count: number }[] = [];
|
||||
databaseErrorsMap.forEach((v, k) => {
|
||||
databaseErrors.push({
|
||||
errorId: k,
|
||||
count: v
|
||||
});
|
||||
});
|
||||
const databaseErrors: { errorId: number, count: number }[] = [];
|
||||
databaseErrorsMap.forEach(
|
||||
(v, k) => databaseErrors.push(
|
||||
{ errorId: k, count: v }));
|
||||
|
||||
sendSqlMigrationActionEvent(
|
||||
TelemetryViews.MigrationWizardTargetSelectionPage,
|
||||
@@ -806,8 +813,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
'sessionId': this._sessionId,
|
||||
'errors': JSON.stringify(databaseErrors)
|
||||
},
|
||||
{}
|
||||
);
|
||||
{});
|
||||
|
||||
} catch (e) {
|
||||
console.log('error during assessment telemetry:');
|
||||
@@ -837,13 +843,8 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
|
||||
public async getSourceConnectionProfile(): Promise<azdata.connection.ConnectionProfile> {
|
||||
const sqlConnections = await azdata.connection.getConnections();
|
||||
return sqlConnections.find((value) => {
|
||||
if (value.connectionId === this.sourceConnectionId) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
})!;
|
||||
return sqlConnections.find(
|
||||
value => value.connectionId === this.sourceConnectionId)!;
|
||||
}
|
||||
|
||||
public getLocationDisplayName(location: string): Promise<string> {
|
||||
@@ -851,22 +852,20 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
}
|
||||
|
||||
public async getManagedDatabases(): Promise<string[]> {
|
||||
return (await getSqlManagedInstanceDatabases(this._azureAccount,
|
||||
this._targetSubscription,
|
||||
<SqlManagedInstance>this._targetServerInstance)).map(t => t.name);
|
||||
return (
|
||||
await getSqlManagedInstanceDatabases(this._azureAccount,
|
||||
this._targetSubscription,
|
||||
<SqlManagedInstance>this._targetServerInstance)
|
||||
).map(t => t.name);
|
||||
}
|
||||
|
||||
public async startMigration() {
|
||||
const sqlConnections = await azdata.connection.getConnections();
|
||||
const currentConnection = sqlConnections.find((value) => {
|
||||
if (value.connectionId === this.sourceConnectionId) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
const currentConnection = sqlConnections.find(
|
||||
value => value.connectionId === this.sourceConnectionId);
|
||||
|
||||
const isOfflineMigration = this._databaseBackup.migrationMode === MigrationMode.OFFLINE;
|
||||
const isSqlDbTarget = this._targetType === MigrationTargetType.SQLDB;
|
||||
|
||||
const requestBody: StartDatabaseMigrationRequest = {
|
||||
location: this._sqlMigrationService?.location!,
|
||||
@@ -877,7 +876,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
sourceSqlConnection: {
|
||||
dataSource: currentConnection?.serverName!,
|
||||
authentication: this._authenticationType,
|
||||
username: this._sqlServerUsername,
|
||||
userName: this._sqlServerUsername,
|
||||
password: this._sqlServerPassword
|
||||
},
|
||||
scope: this._targetServerInstance.id,
|
||||
@@ -926,6 +925,55 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
}
|
||||
};
|
||||
break;
|
||||
default:
|
||||
if (isSqlDbTarget) {
|
||||
const sourceDatabaseName = this._databasesForMigration[i];
|
||||
const targetDatabaseInfo = this._sourceTargetMapping.get(sourceDatabaseName);
|
||||
const totalTables = targetDatabaseInfo?.sourceTables.size ?? 0;
|
||||
// skip databases that don't have tables
|
||||
if (totalTables === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const sourceTables: string[] = [];
|
||||
let selectedTables = 0;
|
||||
targetDatabaseInfo?.sourceTables.forEach(sourceTableInfo => {
|
||||
if (sourceTableInfo.selectedForMigration) {
|
||||
selectedTables++;
|
||||
sourceTables.push(sourceTableInfo.tableName);
|
||||
}
|
||||
});
|
||||
|
||||
// skip databases that don't have tables selected
|
||||
if (selectedTables === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const sqlDbTarget = this._targetServerInstance as AzureSqlDatabaseServer;
|
||||
requestBody.properties.offlineConfiguration = undefined;
|
||||
requestBody.properties.sourceSqlConnection = {
|
||||
dataSource: currentConnection?.serverName!,
|
||||
authentication: this._authenticationType,
|
||||
userName: this._sqlServerUsername,
|
||||
password: this._sqlServerPassword,
|
||||
encryptConnection: true,
|
||||
trustServerCertificate: false,
|
||||
};
|
||||
requestBody.properties.targetSqlConnection = {
|
||||
dataSource: sqlDbTarget.properties.fullyQualifiedDomainName,
|
||||
authentication: MigrationSourceAuthenticationType.Sql,
|
||||
userName: this._targetUserName,
|
||||
password: this._targetPassword,
|
||||
encryptConnection: true,
|
||||
trustServerCertificate: false,
|
||||
};
|
||||
|
||||
// send an empty array when 'all' tables are selected for migration
|
||||
requestBody.properties.tableList = selectedTables === totalTables
|
||||
? []
|
||||
: sourceTables;
|
||||
}
|
||||
break;
|
||||
}
|
||||
requestBody.properties.sourceDatabaseName = this._databasesForMigration[i];
|
||||
const response = await startDatabaseMigration(
|
||||
@@ -969,8 +1017,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
'wizardEntryPoint': wizardEntryPoint,
|
||||
},
|
||||
{
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
void vscode.window.showInformationMessage(
|
||||
localize(
|
||||
@@ -982,7 +1029,10 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
}
|
||||
} catch (e) {
|
||||
void vscode.window.showErrorMessage(
|
||||
localize('sql.migration.starting.migration.error', "An error occurred while starting the migration: '{0}'", e.message));
|
||||
localize(
|
||||
'sql.migration.starting.migration.error',
|
||||
"An error occurred while starting the migration: '{0}'",
|
||||
e.message));
|
||||
logError(TelemetryViews.MigrationLocalStorage, 'StartMigrationFailed', e);
|
||||
}
|
||||
finally {
|
||||
@@ -990,15 +1040,15 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
await this.refreshPerfDataCollection();
|
||||
if ((!this.resumeAssessment || this.retryMigration) && this._perfDataCollectionIsCollecting) {
|
||||
void this.stopPerfDataCollection();
|
||||
void vscode.window.showInformationMessage(constants.AZURE_RECOMMENDATION_STOP_POPUP);
|
||||
void vscode.window.showInformationMessage(
|
||||
constants.AZURE_RECOMMENDATION_STOP_POPUP);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async saveInfo(serverName: string, currentPage: Page): Promise<void> {
|
||||
let saveInfo: SavedInfo;
|
||||
saveInfo = {
|
||||
const saveInfo: SavedInfo = {
|
||||
closedPage: currentPage,
|
||||
databaseAssessment: [],
|
||||
databaseList: [],
|
||||
@@ -1047,7 +1097,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
saveInfo.serverAssessment = this._assessmentResults;
|
||||
|
||||
if (this._skuRecommendationPerformanceDataSource) {
|
||||
let skuRecommendation: SkuRecommendationSavedInfo = {
|
||||
const skuRecommendation: SkuRecommendationSavedInfo = {
|
||||
skuRecommendationPerformanceDataSource: this._skuRecommendationPerformanceDataSource,
|
||||
skuRecommendationPerformanceLocation: this._skuRecommendationPerformanceLocation,
|
||||
perfDataCollectionStartDate: this._perfDataCollectionStartDate,
|
||||
@@ -1072,6 +1122,9 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
this._databasesForAssessment = this.savedInfo.databaseAssessment;
|
||||
this._databasesForMigration = this.savedInfo.databaseList;
|
||||
this._didUpdateDatabasesForMigration = true;
|
||||
this._didDatabaseMappingChange = true;
|
||||
this.refreshDatabaseBackupPage = true;
|
||||
|
||||
switch (this._targetType) {
|
||||
case MigrationTargetType.SQLMI:
|
||||
this._miDbs = this._databasesForMigration;
|
||||
@@ -1079,6 +1132,9 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
case MigrationTargetType.SQLVM:
|
||||
this._vmDbs = this._databasesForMigration;
|
||||
break;
|
||||
case MigrationTargetType.SQLDB:
|
||||
this._sqldbDbs = this._databasesForMigration;
|
||||
break;
|
||||
}
|
||||
|
||||
this._azureAccount = this.savedInfo.azureAccount || undefined!;
|
||||
@@ -1091,7 +1147,6 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
|
||||
this._databaseBackup.migrationMode = this.savedInfo.migrationMode || undefined!;
|
||||
|
||||
this.refreshDatabaseBackupPage = true;
|
||||
this._sourceDatabaseNames = this._databasesForMigration;
|
||||
this._targetDatabaseNames = this.savedInfo.targetDatabaseNames;
|
||||
this._databaseBackup.networkContainerType = this.savedInfo.networkContainerType || undefined!;
|
||||
|
||||
Reference in New Issue
Block a user