mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-25 09:35:37 -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:
@@ -8,12 +8,12 @@ import * as azdata from 'azdata';
|
||||
import * as azurecore from 'azurecore';
|
||||
import * as constants from '../constants/strings';
|
||||
import { getSessionIdHeader } from './utils';
|
||||
import { ProvisioningState } from '../models/migrationLocalStorage';
|
||||
import { URL } from 'url';
|
||||
|
||||
const ARM_MGMT_API_VERSION = '2021-04-01';
|
||||
const SQL_VM_API_VERSION = '2021-11-01-preview';
|
||||
const SQL_MI_API_VERSION = '2021-11-01-preview';
|
||||
const SQL_SQLDB_API_VERSION = '2021-11-01-preview';
|
||||
const DMSV2_API_VERSION = '2022-03-30-preview';
|
||||
|
||||
async function getAzureCoreAPI(): Promise<azurecore.IExtension> {
|
||||
@@ -93,6 +93,100 @@ export async function getAvailableSqlServers(account: azdata.Account, subscripti
|
||||
return result.resources;
|
||||
}
|
||||
|
||||
export interface SKU {
|
||||
name: string,
|
||||
tier: 'GeneralPurpose' | 'BusinessCritical',
|
||||
family: string,
|
||||
capacity: number,
|
||||
}
|
||||
|
||||
export interface AzureSqlDatabase {
|
||||
id: string,
|
||||
name: string,
|
||||
location: string,
|
||||
tags: any,
|
||||
type: string,
|
||||
sku: SKU,
|
||||
kind: string,
|
||||
properties: {
|
||||
collation: string,
|
||||
maxSizeBytes: number,
|
||||
status: string,
|
||||
databaseId: string,
|
||||
creationDate: string,
|
||||
currentServiceObjectiveName: string,
|
||||
requestedServiceObjectiveName: string,
|
||||
defaultSecondaryLocation: string,
|
||||
catalogCollation: string,
|
||||
zoneRedundant: boolean,
|
||||
earliestRestoreDate: string,
|
||||
readScale: string,
|
||||
currentSku: SKU,
|
||||
currentBackupStorageRedundancy: string,
|
||||
requestedBackupStorageRedundancy: string,
|
||||
maintenanceConfigurationId: string,
|
||||
isLedgerOn: boolean
|
||||
isInfraEncryptionEnabled: boolean,
|
||||
licenseType: string,
|
||||
maxLogSizeBytes: number,
|
||||
},
|
||||
}
|
||||
|
||||
export interface ServerAdministrators {
|
||||
administratorType: string,
|
||||
azureADOnlyAuthentication: boolean,
|
||||
login: string,
|
||||
principalType: string,
|
||||
sid: string,
|
||||
tenantId: string,
|
||||
}
|
||||
|
||||
export interface PrivateEndpointProperty {
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export interface PrivateLinkServiceConnectionStateProperty {
|
||||
status: string;
|
||||
description: string;
|
||||
readonly actionsRequired?: string;
|
||||
}
|
||||
|
||||
export interface PrivateEndpointConnectionProperties {
|
||||
groupIds: string[];
|
||||
privateEndpoint?: PrivateEndpointProperty;
|
||||
privateLinkServiceConnectionState?: PrivateLinkServiceConnectionStateProperty;
|
||||
readonly provisioningState?: string;
|
||||
}
|
||||
|
||||
export interface ServerPrivateEndpointConnection {
|
||||
readonly id?: string;
|
||||
readonly properties?: PrivateEndpointConnectionProperties;
|
||||
}
|
||||
|
||||
export interface AzureSqlDatabaseServer {
|
||||
id: string,
|
||||
name: string,
|
||||
kind: string,
|
||||
location: string,
|
||||
tags?: { [propertyName: string]: string; };
|
||||
type: string,
|
||||
// sku: SKU,
|
||||
// subscriptionId: string,
|
||||
// tenantId: string,
|
||||
// fullName: string,
|
||||
properties: {
|
||||
administratorLogin: string,
|
||||
administrators: ServerAdministrators,
|
||||
fullyQualifiedDomainName: string,
|
||||
minimalTlsVersion: string,
|
||||
privateEndpointConnections: ServerPrivateEndpointConnection[],
|
||||
publicNetworkAccess: string,
|
||||
restrictOutboundNetworkAccess: string,
|
||||
state: string,
|
||||
version: string,
|
||||
},
|
||||
}
|
||||
|
||||
export type SqlVMServer = {
|
||||
properties: {
|
||||
virtualMachineResourceId: string,
|
||||
@@ -108,6 +202,31 @@ export type SqlVMServer = {
|
||||
tenantId: string,
|
||||
subscriptionId: string
|
||||
};
|
||||
|
||||
export async function getAvailableSqlDatabaseServers(account: azdata.Account, subscription: Subscription): Promise<AzureSqlDatabaseServer[]> {
|
||||
const api = await getAzureCoreAPI();
|
||||
const path = encodeURI(`/subscriptions/${subscription.id}/providers/Microsoft.Sql/servers?api-version=${SQL_SQLDB_API_VERSION}`);
|
||||
const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint;
|
||||
const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host);
|
||||
if (response.errors.length > 0) {
|
||||
throw new Error(response.errors.toString());
|
||||
}
|
||||
sortResourceArrayByName(response.response.data.value);
|
||||
return response.response.data.value;
|
||||
}
|
||||
|
||||
export async function getAvailableSqlDatabases(account: azdata.Account, subscription: Subscription, resourceGroupName: string, serverName: string): Promise<AzureSqlDatabase[]> {
|
||||
const api = await getAzureCoreAPI();
|
||||
const path = encodeURI(`/subscriptions/${subscription.id}/resourceGroups/${resourceGroupName}/providers/Microsoft.Sql/servers/${serverName}/databases?api-version=${SQL_SQLDB_API_VERSION}`);
|
||||
const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint;
|
||||
const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host);
|
||||
if (response.errors.length > 0) {
|
||||
throw new Error(response.errors.toString());
|
||||
}
|
||||
sortResourceArrayByName(response.response.data.value);
|
||||
return response.response.data.value;
|
||||
}
|
||||
|
||||
export async function getAvailableSqlVMs(account: azdata.Account, subscription: Subscription): Promise<SqlVMServer[]> {
|
||||
const api = await getAzureCoreAPI();
|
||||
const path = encodeURI(`/subscriptions/${subscription.id}/providers/Microsoft.SqlVirtualMachine/sqlVirtualMachines?api-version=${SQL_VM_API_VERSION}`);
|
||||
@@ -203,10 +322,10 @@ export async function getSqlMigrationServices(account: azdata.Account, subscript
|
||||
export async function createSqlMigrationService(account: azdata.Account, subscription: Subscription, resourceGroupName: string, regionName: string, sqlMigrationServiceName: string, sessionId: string): Promise<SqlMigrationService> {
|
||||
const api = await getAzureCoreAPI();
|
||||
const path = encodeURI(`/subscriptions/${subscription.id}/resourceGroups/${resourceGroupName}/providers/Microsoft.DataMigration/sqlMigrationServices/${sqlMigrationServiceName}?api-version=${DMSV2_API_VERSION}`);
|
||||
const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint;
|
||||
const requestBody = {
|
||||
'location': regionName
|
||||
};
|
||||
const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint;
|
||||
const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.PUT, requestBody, true, host, getSessionIdHeader(sessionId));
|
||||
if (response.errors.length > 0) {
|
||||
throw new Error(response.errors.toString());
|
||||
@@ -219,9 +338,9 @@ export async function createSqlMigrationService(account: azdata.Account, subscri
|
||||
for (i = 0; i < maxRetry; i++) {
|
||||
const asyncResponse = await api.makeAzureRestRequest(account, subscription, asyncPath, azurecore.HttpRequestMethod.GET, undefined, true, host);
|
||||
const creationStatus = asyncResponse.response.data.status;
|
||||
if (creationStatus === ProvisioningState.Succeeded) {
|
||||
if (creationStatus === constants.ProvisioningState.Succeeded) {
|
||||
break;
|
||||
} else if (creationStatus === ProvisioningState.Failed) {
|
||||
} else if (creationStatus === constants.ProvisioningState.Failed) {
|
||||
throw new Error(asyncResponse.errors.toString());
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, 5000)); //adding 5 sec delay before getting creation status
|
||||
@@ -287,7 +406,15 @@ export async function getSqlMigrationServiceMonitoringData(account: azdata.Accou
|
||||
return response.response.data;
|
||||
}
|
||||
|
||||
export async function startDatabaseMigration(account: azdata.Account, subscription: Subscription, regionName: string, targetServer: SqlManagedInstance | SqlVMServer, targetDatabaseName: string, requestBody: StartDatabaseMigrationRequest, sessionId: string): Promise<StartDatabaseMigrationResponse> {
|
||||
export async function startDatabaseMigration(
|
||||
account: azdata.Account,
|
||||
subscription: Subscription,
|
||||
regionName: string,
|
||||
targetServer: SqlManagedInstance | SqlVMServer | AzureSqlDatabaseServer,
|
||||
targetDatabaseName: string,
|
||||
requestBody: StartDatabaseMigrationRequest,
|
||||
sessionId: string): Promise<StartDatabaseMigrationResponse> {
|
||||
|
||||
const api = await getAzureCoreAPI();
|
||||
const path = encodeURI(`${targetServer.id}/providers/Microsoft.DataMigration/databaseMigrations/${targetDatabaseName}?api-version=${DMSV2_API_VERSION}`);
|
||||
const host = api.getProviderMetadataForAccount(account).settings.armResource?.endpoint;
|
||||
@@ -438,10 +565,10 @@ export interface SqlMigrationServiceProperties {
|
||||
}
|
||||
|
||||
export interface SqlMigrationService {
|
||||
properties: SqlMigrationServiceProperties;
|
||||
location: string;
|
||||
id: string;
|
||||
name: string;
|
||||
location: string;
|
||||
properties: SqlMigrationServiceProperties;
|
||||
error: {
|
||||
code: string,
|
||||
message: string
|
||||
@@ -485,14 +612,29 @@ export interface StartDatabaseMigrationRequest {
|
||||
},
|
||||
sourceLocation?: SourceLocation
|
||||
},
|
||||
sourceSqlConnection: {
|
||||
authentication: string,
|
||||
targetSqlConnection?: {
|
||||
dataSource: string,
|
||||
username: string,
|
||||
password: string
|
||||
authentication: string,
|
||||
userName: string,
|
||||
password: string,
|
||||
encryptConnection?: boolean,
|
||||
trustServerCertificate?: boolean,
|
||||
},
|
||||
sourceSqlConnection: {
|
||||
dataSource: string,
|
||||
authentication: string,
|
||||
userName: string,
|
||||
password: string,
|
||||
encryptConnection?: boolean,
|
||||
trustServerCertificate?: boolean,
|
||||
},
|
||||
sqlDataCopyThresholds?: {
|
||||
cidxrowthreshold: number,
|
||||
cidxkbsthreshold: number,
|
||||
},
|
||||
tableList?: string[],
|
||||
scope: string,
|
||||
offlineConfiguration: OfflineConfiguration,
|
||||
offlineConfiguration?: OfflineConfiguration,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -503,10 +645,10 @@ export interface StartDatabaseMigrationResponse {
|
||||
}
|
||||
|
||||
export interface DatabaseMigration {
|
||||
properties: DatabaseMigrationProperties;
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
properties: DatabaseMigrationProperties;
|
||||
}
|
||||
|
||||
export interface DatabaseMigrationProperties {
|
||||
@@ -525,6 +667,7 @@ export interface DatabaseMigrationProperties {
|
||||
backupConfiguration: BackupConfiguration;
|
||||
offlineConfiguration: OfflineConfiguration;
|
||||
migrationFailureError: ErrorInfo;
|
||||
tableList: string[];
|
||||
}
|
||||
|
||||
export interface MigrationStatusDetails {
|
||||
@@ -543,6 +686,7 @@ export interface MigrationStatusDetails {
|
||||
pendingLogBackupsCount: number;
|
||||
invalidFiles: string[];
|
||||
listOfCopyProgressDetails: CopyProgressDetail[];
|
||||
sqlDataCopyErrors: string[];
|
||||
}
|
||||
|
||||
export interface CopyProgressDetail {
|
||||
|
||||
Reference in New Issue
Block a user