mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Adding Assessment Telemetry in SQL Migration (#15935)
* Adding telemetry for assessment * Removing dms loading fix * Fixing PR * removing collection of account id * moving database warning count to the server assessment event * Removing individual warning and issue events * Adding aggregates for database issues and warnings * removing extra line * Adding other telemetry events in sql migration * Fixed changes made in the PR * Fixing attribute names * Consolidating issues and errors in 1 event * Converting dependencies to dev depenedencies * Adding a catch for the telemetry function * moving the non type uuid to non dev * Made hashmap code cleaner * Fixing cutover start time * Fixing object creation * Reverting back to old method. * Modifying the date time to js objects * Converting issues and warnings to json object
This commit is contained in:
4
extensions/mssql/src/mssql.d.ts
vendored
4
extensions/mssql/src/mssql.d.ts
vendored
@@ -591,8 +591,8 @@ export interface ServerAssessmentProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface AssessmentResult {
|
export interface AssessmentResult {
|
||||||
startedOn: string;
|
startTime: string;
|
||||||
endedOn: string;
|
endedTime: string;
|
||||||
assessmentResult: ServerAssessmentProperties;
|
assessmentResult: ServerAssessmentProperties;
|
||||||
rawAssessmentResult: any;
|
rawAssessmentResult: any;
|
||||||
errors: ErrorModel[];
|
errors: ErrorModel[];
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"preview": true,
|
"preview": true,
|
||||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt",
|
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt",
|
||||||
"icon": "images/extension.png",
|
"icon": "images/extension.png",
|
||||||
"aiKey": "06ba2446-fa56-40aa-853a-26b73255b723",
|
"aiKey": "AIF-37eefaf0-8022-4671-a3fb-64752724682e",
|
||||||
"engines": {
|
"engines": {
|
||||||
"vscode": "*",
|
"vscode": "*",
|
||||||
"azdata": ">=1.29.0"
|
"azdata": ">=1.29.0"
|
||||||
@@ -151,10 +151,14 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@microsoft/ads-extension-telemetry": "^1.1.3",
|
"@microsoft/ads-extension-telemetry": "^1.1.3",
|
||||||
|
"uuid": "^8.3.2",
|
||||||
"vscode-nls": "^4.1.2"
|
"vscode-nls": "^4.1.2"
|
||||||
},
|
},
|
||||||
"__metadata": {
|
"__metadata": {
|
||||||
"publisherDisplayName": "Microsoft",
|
"publisherDisplayName": "Microsoft",
|
||||||
"publisherId": "Microsoft"
|
"publisherId": "Microsoft"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/uuid": "^8.3.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -403,7 +403,8 @@ export interface StartDatabaseMigrationRequest {
|
|||||||
username: string,
|
username: string,
|
||||||
password: string
|
password: string
|
||||||
},
|
},
|
||||||
scope: string
|
scope: string,
|
||||||
|
autoCutoverConfiguration?: AutoCutoverConfiguration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { CategoryValue, DropDownComponent } from 'azdata';
|
|||||||
import { DAYS, HRS, MINUTE, SEC } from '../constants/strings';
|
import { DAYS, HRS, MINUTE, SEC } from '../constants/strings';
|
||||||
import { AdsMigrationStatus } from '../dialog/migrationStatus/migrationStatusDialogModel';
|
import { AdsMigrationStatus } from '../dialog/migrationStatus/migrationStatusDialogModel';
|
||||||
import { MigrationContext } from '../models/migrationLocalStorage';
|
import { MigrationContext } from '../models/migrationLocalStorage';
|
||||||
|
import * as crypto from 'crypto';
|
||||||
|
|
||||||
export function deepClone<T>(obj: T): T {
|
export function deepClone<T>(obj: T): T {
|
||||||
if (!obj || typeof obj !== 'object') {
|
if (!obj || typeof obj !== 'object') {
|
||||||
@@ -157,6 +158,10 @@ export function findDropDownItemIndex(dropDown: DropDownComponent, value: string
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function hashString(value: string): string {
|
||||||
|
return crypto.createHash('sha512').update(value).digest('hex');
|
||||||
|
}
|
||||||
|
|
||||||
export function debounce(delay: number): Function {
|
export function debounce(delay: number): Function {
|
||||||
return decorate((fn, key) => {
|
return decorate((fn, key) => {
|
||||||
const timerKey = `$debounce$${key}`;
|
const timerKey = `$debounce$${key}`;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
import { getMigrationStatus, DatabaseMigration, startMigrationCutover, stopMigration, getMigrationAsyncOperationDetails, AzureAsyncOperationResource } from '../../api/azure';
|
import { getMigrationStatus, DatabaseMigration, startMigrationCutover, stopMigration, getMigrationAsyncOperationDetails, AzureAsyncOperationResource } from '../../api/azure';
|
||||||
import { MigrationContext } from '../../models/migrationLocalStorage';
|
import { MigrationContext } from '../../models/migrationLocalStorage';
|
||||||
|
import { sendSqlMigrationActionEvent, TelemetryAction, TelemetryViews } from '../../telemtery';
|
||||||
|
|
||||||
export enum MigrationStatus {
|
export enum MigrationStatus {
|
||||||
Failed = 'Failed',
|
Failed = 'Failed',
|
||||||
@@ -34,6 +35,16 @@ export class MigrationCutoverDialogModel {
|
|||||||
this._migration.subscription,
|
this._migration.subscription,
|
||||||
this._migration.migrationContext
|
this._migration.migrationContext
|
||||||
));
|
));
|
||||||
|
|
||||||
|
sendSqlMigrationActionEvent(
|
||||||
|
TelemetryViews.MigrationCutoverDialog,
|
||||||
|
TelemetryAction.MigrationStatus,
|
||||||
|
{
|
||||||
|
'sessionId': this._migration.sessionId!,
|
||||||
|
'migrationStatus': this.migrationStatus.properties.migrationStatus
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
// Logging status to help debugging.
|
// Logging status to help debugging.
|
||||||
console.log(this.migrationStatus);
|
console.log(this.migrationStatus);
|
||||||
}
|
}
|
||||||
@@ -41,11 +52,21 @@ export class MigrationCutoverDialogModel {
|
|||||||
public async startCutover(): Promise<DatabaseMigration | undefined> {
|
public async startCutover(): Promise<DatabaseMigration | undefined> {
|
||||||
try {
|
try {
|
||||||
if (this.migrationStatus) {
|
if (this.migrationStatus) {
|
||||||
return await startMigrationCutover(
|
const cutover = await startMigrationCutover(
|
||||||
this._migration.azureAccount,
|
this._migration.azureAccount,
|
||||||
this._migration.subscription,
|
this._migration.subscription,
|
||||||
this.migrationStatus
|
this.migrationStatus
|
||||||
);
|
);
|
||||||
|
sendSqlMigrationActionEvent(
|
||||||
|
TelemetryViews.MigrationCutoverDialog,
|
||||||
|
TelemetryAction.CutoverMigration,
|
||||||
|
{
|
||||||
|
'sessionId': this._migration.sessionId!,
|
||||||
|
'migrationEndTime': new Date().toString()
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
return cutover;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
@@ -56,11 +77,21 @@ export class MigrationCutoverDialogModel {
|
|||||||
public async cancelMigration(): Promise<void> {
|
public async cancelMigration(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
if (this.migrationStatus) {
|
if (this.migrationStatus) {
|
||||||
|
const cutoverStartTime = new Date().toString();
|
||||||
await stopMigration(
|
await stopMigration(
|
||||||
this._migration.azureAccount,
|
this._migration.azureAccount,
|
||||||
this._migration.subscription,
|
this._migration.subscription,
|
||||||
this.migrationStatus
|
this.migrationStatus
|
||||||
);
|
);
|
||||||
|
sendSqlMigrationActionEvent(
|
||||||
|
TelemetryViews.MigrationCutoverDialog,
|
||||||
|
TelemetryAction.CancelMigration,
|
||||||
|
{
|
||||||
|
'sessionId': this._migration.sessionId!,
|
||||||
|
'cutoverStartTime': cutoverStartTime
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
|||||||
@@ -67,7 +67,8 @@ export class MigrationLocalStorage {
|
|||||||
azureAccount: azdata.Account,
|
azureAccount: azdata.Account,
|
||||||
subscription: azureResource.AzureResourceSubscription,
|
subscription: azureResource.AzureResourceSubscription,
|
||||||
controller: SqlMigrationService,
|
controller: SqlMigrationService,
|
||||||
asyncURL: string): void {
|
asyncURL: string,
|
||||||
|
sessionId: string): void {
|
||||||
try {
|
try {
|
||||||
let migrationMementos: MigrationContext[] = this.context.globalState.get(this.mementoToken) || [];
|
let migrationMementos: MigrationContext[] = this.context.globalState.get(this.mementoToken) || [];
|
||||||
migrationMementos = migrationMementos.filter(m => m.migrationContext.id !== migrationContext.id);
|
migrationMementos = migrationMementos.filter(m => m.migrationContext.id !== migrationContext.id);
|
||||||
@@ -78,7 +79,8 @@ export class MigrationLocalStorage {
|
|||||||
subscription: subscription,
|
subscription: subscription,
|
||||||
azureAccount: azureAccount,
|
azureAccount: azureAccount,
|
||||||
controller: controller,
|
controller: controller,
|
||||||
asyncUrl: asyncURL
|
asyncUrl: asyncURL,
|
||||||
|
sessionId: sessionId
|
||||||
});
|
});
|
||||||
this.context.globalState.update(this.mementoToken, migrationMementos);
|
this.context.globalState.update(this.mementoToken, migrationMementos);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -99,5 +101,6 @@ export interface MigrationContext {
|
|||||||
subscription: azureResource.AzureResourceSubscription,
|
subscription: azureResource.AzureResourceSubscription,
|
||||||
controller: SqlMigrationService,
|
controller: SqlMigrationService,
|
||||||
asyncUrl: string,
|
asyncUrl: string,
|
||||||
asyncOperationResult?: AzureAsyncOperationResource
|
asyncOperationResult?: AzureAsyncOperationResource,
|
||||||
|
sessionId?: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ import { SKURecommendations } from './externalContract';
|
|||||||
import * as constants from '../constants/strings';
|
import * as constants from '../constants/strings';
|
||||||
import { MigrationLocalStorage } from './migrationLocalStorage';
|
import { MigrationLocalStorage } from './migrationLocalStorage';
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
import { sendSqlMigrationActionEvent, TelemetryAction, TelemetryViews } from '../telemtery';
|
||||||
|
import { hashString } from '../api/utils';
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
export enum State {
|
export enum State {
|
||||||
@@ -133,11 +136,15 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
|
|
||||||
private _skuRecommendations: SKURecommendations | undefined;
|
private _skuRecommendations: SKURecommendations | undefined;
|
||||||
public _assessmentResults!: ServerAssessement;
|
public _assessmentResults!: ServerAssessement;
|
||||||
|
private _assessmentApiResponse!: mssql.AssessmentResult;
|
||||||
|
|
||||||
public _vmDbs: string[] = [];
|
public _vmDbs: string[] = [];
|
||||||
public _miDbs: string[] = [];
|
public _miDbs: string[] = [];
|
||||||
public _targetType!: MigrationTargetType;
|
public _targetType!: MigrationTargetType;
|
||||||
public refreshDatabaseBackupPage!: boolean;
|
public refreshDatabaseBackupPage!: boolean;
|
||||||
|
|
||||||
|
public _sessionId: string = uuidv4();
|
||||||
|
|
||||||
public excludeDbs: string[] = [
|
public excludeDbs: string[] = [
|
||||||
'master',
|
'master',
|
||||||
'tempdb',
|
'tempdb',
|
||||||
@@ -177,27 +184,151 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
|
|
||||||
public async getDatabaseAssessments(): Promise<ServerAssessement> {
|
public async getDatabaseAssessments(): Promise<ServerAssessement> {
|
||||||
const ownerUri = await azdata.connection.getUriForConnection(this.sourceConnectionId);
|
const ownerUri = await azdata.connection.getUriForConnection(this.sourceConnectionId);
|
||||||
// stress test backend & dialog component
|
this._assessmentApiResponse = (await this.migrationService.getAssessments(ownerUri, this._databaseAssessment))!;
|
||||||
const assessmentResults = await this.migrationService.getAssessments(
|
|
||||||
ownerUri,
|
|
||||||
this._databaseAssessment
|
|
||||||
);
|
|
||||||
const dbAssessments = assessmentResults?.assessmentResult.databases.filter(d => !this.excludeDbs.includes(d.name)).map(d => {
|
|
||||||
return {
|
|
||||||
name: d.name,
|
|
||||||
issues: d.items.filter(i => i.appliesToMigrationTargetPlatform === MigrationTargetType.SQLMI) ?? []
|
|
||||||
};
|
|
||||||
});
|
|
||||||
this._assessmentResults = {
|
this._assessmentResults = {
|
||||||
issues: assessmentResults?.assessmentResult.items?.filter(i => i.appliesToMigrationTargetPlatform === MigrationTargetType.SQLMI) ?? [],
|
issues: this._assessmentApiResponse.assessmentResult.items,
|
||||||
databaseAssessments: dbAssessments! ?? []
|
databaseAssessments: this._assessmentApiResponse.assessmentResult.databases.map(d => {
|
||||||
|
return {
|
||||||
|
name: d.name,
|
||||||
|
issues: d.items
|
||||||
|
};
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Generating all the telemetry asynchronously as we don't need to block the user for it.
|
||||||
|
this.generateAssessmentTelemetry().catch(e => console.error(e));
|
||||||
return this._assessmentResults;
|
return this._assessmentResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
public findDatabaseAssessments(databaseName: string): mssql.SqlMigrationAssessmentResultItem[] | undefined {
|
private async generateAssessmentTelemetry(): Promise<void> {
|
||||||
return this._assessmentResults.databaseAssessments.find(databaseAsssessment => databaseAsssessment.name === databaseName)?.issues;
|
try {
|
||||||
|
|
||||||
|
let serverIssues = this._assessmentResults.issues.map(i => {
|
||||||
|
return {
|
||||||
|
ruleId: i.ruleId,
|
||||||
|
count: i.impactedObjects.length
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const serverAssessmentErrorsMap: Map<number, number> = new Map();
|
||||||
|
this._assessmentApiResponse.assessmentResult.errors.forEach(e => {
|
||||||
|
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 startTime = new Date(this._assessmentApiResponse.startTime);
|
||||||
|
const endTime = new Date(this._assessmentApiResponse.endedTime);
|
||||||
|
|
||||||
|
sendSqlMigrationActionEvent(
|
||||||
|
TelemetryViews.MigrationWizardTargetSelectionPage,
|
||||||
|
TelemetryAction.ServerAssessment,
|
||||||
|
{
|
||||||
|
'sessionId': this._sessionId,
|
||||||
|
'tenantId': this._azureAccount.properties.tenants[0].id,
|
||||||
|
'hashedServerName': hashString(this._assessmentApiResponse.assessmentResult.name),
|
||||||
|
'startTime': startTime.toString(),
|
||||||
|
'endTime': endTime.toString(),
|
||||||
|
'serverVersion': this._assessmentApiResponse.assessmentResult.serverVersion,
|
||||||
|
'serverEdition': this._assessmentApiResponse.assessmentResult.serverEdition,
|
||||||
|
'platform': this._assessmentApiResponse.assessmentResult.serverHostPlatform,
|
||||||
|
'engineEdition': this._assessmentApiResponse.assessmentResult.serverEngineEdition,
|
||||||
|
'serverIssues': JSON.stringify(serverIssues),
|
||||||
|
'serverErrors': JSON.stringify(serverErrors)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'issuesCount': this._assessmentResults.issues.length,
|
||||||
|
'warningsCount': this._assessmentResults.databaseAssessments.reduce((count, d) => count + d.issues.length, 0),
|
||||||
|
'durationInMilliseconds': endTime.getTime() - startTime.getTime(),
|
||||||
|
'databaseCount': this._assessmentResults.databaseAssessments.length,
|
||||||
|
'serverHostCpuCount': this._assessmentApiResponse.assessmentResult.cpuCoreCount,
|
||||||
|
'serverHostPhysicalMemoryInBytes': this._assessmentApiResponse.assessmentResult.physicalServerMemory,
|
||||||
|
'serverDatabases': this._assessmentApiResponse.assessmentResult.numberOfUserDatabases,
|
||||||
|
'serverDatabasesReadyForMigration': this._assessmentApiResponse.assessmentResult.sqlManagedInstanceTargetReadiness.numberOfDatabasesReadyForMigration,
|
||||||
|
'offlineDatabases': this._assessmentApiResponse.assessmentResult.sqlManagedInstanceTargetReadiness.numberOfNonOnlineDatabases
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const databaseWarningsMap: Map<string, number> = new Map();
|
||||||
|
const databaseErrorsMap: Map<number, number> = new Map();
|
||||||
|
|
||||||
|
this._assessmentApiResponse.assessmentResult.databases.forEach(d => {
|
||||||
|
|
||||||
|
sendSqlMigrationActionEvent(
|
||||||
|
TelemetryViews.MigrationWizardTargetSelectionPage,
|
||||||
|
TelemetryAction.DatabaseAssessment,
|
||||||
|
{
|
||||||
|
'sessionId': this._sessionId,
|
||||||
|
'hashedDatabaseName': hashString(d.name),
|
||||||
|
'compatibilityLevel': d.compatibilityLevel
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'warningsCount': d.items.length,
|
||||||
|
'errorsCount': d.errors.length,
|
||||||
|
'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);
|
||||||
|
});
|
||||||
|
|
||||||
|
d.errors.forEach(e => {
|
||||||
|
databaseErrorsMap.set(e.errorId, databaseErrorsMap.get(e.errorId) ?? 0 + 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
let databaseWarnings: { warningId: string, count: number }[] = [];
|
||||||
|
|
||||||
|
databaseWarningsMap.forEach((v, k) => {
|
||||||
|
databaseWarnings.push({
|
||||||
|
warningId: k,
|
||||||
|
count: v
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
sendSqlMigrationActionEvent(
|
||||||
|
TelemetryViews.MigrationWizardTargetSelectionPage,
|
||||||
|
TelemetryAction.DatabaseAssessmentWarning,
|
||||||
|
{
|
||||||
|
'sessionId': this._sessionId,
|
||||||
|
'warnings': JSON.stringify(databaseWarnings)
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
let databaseErrors: { errorId: number, count: number }[] = [];
|
||||||
|
databaseErrorsMap.forEach((v, k) => {
|
||||||
|
databaseErrors.push({
|
||||||
|
errorId: k,
|
||||||
|
count: v
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
sendSqlMigrationActionEvent(
|
||||||
|
TelemetryViews.MigrationWizardTargetSelectionPage,
|
||||||
|
TelemetryAction.DatabaseAssessmentError,
|
||||||
|
{
|
||||||
|
'sessionId': this._sessionId,
|
||||||
|
'errors': JSON.stringify(databaseErrors)
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public get gatheringInformationError(): string | undefined {
|
public get gatheringInformationError(): string | undefined {
|
||||||
@@ -566,6 +697,15 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
|
|
||||||
public async getBlobContainerValues(subscription: azureResource.AzureResourceSubscription, storageAccount: StorageAccount): Promise<azdata.CategoryValue[]> {
|
public async getBlobContainerValues(subscription: azureResource.AzureResourceSubscription, storageAccount: StorageAccount): Promise<azdata.CategoryValue[]> {
|
||||||
let blobContainerValues: azdata.CategoryValue[] = [];
|
let blobContainerValues: azdata.CategoryValue[] = [];
|
||||||
|
if (!this._azureAccount || !subscription || !storageAccount) {
|
||||||
|
blobContainerValues = [
|
||||||
|
{
|
||||||
|
displayName: constants.NO_BLOBCONTAINERS_FOUND,
|
||||||
|
name: ''
|
||||||
|
}
|
||||||
|
];
|
||||||
|
return blobContainerValues;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
this._blobContainers = await getBlobContainers(this._azureAccount, subscription, storageAccount);
|
this._blobContainers = await getBlobContainers(this._azureAccount, subscription, storageAccount);
|
||||||
this._blobContainers.forEach((blobContainer) => {
|
this._blobContainers.forEach((blobContainer) => {
|
||||||
@@ -704,6 +844,27 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
response.databaseMigration.properties.sourceDatabaseName = this._migrationDbs[i];
|
response.databaseMigration.properties.sourceDatabaseName = this._migrationDbs[i];
|
||||||
response.databaseMigration.properties.backupConfiguration = requestBody.properties.backupConfiguration!;
|
response.databaseMigration.properties.backupConfiguration = requestBody.properties.backupConfiguration!;
|
||||||
if (response.status === 201 || response.status === 200) {
|
if (response.status === 201 || response.status === 200) {
|
||||||
|
|
||||||
|
sendSqlMigrationActionEvent(
|
||||||
|
TelemetryViews.MigrationWizardSummaryPage,
|
||||||
|
TelemetryAction.StartMigration,
|
||||||
|
{
|
||||||
|
'hashedServerName': hashString(this._assessmentApiResponse.assessmentResult.name),
|
||||||
|
'hashedDatabaseName': hashString(this._migrationDbs[i]),
|
||||||
|
'migrationMode': requestBody.properties.autoCutoverConfiguration ? 'online' : 'offline',
|
||||||
|
'sessionId': this._sessionId,
|
||||||
|
'migrationStartTime': new Date().toString(),
|
||||||
|
'targetDatabaseName': this._targetDatabaseNames[i],
|
||||||
|
'serverName': this._targetServerInstance.name,
|
||||||
|
'tenantId': this._azureAccount.properties.tenants[0].id,
|
||||||
|
'location': this._targetServerInstance.location,
|
||||||
|
'sqlMigrationServiceId': this._sqlMigrationService.id,
|
||||||
|
'irRegistered': (this._nodeNames.length > 0).toString()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
MigrationLocalStorage.saveMigration(
|
MigrationLocalStorage.saveMigration(
|
||||||
currentConnection!,
|
currentConnection!,
|
||||||
response.databaseMigration,
|
response.databaseMigration,
|
||||||
@@ -711,7 +872,8 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
this._azureAccount,
|
this._azureAccount,
|
||||||
this._targetSubscription,
|
this._targetSubscription,
|
||||||
this._sqlMigrationService,
|
this._sqlMigrationService,
|
||||||
response.asyncUrl
|
response.asyncUrl,
|
||||||
|
this._sessionId
|
||||||
);
|
);
|
||||||
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]));
|
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]));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,22 +6,36 @@
|
|||||||
import AdsTelemetryReporter, { TelemetryEventMeasures, TelemetryEventProperties } from '@microsoft/ads-extension-telemetry';
|
import AdsTelemetryReporter, { TelemetryEventMeasures, TelemetryEventProperties } from '@microsoft/ads-extension-telemetry';
|
||||||
import { getPackageInfo } from './api/utils';
|
import { getPackageInfo } from './api/utils';
|
||||||
const packageJson = require('../package.json');
|
const packageJson = require('../package.json');
|
||||||
|
|
||||||
let packageInfo = getPackageInfo(packageJson)!;
|
let packageInfo = getPackageInfo(packageJson)!;
|
||||||
|
|
||||||
export const TelemetryReporter = new AdsTelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey);
|
export const TelemetryReporter = new AdsTelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey);
|
||||||
|
|
||||||
export enum TelemetryViews {
|
export enum TelemetryViews {
|
||||||
SqlServerDashboard = 'SqlServerDashboard',
|
SqlServerDashboard = 'SqlServerDashboard',
|
||||||
MigrationWizard = 'MigrationWizard',
|
|
||||||
CreateDataMigrationServiceDialog = 'CreateDataMigrationServiceDialog',
|
CreateDataMigrationServiceDialog = 'CreateDataMigrationServiceDialog',
|
||||||
AssessmentsDialog = 'AssessmentsDialog',
|
AssessmentsDialog = 'AssessmentsDialog',
|
||||||
MigrationCutoverDialog = 'MigrationCutoverDialog',
|
MigrationCutoverDialog = 'MigrationCutoverDialog',
|
||||||
MigrationStatusDialog = 'MigrationStatusDialog',
|
MigrationStatusDialog = 'MigrationStatusDialog',
|
||||||
AssessmentsPage = 'AssessmentsPage'
|
MigrationWizardAccountSelectionPage = 'MigrationWizardAccountSelectionPage',
|
||||||
|
MigrationWizardTargetSelectionPage = 'MigrationWizardTargetSelectionPage',
|
||||||
|
MigrationWizardSummaryPage = 'MigrationWizardSummaryPage',
|
||||||
|
StartMigrationService = 'StartMigrationSerivce'
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sendSqlMigrationActionEvent(telemetryView: string, telemetryAction: string, additionalProps: TelemetryEventProperties, additionalMeasurements: TelemetryEventMeasures): void {
|
export enum TelemetryAction {
|
||||||
|
ServerAssessment = 'ServerAssessment',
|
||||||
|
ServerAssessmentIssues = 'ServerAssessmentIssues',
|
||||||
|
ServerAssessmentError = 'ServerAssessmentError',
|
||||||
|
DatabaseAssessment = 'DatabaseAsssessment',
|
||||||
|
DatabaseAssessmentWarning = 'DatabaseAssessmentWarning',
|
||||||
|
DatabaseAssessmentError = 'DatabaseAssessmentError',
|
||||||
|
StartMigration = 'StartMigration',
|
||||||
|
CutoverMigration = 'CutoverMigration',
|
||||||
|
CancelMigration = 'CancelMigration',
|
||||||
|
MigrationStatus = 'MigrationStatus'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sendSqlMigrationActionEvent(telemetryView: TelemetryViews, telemetryAction: TelemetryAction, additionalProps: TelemetryEventProperties, additionalMeasurements: TelemetryEventMeasures): void {
|
||||||
TelemetryReporter.createActionEvent(telemetryView, telemetryAction)
|
TelemetryReporter.createActionEvent(telemetryView, telemetryAction)
|
||||||
.withAdditionalProperties(additionalProps)
|
.withAdditionalProperties(additionalProps)
|
||||||
.withAdditionalMeasurements(additionalMeasurements)
|
.withAdditionalMeasurements(additionalMeasurements)
|
||||||
|
|||||||
@@ -9,6 +9,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
vscode-extension-telemetry "^0.1.6"
|
vscode-extension-telemetry "^0.1.6"
|
||||||
|
|
||||||
|
"@types/uuid@^8.3.1":
|
||||||
|
version "8.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.1.tgz#1a32969cf8f0364b3d8c8af9cc3555b7805df14f"
|
||||||
|
integrity sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg==
|
||||||
|
|
||||||
applicationinsights@1.7.4:
|
applicationinsights@1.7.4:
|
||||||
version "1.7.4"
|
version "1.7.4"
|
||||||
resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.7.4.tgz#e7d96435594d893b00cf49f70a5927105dbb8749"
|
resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.7.4.tgz#e7d96435594d893b00cf49f70a5927105dbb8749"
|
||||||
@@ -85,6 +90,11 @@ stack-chain@^1.3.7:
|
|||||||
resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-1.3.7.tgz#d192c9ff4ea6a22c94c4dd459171e3f00cea1285"
|
resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-1.3.7.tgz#d192c9ff4ea6a22c94c4dd459171e3f00cea1285"
|
||||||
integrity sha1-0ZLJ/06moiyUxN1FkXHj8AzqEoU=
|
integrity sha1-0ZLJ/06moiyUxN1FkXHj8AzqEoU=
|
||||||
|
|
||||||
|
uuid@^8.3.2:
|
||||||
|
version "8.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||||
|
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||||
|
|
||||||
vscode-extension-telemetry@^0.1.6:
|
vscode-extension-telemetry@^0.1.6:
|
||||||
version "0.1.7"
|
version "0.1.7"
|
||||||
resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.7.tgz#18389bc24127c89dade29cd2b71ba69a6ee6ad26"
|
resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.7.tgz#18389bc24127c89dade29cd2b71ba69a6ee6ad26"
|
||||||
|
|||||||
Reference in New Issue
Block a user