mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Refactor functionality into LoginMigrationsModel (#21933)
This PR refactors to encapsulate all login migration functionality into LoginMigrationModel
This commit is contained in:
@@ -6,7 +6,10 @@
|
|||||||
import * as mssql from 'mssql';
|
import * as mssql from 'mssql';
|
||||||
import { MultiStepResult, MultiStepState } from '../dialog/generic/multiStepStatusDialog';
|
import { MultiStepResult, MultiStepState } from '../dialog/generic/multiStepStatusDialog';
|
||||||
import * as constants from '../constants/strings';
|
import * as constants from '../constants/strings';
|
||||||
import { LoginTableInfo } from '../api/sqlUtils';
|
import { getSourceConnectionString, getTargetConnectionString, LoginTableInfo } from '../api/sqlUtils';
|
||||||
|
import { MigrationStateModel } from './stateMachine';
|
||||||
|
import { logError, TelemetryViews } from '../telemetry';
|
||||||
|
import * as contracts from '../service/contracts';
|
||||||
|
|
||||||
type ExceptionMap = { [login: string]: any }
|
type ExceptionMap = { [login: string]: any }
|
||||||
|
|
||||||
@@ -59,14 +62,17 @@ export class LoginMigrationModel {
|
|||||||
public collectedTargetLogins: boolean = false;;
|
public collectedTargetLogins: boolean = false;;
|
||||||
public loginsOnSource: LoginTableInfo[] = [];
|
public loginsOnSource: LoginTableInfo[] = [];
|
||||||
public loginsOnTarget: string[] = [];
|
public loginsOnTarget: string[] = [];
|
||||||
private _currentStepIdx: number = 0;;
|
public loginMigrationsResult!: contracts.StartLoginMigrationResult;
|
||||||
|
public loginMigrationsError: any;
|
||||||
|
public loginsForMigration!: LoginTableInfo[];
|
||||||
|
private _currentStepIdx: number = 0;
|
||||||
private _logins: Map<string, Login>;
|
private _logins: Map<string, Login>;
|
||||||
private _loginMigrationSteps: LoginMigrationStep[] = [];
|
private _loginMigrationSteps: LoginMigrationStep[] = [];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.resultsPerStep = new Map<mssql.LoginMigrationStep, mssql.StartLoginMigrationResult>();
|
this.resultsPerStep = new Map<mssql.LoginMigrationStep, mssql.StartLoginMigrationResult>();
|
||||||
this._logins = new Map<string, Login>();
|
this._logins = new Map<string, Login>();
|
||||||
this.SetLoginMigrationSteps();
|
this.setLoginMigrationSteps();
|
||||||
}
|
}
|
||||||
|
|
||||||
public get currentStep(): LoginMigrationStep {
|
public get currentStep(): LoginMigrationStep {
|
||||||
@@ -77,39 +83,29 @@ export class LoginMigrationModel {
|
|||||||
return this._currentStepIdx === this._loginMigrationSteps.length;
|
return this._currentStepIdx === this._loginMigrationSteps.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AddLoginMigrationResults(step: LoginMigrationStep, newResult: mssql.StartLoginMigrationResult): void {
|
public async MigrateLogins(stateMachine: MigrationStateModel): Promise<boolean> {
|
||||||
const exceptionMap = this._getExceptionMapWithNormalizedKeys(newResult.exceptionMap);
|
this.addNewLogins(stateMachine._loginMigrationModel.loginsForMigration.map(row => row.loginName));
|
||||||
this._currentStepIdx = this._loginMigrationSteps.findIndex(s => s === step) + 1;
|
return await this.runLoginMigrationStep(LoginMigrationStep.MigrateLogins, stateMachine);
|
||||||
|
|
||||||
for (const loginName of this._logins.keys()) {
|
// TODO AKMA : emit telemetry
|
||||||
const status = loginName in exceptionMap ? MultiStepState.Failed : MultiStepState.Succeeded;
|
|
||||||
const errors = loginName in exceptionMap ? this._extractErrors(exceptionMap, loginName) : [];
|
|
||||||
this._addStepStateForLogin(loginName, step, status, errors);
|
|
||||||
|
|
||||||
if (this.isMigrationComplete) {
|
|
||||||
const loginStatus = this._didAnyStepFail(loginName) ? MultiStepState.Failed : MultiStepState.Succeeded;
|
|
||||||
this._markLoginStatus(loginName, loginStatus);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReportException(step: LoginMigrationStep, error: any): void {
|
public async EstablishUserMappings(stateMachine: MigrationStateModel): Promise<boolean> {
|
||||||
this._currentStepIdx = this._loginMigrationSteps.findIndex(s => s === step) + 1;
|
return await this.runLoginMigrationStep(LoginMigrationStep.EstablishUserMapping, stateMachine);
|
||||||
|
|
||||||
for (const loginName of this._logins.keys()) {
|
// TODO AKMA : emit telemetry
|
||||||
// Mark current step as failed with the error message and mark remaining messages as canceled
|
|
||||||
let errors = [error.message];
|
|
||||||
this._addStepStateForLogin(loginName, step, MultiStepState.Failed, errors);
|
|
||||||
this._markRemainingSteps(loginName, MultiStepState.Canceled);
|
|
||||||
this._markLoginStatus(loginName, MultiStepState.Failed);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._markMigrationComplete();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async MigrateServerRolesAndSetPermissions(stateMachine: MigrationStateModel): Promise<boolean> {
|
||||||
|
return await this.runLoginMigrationStep(LoginMigrationStep.MigrateServerRolesAndSetPermissions, stateMachine);
|
||||||
|
|
||||||
|
// TODO AKMA : emit telemetry
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public GetLoginMigrationResults(loginName: string): MultiStepResult[] {
|
public GetLoginMigrationResults(loginName: string): MultiStepResult[] {
|
||||||
let loginResults: MultiStepResult[] = [];
|
let loginResults: MultiStepResult[] = [];
|
||||||
let login = this._getLogin(loginName);
|
let login = this.getLogin(loginName);
|
||||||
|
|
||||||
for (const step of this._loginMigrationSteps) {
|
for (const step of this._loginMigrationSteps) {
|
||||||
// The default steps and state will be added if no steps have completed
|
// The default steps and state will be added if no steps have completed
|
||||||
@@ -134,11 +130,7 @@ export class LoginMigrationModel {
|
|||||||
return loginResults;
|
return loginResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AddNewLogins(logins: string[]) {
|
private setLoginMigrationSteps(steps: LoginMigrationStep[] = []) {
|
||||||
logins.forEach(login => this._addNewLogin(login));
|
|
||||||
}
|
|
||||||
|
|
||||||
public SetLoginMigrationSteps(steps: LoginMigrationStep[] = []) {
|
|
||||||
this._loginMigrationSteps = [];
|
this._loginMigrationSteps = [];
|
||||||
|
|
||||||
if (steps.length === 0) {
|
if (steps.length === 0) {
|
||||||
@@ -150,12 +142,144 @@ export class LoginMigrationModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private addNewLogins(logins: string[]) {
|
||||||
|
logins.forEach(login => this.addNewLogin(login));
|
||||||
|
}
|
||||||
|
|
||||||
private _getLogin(loginName: string) {
|
public addLoginMigrationResults(step: LoginMigrationStep, newResult: mssql.StartLoginMigrationResult): void {
|
||||||
|
const exceptionMap = this.getExceptionMapWithNormalizedKeys(newResult.exceptionMap);
|
||||||
|
this._currentStepIdx = this._loginMigrationSteps.findIndex(s => s === step) + 1;
|
||||||
|
|
||||||
|
for (const loginName of this._logins.keys()) {
|
||||||
|
const status = loginName in exceptionMap ? MultiStepState.Failed : MultiStepState.Succeeded;
|
||||||
|
const errors = loginName in exceptionMap ? this.extractErrors(exceptionMap, loginName) : [];
|
||||||
|
this.addStepStateForLogin(loginName, step, status, errors);
|
||||||
|
|
||||||
|
if (this.isMigrationComplete) {
|
||||||
|
const loginStatus = this.didAnyStepFail(loginName) ? MultiStepState.Failed : MultiStepState.Succeeded;
|
||||||
|
this.markLoginStatus(loginName, loginStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateLoginMigrationResults(newResult: contracts.StartLoginMigrationResult): void {
|
||||||
|
if (this.loginMigrationsResult && this.loginMigrationsResult.exceptionMap) {
|
||||||
|
for (var key in newResult.exceptionMap) {
|
||||||
|
this.loginMigrationsResult.exceptionMap[key] = [...this.loginMigrationsResult.exceptionMap[key] || [], newResult.exceptionMap[key]]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.loginMigrationsResult = newResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public reportException(step: LoginMigrationStep, error: any): void {
|
||||||
|
this._currentStepIdx = this._loginMigrationSteps.findIndex(s => s === step) + 1;
|
||||||
|
|
||||||
|
for (const loginName of this._logins.keys()) {
|
||||||
|
// Mark current step as failed with the error message and mark remaining messages as canceled
|
||||||
|
let errors = [error.message];
|
||||||
|
this.addStepStateForLogin(loginName, step, MultiStepState.Failed, errors);
|
||||||
|
this.markRemainingSteps(loginName, MultiStepState.Canceled);
|
||||||
|
this.markLoginStatus(loginName, MultiStepState.Failed);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.markMigrationComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async runMigrateLoginsTask(sourceConnStr: string, targetConnStr: string, stateMachine: MigrationStateModel): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
var response = (await stateMachine.migrationService.migrateLogins(
|
||||||
|
sourceConnStr,
|
||||||
|
targetConnStr,
|
||||||
|
stateMachine._loginMigrationModel.loginsForMigration.map(row => row.loginName),
|
||||||
|
stateMachine._aadDomainName
|
||||||
|
))!;
|
||||||
|
|
||||||
|
this.updateLoginMigrationResults(response);
|
||||||
|
this.addLoginMigrationResults(LoginMigrationStep.MigrateLogins, response);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logError(TelemetryViews.LoginMigrationWizard, 'MigratinLoginsFailed', error);
|
||||||
|
this.reportException(LoginMigrationStep.MigrateLogins, error);
|
||||||
|
this.loginMigrationsError = error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async runEstablishUserMappingTask(sourceConnStr: string, targetConnStr: string, stateMachine: MigrationStateModel): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
var response = (await stateMachine.migrationService.establishUserMapping(
|
||||||
|
sourceConnStr,
|
||||||
|
targetConnStr,
|
||||||
|
stateMachine._loginMigrationModel.loginsForMigration.map(row => row.loginName),
|
||||||
|
stateMachine._aadDomainName
|
||||||
|
))!;
|
||||||
|
|
||||||
|
this.updateLoginMigrationResults(response);
|
||||||
|
this.addLoginMigrationResults(LoginMigrationStep.EstablishUserMapping, response);
|
||||||
|
return false;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logError(TelemetryViews.LoginMigrationWizard, 'EstablishingUserMappingFailed', error);
|
||||||
|
this.reportException(LoginMigrationStep.EstablishUserMapping, error);
|
||||||
|
this.loginMigrationsError = error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async runMigrateServerRolesAndSetPermissionsTask(sourceConnStr: string, targetConnStr: string, stateMachine: MigrationStateModel): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
var response = (await stateMachine.migrationService.migrateServerRolesAndSetPermissions(
|
||||||
|
sourceConnStr,
|
||||||
|
targetConnStr,
|
||||||
|
stateMachine._loginMigrationModel.loginsForMigration.map(row => row.loginName),
|
||||||
|
stateMachine._aadDomainName
|
||||||
|
))!;
|
||||||
|
|
||||||
|
this.updateLoginMigrationResults(response);
|
||||||
|
this.addLoginMigrationResults(LoginMigrationStep.MigrateServerRolesAndSetPermissions, response);
|
||||||
|
return false;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logError(TelemetryViews.LoginMigrationWizard, 'MigratingServerRolesAndSettingPermissionsFailed', error);
|
||||||
|
this.reportException(LoginMigrationStep.MigrateServerRolesAndSetPermissions, error);
|
||||||
|
this.loginMigrationsError = error;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async runLoginMigrationStep(step: LoginMigrationStep, stateMachine: MigrationStateModel): Promise<boolean> {
|
||||||
|
const sourceConnectionString = await getSourceConnectionString();
|
||||||
|
const targetConnectionString = await getTargetConnectionString(
|
||||||
|
stateMachine.targetServerName,
|
||||||
|
stateMachine._targetServerInstance.id,
|
||||||
|
stateMachine._targetUserName,
|
||||||
|
stateMachine._targetPassword,
|
||||||
|
// for login migration, connect to target Azure SQL with true/true
|
||||||
|
// to-do: take as input from the user, should be true/false for DB/MI but true/true for VM
|
||||||
|
true /* encryptConnection */,
|
||||||
|
true /* trustServerCertificate */);
|
||||||
|
|
||||||
|
// Get telemtry values
|
||||||
|
switch (step) {
|
||||||
|
case LoginMigrationStep.MigrateLogins:
|
||||||
|
return await this.runMigrateLoginsTask(sourceConnectionString, targetConnectionString, stateMachine);
|
||||||
|
case LoginMigrationStep.EstablishUserMapping:
|
||||||
|
return await this.runEstablishUserMappingTask(sourceConnectionString, targetConnectionString, stateMachine);
|
||||||
|
case LoginMigrationStep.MigrateServerRolesAndSetPermissions:
|
||||||
|
return await this.runMigrateServerRolesAndSetPermissionsTask(sourceConnectionString, targetConnectionString, stateMachine);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getLogin(loginName: string) {
|
||||||
return this._logins.get(loginName.toLocaleLowerCase());
|
return this._logins.get(loginName.toLocaleLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
private _addNewLogin(loginName: string, status: MultiStepState = MultiStepState.Pending) {
|
private addNewLogin(loginName: string, status: MultiStepState = MultiStepState.Pending) {
|
||||||
let newLogin: Login = {
|
let newLogin: Login = {
|
||||||
loginName: loginName,
|
loginName: loginName,
|
||||||
overallStatus: status,
|
overallStatus: status,
|
||||||
@@ -165,14 +289,14 @@ export class LoginMigrationModel {
|
|||||||
this._logins.set(loginName.toLocaleLowerCase(), newLogin);
|
this._logins.set(loginName.toLocaleLowerCase(), newLogin);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _addStepStateForLogin(loginName: string, step: LoginMigrationStep, stepStatus: MultiStepState, errors: string[] = []) {
|
private addStepStateForLogin(loginName: string, step: LoginMigrationStep, stepStatus: MultiStepState, errors: string[] = []) {
|
||||||
const loginExist = this._logins.has(loginName);
|
const loginExist = this._logins.has(loginName);
|
||||||
|
|
||||||
if (!loginExist) {
|
if (!loginExist) {
|
||||||
this._addNewLogin(loginName, MultiStepState.Running);
|
this.addNewLogin(loginName, MultiStepState.Running);
|
||||||
}
|
}
|
||||||
|
|
||||||
const login = this._getLogin(loginName);
|
const login = this.getLogin(loginName);
|
||||||
|
|
||||||
if (login) {
|
if (login) {
|
||||||
login.overallStatus = MultiStepState.Running;
|
login.overallStatus = MultiStepState.Running;
|
||||||
@@ -188,22 +312,22 @@ export class LoginMigrationModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _markLoginStatus(loginName: string, status: MultiStepState) {
|
private markLoginStatus(loginName: string, status: MultiStepState) {
|
||||||
const loginExist = this._logins.has(loginName);
|
const loginExist = this._logins.has(loginName);
|
||||||
|
|
||||||
if (!loginExist) {
|
if (!loginExist) {
|
||||||
this._addNewLogin(loginName, MultiStepState.Running);
|
this.addNewLogin(loginName, MultiStepState.Running);
|
||||||
}
|
}
|
||||||
|
|
||||||
let login = this._getLogin(loginName);
|
let login = this.getLogin(loginName);
|
||||||
|
|
||||||
if (login) {
|
if (login) {
|
||||||
login.overallStatus = status;
|
login.overallStatus = status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _didAnyStepFail(loginName: string) {
|
private didAnyStepFail(loginName: string) {
|
||||||
const login = this._getLogin(loginName);
|
const login = this.getLogin(loginName);
|
||||||
if (login) {
|
if (login) {
|
||||||
return Object.values(login.statusPerStep).every(status => status === MultiStepState.Failed);
|
return Object.values(login.statusPerStep).every(status => status === MultiStepState.Failed);
|
||||||
}
|
}
|
||||||
@@ -211,25 +335,25 @@ export class LoginMigrationModel {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getExceptionMapWithNormalizedKeys(exceptionMap: ExceptionMap): ExceptionMap {
|
private getExceptionMapWithNormalizedKeys(exceptionMap: ExceptionMap): ExceptionMap {
|
||||||
return Object.keys(exceptionMap).reduce((result: ExceptionMap, key: string) => {
|
return Object.keys(exceptionMap).reduce((result: ExceptionMap, key: string) => {
|
||||||
result[key.toLocaleLowerCase()] = exceptionMap[key];
|
result[key.toLocaleLowerCase()] = exceptionMap[key];
|
||||||
return result;
|
return result;
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _extractErrors(exceptionMap: ExceptionMap, loginName: string): string[] {
|
private extractErrors(exceptionMap: ExceptionMap, loginName: string): string[] {
|
||||||
return exceptionMap[loginName].map((exception: any) => typeof exception.InnerException !== 'undefined'
|
return exceptionMap[loginName].map((exception: any) => typeof exception.InnerException !== 'undefined'
|
||||||
&& exception.InnerException !== null ? exception.InnerException.Message : exception.Message);
|
&& exception.InnerException !== null ? exception.InnerException.Message : exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _markMigrationComplete() {
|
private markMigrationComplete() {
|
||||||
this._currentStepIdx = this._loginMigrationSteps.length;
|
this._currentStepIdx = this._loginMigrationSteps.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _markRemainingSteps(loginName: string, status: MultiStepState) {
|
private markRemainingSteps(loginName: string, status: MultiStepState) {
|
||||||
for (let i = this._currentStepIdx; i < this._loginMigrationSteps.length; i++) {
|
for (let i = this._currentStepIdx; i < this._loginMigrationSteps.length; i++) {
|
||||||
this._addStepStateForLogin(loginName, this._loginMigrationSteps[i], status, []);
|
this.addStepStateForLogin(loginName, this._loginMigrationSteps[i], status, []);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import { v4 as uuidv4 } from 'uuid';
|
|||||||
import { sendSqlMigrationActionEvent, TelemetryAction, TelemetryViews, logError } from '../telemetry';
|
import { sendSqlMigrationActionEvent, TelemetryAction, TelemetryViews, logError } from '../telemetry';
|
||||||
import { hashString, deepClone } from '../api/utils';
|
import { hashString, deepClone } from '../api/utils';
|
||||||
import { SKURecommendationPage } from '../wizard/skuRecommendationPage';
|
import { SKURecommendationPage } from '../wizard/skuRecommendationPage';
|
||||||
import { excludeDatabases, getEncryptConnectionValue, getSourceConnectionId, getSourceConnectionProfile, getSourceConnectionServerInfo, getSourceConnectionString, getSourceConnectionUri, getTargetConnectionString, getTrustServerCertificateValue, LoginTableInfo, SourceDatabaseInfo, TargetDatabaseInfo } from '../api/sqlUtils';
|
import { excludeDatabases, getEncryptConnectionValue, getSourceConnectionId, getSourceConnectionProfile, getSourceConnectionServerInfo, getSourceConnectionString, getSourceConnectionUri, getTrustServerCertificateValue, SourceDatabaseInfo, TargetDatabaseInfo } from '../api/sqlUtils';
|
||||||
import { LoginMigrationModel, LoginMigrationStep } from './loginMigrationModel';
|
import { LoginMigrationModel } from './loginMigrationModel';
|
||||||
import { TdeMigrationDbResult, TdeMigrationModel } from './tdeModels';
|
import { TdeMigrationDbResult, TdeMigrationModel } from './tdeModels';
|
||||||
import { NetworkInterfaceModel } from '../api/dataModels/azure/networkInterfaceModel';
|
import { NetworkInterfaceModel } from '../api/dataModels/azure/networkInterfaceModel';
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
@@ -253,10 +253,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
public _perfDataCollectionErrors!: string[];
|
public _perfDataCollectionErrors!: string[];
|
||||||
public _perfDataCollectionIsCollecting!: boolean;
|
public _perfDataCollectionIsCollecting!: boolean;
|
||||||
|
|
||||||
public _loginsForMigration!: LoginTableInfo[];
|
|
||||||
public _aadDomainName!: string;
|
public _aadDomainName!: string;
|
||||||
public _loginMigrationsResult!: contracts.StartLoginMigrationResult;
|
|
||||||
public _loginMigrationsError: any;
|
|
||||||
public _loginMigrationModel: LoginMigrationModel;
|
public _loginMigrationModel: LoginMigrationModel;
|
||||||
|
|
||||||
public readonly _refreshGetSkuRecommendationIntervalInMinutes = 10;
|
public readonly _refreshGetSkuRecommendationIntervalInMinutes = 10;
|
||||||
@@ -523,142 +520,6 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
return this._skuRecommendationResults;
|
return this._skuRecommendationResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setTargetServerName(): void {
|
|
||||||
switch (this._targetType) {
|
|
||||||
case MigrationTargetType.SQLMI:
|
|
||||||
const sqlMi = this._targetServerInstance as SqlManagedInstance;
|
|
||||||
this._targetServerName = sqlMi.properties.fullyQualifiedDomainName;
|
|
||||||
case MigrationTargetType.SQLDB:
|
|
||||||
const sqlDb = this._targetServerInstance as AzureSqlDatabaseServer;
|
|
||||||
this._targetServerName = sqlDb.properties.fullyQualifiedDomainName;
|
|
||||||
case MigrationTargetType.SQLVM:
|
|
||||||
// For sqlvm, we need to use ip address from the network interface to connect to the server
|
|
||||||
const sqlVm = this._targetServerInstance as SqlVMServer;
|
|
||||||
const networkInterfaces = Array.from(sqlVm.networkInterfaces.values());
|
|
||||||
this._targetServerName = NetworkInterfaceModel.getIpAddress(networkInterfaces);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public get targetServerName(): string {
|
|
||||||
// If the target server name is not already set, return it
|
|
||||||
if (!this._targetServerName) {
|
|
||||||
this.setTargetServerName();
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._targetServerName;
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateLoginMigrationResults(newResult: contracts.StartLoginMigrationResult): void {
|
|
||||||
if (this._loginMigrationsResult && this._loginMigrationsResult.exceptionMap) {
|
|
||||||
for (var key in newResult.exceptionMap) {
|
|
||||||
this._loginMigrationsResult.exceptionMap[key] = [...this._loginMigrationsResult.exceptionMap[key] || [], newResult.exceptionMap[key]]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._loginMigrationsResult = newResult;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async migrateLogins(): Promise<Boolean> {
|
|
||||||
try {
|
|
||||||
this._loginMigrationModel.AddNewLogins(this._loginsForMigration.map(row => row.loginName));
|
|
||||||
|
|
||||||
const sourceConnectionString = await getSourceConnectionString();
|
|
||||||
const targetConnectionString = await getTargetConnectionString(
|
|
||||||
this.targetServerName,
|
|
||||||
this._targetServerInstance.id,
|
|
||||||
this._targetUserName,
|
|
||||||
this._targetPassword,
|
|
||||||
// for login migration, connect to target Azure SQL with true/true
|
|
||||||
// to-do: take as input from the user, should be true/false for DB/MI but true/true for VM
|
|
||||||
true /* encryptConnection */,
|
|
||||||
true /* trustServerCertificate */);
|
|
||||||
|
|
||||||
var response = (await this.migrationService.migrateLogins(
|
|
||||||
sourceConnectionString,
|
|
||||||
targetConnectionString,
|
|
||||||
this._loginsForMigration.map(row => row.loginName),
|
|
||||||
this._aadDomainName
|
|
||||||
))!;
|
|
||||||
|
|
||||||
this.updateLoginMigrationResults(response);
|
|
||||||
this._loginMigrationModel.AddLoginMigrationResults(LoginMigrationStep.MigrateLogins, response);
|
|
||||||
} catch (error) {
|
|
||||||
logError(TelemetryViews.LoginMigrationWizard, 'StartLoginMigrationFailed', error);
|
|
||||||
this._loginMigrationModel.ReportException(LoginMigrationStep.MigrateLogins, error);
|
|
||||||
this._loginMigrationsError = error;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO AKMA : emit telemetry
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async establishUserMappings(): Promise<Boolean> {
|
|
||||||
try {
|
|
||||||
const sourceConnectionString = await getSourceConnectionString();
|
|
||||||
const targetConnectionString = await getTargetConnectionString(
|
|
||||||
this.targetServerName,
|
|
||||||
this._targetServerInstance.id,
|
|
||||||
this._targetUserName,
|
|
||||||
this._targetPassword,
|
|
||||||
// for login migration, connect to target Azure SQL with true/true
|
|
||||||
// to-do: take as input from the user, should be true/false for DB/MI but true/true for VM
|
|
||||||
true /* encryptConnection */,
|
|
||||||
true /* trustServerCertificate */);
|
|
||||||
|
|
||||||
var response = (await this.migrationService.establishUserMapping(
|
|
||||||
sourceConnectionString,
|
|
||||||
targetConnectionString,
|
|
||||||
this._loginsForMigration.map(row => row.loginName),
|
|
||||||
this._aadDomainName
|
|
||||||
))!;
|
|
||||||
|
|
||||||
this.updateLoginMigrationResults(response);
|
|
||||||
this._loginMigrationModel.AddLoginMigrationResults(LoginMigrationStep.EstablishUserMapping, response);
|
|
||||||
} catch (error) {
|
|
||||||
logError(TelemetryViews.LoginMigrationWizard, 'StartLoginMigrationFailed', error);
|
|
||||||
this._loginMigrationModel.ReportException(LoginMigrationStep.MigrateLogins, error);
|
|
||||||
this._loginMigrationsError = error;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO AKMA : emit telemetry
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async migrateServerRolesAndSetPermissions(): Promise<Boolean> {
|
|
||||||
try {
|
|
||||||
const sourceConnectionString = await getSourceConnectionString();
|
|
||||||
const targetConnectionString = await getTargetConnectionString(
|
|
||||||
this.targetServerName,
|
|
||||||
this._targetServerInstance.id,
|
|
||||||
this._targetUserName,
|
|
||||||
this._targetPassword,
|
|
||||||
// for login migration, connect to target Azure SQL with true/true
|
|
||||||
// to-do: take as input from the user, should be true/false for DB/MI but true/true for VM
|
|
||||||
true /* encryptConnection */,
|
|
||||||
true /* trustServerCertificate */);
|
|
||||||
|
|
||||||
var response = (await this.migrationService.migrateServerRolesAndSetPermissions(
|
|
||||||
sourceConnectionString,
|
|
||||||
targetConnectionString,
|
|
||||||
this._loginsForMigration.map(row => row.loginName),
|
|
||||||
this._aadDomainName
|
|
||||||
))!;
|
|
||||||
|
|
||||||
this.updateLoginMigrationResults(response);
|
|
||||||
this._loginMigrationModel.AddLoginMigrationResults(LoginMigrationStep.MigrateServerRolesAndSetPermissions, response);
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
logError(TelemetryViews.LoginMigrationWizard, 'StartLoginMigrationFailed', error);
|
|
||||||
this._loginMigrationModel.ReportException(LoginMigrationStep.MigrateLogins, error);
|
|
||||||
this._loginMigrationsError = error;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO AKMA : emit telemetry
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async generateSkuRecommendationTelemetry(): Promise<void> {
|
private async generateSkuRecommendationTelemetry(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
@@ -1474,6 +1335,31 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
public get isWindowsAuthMigrationSupported(): boolean {
|
public get isWindowsAuthMigrationSupported(): boolean {
|
||||||
return this._targetType === MigrationTargetType.SQLMI;
|
return this._targetType === MigrationTargetType.SQLMI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setTargetServerName(): void {
|
||||||
|
switch (this._targetType) {
|
||||||
|
case MigrationTargetType.SQLMI:
|
||||||
|
const sqlMi = this._targetServerInstance as SqlManagedInstance;
|
||||||
|
this._targetServerName = sqlMi.properties.fullyQualifiedDomainName;
|
||||||
|
case MigrationTargetType.SQLDB:
|
||||||
|
const sqlDb = this._targetServerInstance as AzureSqlDatabaseServer;
|
||||||
|
this._targetServerName = sqlDb.properties.fullyQualifiedDomainName;
|
||||||
|
case MigrationTargetType.SQLVM:
|
||||||
|
// For sqlvm, we need to use ip address from the network interface to connect to the server
|
||||||
|
const sqlVm = this._targetServerInstance as SqlVMServer;
|
||||||
|
const networkInterfaces = Array.from(sqlVm.networkInterfaces.values());
|
||||||
|
this._targetServerName = NetworkInterfaceModel.getIpAddress(networkInterfaces);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get targetServerName(): string {
|
||||||
|
// If the target server name is not already set, return it
|
||||||
|
if (!this._targetServerName) {
|
||||||
|
this.setTargetServerName();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._targetServerName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ServerAssessment {
|
export interface ServerAssessment {
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ export class LoginMigrationStatusPage extends MigrationWizardPage {
|
|||||||
this.wizard.message = {
|
this.wizard.message = {
|
||||||
text: constants.LOGIN_MIGRATIONS_FAILED,
|
text: constants.LOGIN_MIGRATIONS_FAILED,
|
||||||
level: azdata.window.MessageLevel.Error,
|
level: azdata.window.MessageLevel.Error,
|
||||||
description: constants.LOGIN_MIGRATIONS_ERROR(this.migrationStateModel._loginMigrationsError.message),
|
description: constants.LOGIN_MIGRATIONS_ERROR(this.migrationStateModel._loginMigrationModel.loginMigrationsError.message),
|
||||||
};
|
};
|
||||||
|
|
||||||
this._progressLoader.loading = false;
|
this._progressLoader.loading = false;
|
||||||
@@ -292,7 +292,7 @@ export class LoginMigrationStatusPage extends MigrationWizardPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _loadMigratingLoginsList(stateMachine: MigrationStateModel): Promise<void> {
|
private async _loadMigratingLoginsList(stateMachine: MigrationStateModel): Promise<void> {
|
||||||
const loginList = stateMachine._loginsForMigration || [];
|
const loginList = stateMachine._loginMigrationModel.loginsForMigration || [];
|
||||||
loginList.sort((a, b) => a.loginName.localeCompare(b.loginName));
|
loginList.sort((a, b) => a.loginName.localeCompare(b.loginName));
|
||||||
|
|
||||||
this._loginsTableValues = loginList.map(login => {
|
this._loginsTableValues = loginList.map(login => {
|
||||||
@@ -300,13 +300,13 @@ export class LoginMigrationStatusPage extends MigrationWizardPage {
|
|||||||
|
|
||||||
var status = LoginMigrationStatusCodes.InProgress;
|
var status = LoginMigrationStatusCodes.InProgress;
|
||||||
var title = constants.LOGIN_MIGRATION_STATUS_IN_PROGRESS;
|
var title = constants.LOGIN_MIGRATION_STATUS_IN_PROGRESS;
|
||||||
if (stateMachine._loginMigrationsError) {
|
if (stateMachine._loginMigrationModel.loginMigrationsError) {
|
||||||
status = LoginMigrationStatusCodes.Failed;
|
status = LoginMigrationStatusCodes.Failed;
|
||||||
title = constants.LOGIN_MIGRATION_STATUS_FAILED;
|
title = constants.LOGIN_MIGRATION_STATUS_FAILED;
|
||||||
} else if (stateMachine._loginMigrationsResult) {
|
} else if (stateMachine._loginMigrationModel.loginMigrationsResult) {
|
||||||
status = LoginMigrationStatusCodes.Succeeded;
|
status = LoginMigrationStatusCodes.Succeeded;
|
||||||
title = constants.LOGIN_MIGRATION_STATUS_SUCCEEDED;
|
title = constants.LOGIN_MIGRATION_STATUS_SUCCEEDED;
|
||||||
var didLoginFail = Object.keys(stateMachine._loginMigrationsResult.exceptionMap).some(key => key.toLocaleLowerCase() === loginName.toLocaleLowerCase());
|
var didLoginFail = Object.keys(stateMachine._loginMigrationModel.loginMigrationsResult.exceptionMap).some(key => key.toLocaleLowerCase() === loginName.toLocaleLowerCase());
|
||||||
if (didLoginFail) {
|
if (didLoginFail) {
|
||||||
status = LoginMigrationStatusCodes.Failed;
|
status = LoginMigrationStatusCodes.Failed;
|
||||||
title = constants.LOGIN_MIGRATION_STATUS_FAILED;
|
title = constants.LOGIN_MIGRATION_STATUS_FAILED;
|
||||||
@@ -352,7 +352,7 @@ export class LoginMigrationStatusPage extends MigrationWizardPage {
|
|||||||
'value': constants.STARTING_LOGIN_MIGRATION
|
'value': constants.STARTING_LOGIN_MIGRATION
|
||||||
});
|
});
|
||||||
|
|
||||||
var result = await this.migrationStateModel.migrateLogins();
|
var result = await this.migrationStateModel._loginMigrationModel.MigrateLogins(this.migrationStateModel);
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
await this._migrationProgressDetails.updateProperties({
|
await this._migrationProgressDetails.updateProperties({
|
||||||
@@ -366,7 +366,7 @@ export class LoginMigrationStatusPage extends MigrationWizardPage {
|
|||||||
'value': constants.ESTABLISHING_USER_MAPPINGS
|
'value': constants.ESTABLISHING_USER_MAPPINGS
|
||||||
});
|
});
|
||||||
|
|
||||||
result = await this.migrationStateModel.establishUserMappings();
|
result = await this.migrationStateModel._loginMigrationModel.EstablishUserMappings(this.migrationStateModel);
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
await this._migrationProgressDetails.updateProperties({
|
await this._migrationProgressDetails.updateProperties({
|
||||||
@@ -380,7 +380,7 @@ export class LoginMigrationStatusPage extends MigrationWizardPage {
|
|||||||
'value': constants.MIGRATING_SERVER_ROLES_AND_SET_PERMISSIONS
|
'value': constants.MIGRATING_SERVER_ROLES_AND_SET_PERMISSIONS
|
||||||
});
|
});
|
||||||
|
|
||||||
result = await this.migrationStateModel.migrateServerRolesAndSetPermissions();
|
result = await this.migrationStateModel._loginMigrationModel.MigrateServerRolesAndSetPermissions(this.migrationStateModel);
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
await this._migrationProgressDetails.updateProperties({
|
await this._migrationProgressDetails.updateProperties({
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ export class LoginSelectorPage extends MigrationWizardPage {
|
|||||||
await this._loadLoginList(false);
|
await this._loadLoginList(false);
|
||||||
|
|
||||||
// load unfiltered table list and pre-select list of logins saved in state
|
// load unfiltered table list and pre-select list of logins saved in state
|
||||||
await this._filterTableList('', this.migrationStateModel._loginsForMigration);
|
await this._filterTableList('', this.migrationStateModel._loginMigrationModel.loginsForMigration);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async onPageLeave(): Promise<void> {
|
public async onPageLeave(): Promise<void> {
|
||||||
@@ -115,7 +115,7 @@ export class LoginSelectorPage extends MigrationWizardPage {
|
|||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this._disposables.push(
|
this._disposables.push(
|
||||||
resourceSearchBox.onTextChanged(value => this._filterTableList(value, this.migrationStateModel._loginsForMigration || [])));
|
resourceSearchBox.onTextChanged(value => this._filterTableList(value, this.migrationStateModel._loginMigrationModel.loginsForMigration || [])));
|
||||||
|
|
||||||
const searchContainer = this._view.modelBuilder.divContainer().withItems([resourceSearchBox]).withProps({
|
const searchContainer = this._view.modelBuilder.divContainer().withItems([resourceSearchBox]).withProps({
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
@@ -326,7 +326,7 @@ export class LoginSelectorPage extends MigrationWizardPage {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// load unfiltered table list and pre-select list of logins saved in state
|
// load unfiltered table list and pre-select list of logins saved in state
|
||||||
await this._filterTableList('', this.migrationStateModel._loginsForMigration);
|
await this._filterTableList('', this.migrationStateModel._loginMigrationModel.loginsForMigration);
|
||||||
|
|
||||||
const flex = view.modelBuilder.flexContainer().withLayout({
|
const flex = view.modelBuilder.flexContainer().withLayout({
|
||||||
flexFlow: 'column',
|
flexFlow: 'column',
|
||||||
@@ -416,7 +416,7 @@ export class LoginSelectorPage extends MigrationWizardPage {
|
|||||||
|
|
||||||
private async _loadLoginList(runQuery: boolean = true): Promise<void> {
|
private async _loadLoginList(runQuery: boolean = true): Promise<void> {
|
||||||
const stateMachine: MigrationStateModel = this.migrationStateModel;
|
const stateMachine: MigrationStateModel = this.migrationStateModel;
|
||||||
const selectedLogins: LoginTableInfo[] = stateMachine._loginsForMigration || [];
|
const selectedLogins: LoginTableInfo[] = stateMachine._loginMigrationModel.loginsForMigration || [];
|
||||||
|
|
||||||
// Get source logins if caller asked us to or if we haven't collected in the past
|
// Get source logins if caller asked us to or if we haven't collected in the past
|
||||||
if (runQuery || !stateMachine._loginMigrationModel.collectedSourceLogins) {
|
if (runQuery || !stateMachine._loginMigrationModel.collectedSourceLogins) {
|
||||||
@@ -488,7 +488,7 @@ export class LoginSelectorPage extends MigrationWizardPage {
|
|||||||
await utils.updateControlDisplay(this._aadDomainNameContainer, hasSelectedWindowsLogins);
|
await utils.updateControlDisplay(this._aadDomainNameContainer, hasSelectedWindowsLogins);
|
||||||
await this._loginSelectorTable.updateProperty("height", hasSelectedWindowsLogins ? 600 : 650);
|
await this._loginSelectorTable.updateProperty("height", hasSelectedWindowsLogins ? 600 : 650);
|
||||||
|
|
||||||
this.migrationStateModel._loginsForMigration = selectedLogins;
|
this.migrationStateModel._loginMigrationModel.loginsForMigration = selectedLogins;
|
||||||
this.updateNextButton();
|
this.updateNextButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,7 +496,7 @@ export class LoginSelectorPage extends MigrationWizardPage {
|
|||||||
// Only uppdate next label if we are currently on this page
|
// Only uppdate next label if we are currently on this page
|
||||||
if (this._isCurrentPage) {
|
if (this._isCurrentPage) {
|
||||||
this.wizard.nextButton.label = constants.LOGIN_MIGRATE_BUTTON_TEXT;
|
this.wizard.nextButton.label = constants.LOGIN_MIGRATE_BUTTON_TEXT;
|
||||||
this.wizard.nextButton.enabled = this.migrationStateModel?._loginsForMigration?.length > 0;
|
this.wizard.nextButton.enabled = this.migrationStateModel?._loginMigrationModel.loginsForMigration?.length > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user