mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-18 09:35:39 -05:00
* add new migration details * move migraiton target type enum to utils * address review feedback, refectore, text update * fix variable name * limit and filter migrations list to mi/vm/db
321 lines
12 KiB
TypeScript
321 lines
12 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import * as azdata from 'azdata';
|
|
import { AzureResourceKind, DatabaseMigration } from '../api/azure';
|
|
import { DefaultSettingValue, MigrationTargetType } from '../api/utils';
|
|
import { FileStorageType, MigrationMode } from '../models/stateMachine';
|
|
import * as loc from './strings';
|
|
|
|
export enum SQLTargetAssetType {
|
|
SQLMI = 'microsoft.sql/managedinstances',
|
|
SQLVM = 'Microsoft.SqlVirtualMachine/sqlVirtualMachines',
|
|
SQLDB = 'Microsoft.Sql/servers',
|
|
}
|
|
|
|
export const FileStorageTypeCodes = {
|
|
FileShare: "FileShare",
|
|
AzureBlob: "AzureBlob",
|
|
None: "None",
|
|
};
|
|
|
|
export const BackupTypeCodes = {
|
|
// Type of backup. The values match the output of a RESTORE HEADERONLY query.
|
|
Unknown: "Unknown",
|
|
Database: "Database",
|
|
TransactionLog: "TransactionLog",
|
|
File: "File",
|
|
DifferentialDatabase: "DifferentialDatabase",
|
|
DifferentialFile: "DifferentialFile",
|
|
Partial: "Partial",
|
|
DifferentialPartial: "DifferentialPartial",
|
|
};
|
|
|
|
export const InternalManagedDatabaseRestoreDetailsBackupSetStatusCodes = {
|
|
None: "None",
|
|
Skipped: "Skipped",
|
|
Queued: "Queued",
|
|
Restoring: "Restoring",
|
|
Restored: "Restored",
|
|
};
|
|
|
|
export const InternalManagedDatabaseRestoreDetailsStatusCodes = {
|
|
None: "None", // Something went wrong most likely.
|
|
Initializing: "Initializing", // Restore is initializing.
|
|
NotStarted: "NotStarted", // Restore not started
|
|
SearchingBackups: "SearchingBackups", // Searching for backups
|
|
Restoring: "Restoring", // Restore is in progress
|
|
RestorePaused: "RestorePaused", // Restore is paused
|
|
RestoreCompleted: "RestoreCompleted", // Restore completed for all found log, but there may have more logs coming
|
|
Waiting: "Waiting", // Waiting for new files to be uploaded or for Complete restore signal.
|
|
CompletingMigration: "CompletingMigration", // Completing migration
|
|
Cancelled: "Cancelled", // Restore cancelled.
|
|
Failed: "Failed", // Restore failed.
|
|
Completed: "Completed", // Database is restored and recovery is complete.
|
|
Blocked: "Blocked", // Restore is temporarily blocked: "", awaiting for user to mitigate the issue.
|
|
};
|
|
|
|
export const ParallelCopyTypeCodes = {
|
|
None: 'None',
|
|
DynamicRange: 'DynamicRange',
|
|
PhysicalPartitionsOfTable: 'PhysicalPartitionsOfTable',
|
|
};
|
|
|
|
export const PipelineStatusCodes = {
|
|
// status codes: 'PreparingForCopy' | 'Copying' | 'CopyFinished' | 'RebuildingIndexes' | 'Succeeded' | 'Failed' | 'Canceled',
|
|
PreparingForCopy: 'PreparingForCopy',
|
|
Copying: 'Copying',
|
|
CopyFinished: 'CopyFinished',
|
|
RebuildingIndexes: 'RebuildingIndexes',
|
|
Succeeded: 'Succeeded',
|
|
Failed: 'Failed',
|
|
Canceled: 'Canceled',
|
|
|
|
// legacy status codes
|
|
Queued: 'Queued',
|
|
InProgress: 'InProgress',
|
|
Cancelled: 'Cancelled',
|
|
};
|
|
|
|
export const LoginMigrationStatusCodes = {
|
|
// status codes: 'InProgress' | 'Failed' | 'Succeeded'
|
|
InProgress: 'InProgress',
|
|
Succeeded: 'Succeeded',
|
|
Failed: 'Failed',
|
|
};
|
|
|
|
export const ValidationErrorCodes = {
|
|
// TODO: adding other error codes for troubleshooting
|
|
SqlInfoValidationFailed: '2056'
|
|
};
|
|
|
|
const _dateFormatter = new Intl.DateTimeFormat(
|
|
undefined, {
|
|
year: 'numeric',
|
|
month: '2-digit',
|
|
day: '2-digit',
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
second: '2-digit'
|
|
});
|
|
|
|
const _numberFormatter = new Intl.NumberFormat(
|
|
undefined, {
|
|
style: 'decimal',
|
|
useGrouping: true,
|
|
minimumIntegerDigits: 1,
|
|
minimumFractionDigits: 0,
|
|
maximumFractionDigits: 0,
|
|
});
|
|
|
|
export function formatDateTimeString(dateTime: string): string {
|
|
return dateTime
|
|
? _dateFormatter.format(new Date(dateTime))
|
|
: '';
|
|
}
|
|
|
|
export function formatTime(miliseconds: number): string {
|
|
if (miliseconds > 0) {
|
|
// hh:mm:ss
|
|
const matches = (new Date(miliseconds))?.toUTCString()?.match(/(\d\d:\d\d:\d\d)/) || [];
|
|
return matches?.length > 0
|
|
? matches[0] ?? ''
|
|
: '';
|
|
}
|
|
return '';
|
|
}
|
|
|
|
export function formatNumber(value: number): string {
|
|
return value >= 0
|
|
? _numberFormatter.format(value)
|
|
: '';
|
|
}
|
|
|
|
export function formatCopyThroughPut(value: number): string {
|
|
return value >= 0
|
|
? loc.sizeFormatter.format(value / 1024)
|
|
: '';
|
|
}
|
|
|
|
export function formatSizeBytes(sizeBytes: number): string {
|
|
return formatSizeKb(sizeBytes / 1024);
|
|
}
|
|
|
|
export function formatSizeKb(sizeKb: number): string {
|
|
return loc.formatSizeMb(sizeKb / 1024);
|
|
}
|
|
|
|
export function getMigrationTargetType(migration: DatabaseMigration | undefined): string {
|
|
const id = migration?.id?.toLowerCase() || '';
|
|
if (id?.indexOf(SQLTargetAssetType.SQLMI.toLowerCase()) > -1) {
|
|
return loc.SQL_MANAGED_INSTANCE;
|
|
}
|
|
else if (id?.indexOf(SQLTargetAssetType.SQLVM.toLowerCase()) > -1) {
|
|
return loc.SQL_VIRTUAL_MACHINE;
|
|
}
|
|
else if (id?.indexOf(SQLTargetAssetType.SQLDB.toLowerCase()) > -1) {
|
|
return loc.SQL_DATABASE;
|
|
}
|
|
return '';
|
|
}
|
|
|
|
export function getMigrationTargetTypeEnum(migration: DatabaseMigration | undefined): MigrationTargetType | undefined {
|
|
switch (migration?.properties?.kind) {
|
|
case AzureResourceKind.SQLMI:
|
|
return MigrationTargetType.SQLMI;
|
|
case AzureResourceKind.SQLVM:
|
|
return MigrationTargetType.SQLVM;
|
|
case AzureResourceKind.SQLDB:
|
|
return MigrationTargetType.SQLDB;
|
|
default:
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
export function getMigrationMode(migration: DatabaseMigration | undefined): string {
|
|
return isOfflineMigation(migration)
|
|
? loc.OFFLINE
|
|
: loc.ONLINE;
|
|
}
|
|
|
|
export function getMigrationModeEnum(migration: DatabaseMigration | undefined): MigrationMode {
|
|
return isOfflineMigation(migration)
|
|
? MigrationMode.OFFLINE
|
|
: MigrationMode.ONLINE;
|
|
}
|
|
|
|
export function isOfflineMigation(migration: DatabaseMigration | undefined): boolean {
|
|
return migration?.properties?.offlineConfiguration?.offline === true;
|
|
}
|
|
|
|
export function isBlobMigration(migration: DatabaseMigration | undefined): boolean {
|
|
return migration?.properties?.backupConfiguration?.sourceLocation?.fileStorageType === FileStorageType.AzureBlob;
|
|
}
|
|
|
|
export function isShirMigration(migration?: DatabaseMigration): boolean {
|
|
return isLogicalMigration(migration)
|
|
|| isFileShareMigration(migration);
|
|
}
|
|
|
|
export function isLogicalMigration(migration?: DatabaseMigration): boolean {
|
|
return migration?.properties?.kind === AzureResourceKind.ORACLETOSQLDB
|
|
|| migration?.properties?.kind === AzureResourceKind.SQLDB;
|
|
}
|
|
|
|
export function isFileShareMigration(migration?: DatabaseMigration): boolean {
|
|
return migration?.properties?.backupConfiguration?.sourceLocation?.fileStorageType === FileStorageTypeCodes.FileShare;
|
|
}
|
|
|
|
export function isTargetType(migration?: DatabaseMigration, kind?: string): boolean {
|
|
return migration?.properties?.kind === kind;
|
|
}
|
|
|
|
export function getMigrationStatus(migration: DatabaseMigration | undefined): string | undefined {
|
|
return migration?.properties.migrationStatus
|
|
?? migration?.properties.provisioningState;
|
|
}
|
|
|
|
export function getMigrationStatusString(migration: DatabaseMigration | undefined): string {
|
|
const migrationStatus = getMigrationStatus(migration) ?? DefaultSettingValue;
|
|
return loc.StatusLookup[migrationStatus] ?? migrationStatus;
|
|
}
|
|
|
|
export function hasMigrationOperationId(migration: DatabaseMigration | undefined): boolean {
|
|
const migrationId = migration?.id ?? '';
|
|
const migationOperationId = migration?.properties?.migrationOperationId ?? '';
|
|
return migrationId.length > 0
|
|
&& migationOperationId.length > 0;
|
|
}
|
|
|
|
export function getMigrationBackupLocation(migration: DatabaseMigration): string | undefined {
|
|
return migration?.properties?.backupConfiguration?.sourceLocation?.fileShare?.path
|
|
?? migration?.properties?.backupConfiguration?.sourceLocation?.azureBlob?.blobContainerName
|
|
?? migration?.properties?.migrationStatusDetails?.blobContainerName;
|
|
}
|
|
|
|
export function getMigrationFullBackupFiles(migration: DatabaseMigration): string | undefined {
|
|
return migration?.properties?.migrationStatusDetails?.fullBackupSetInfo?.listOfBackupFiles?.map(file => file.fileName).join(',');
|
|
}
|
|
|
|
export function hasRestoreBlockingReason(migration: DatabaseMigration | undefined): boolean {
|
|
return (migration?.properties.migrationStatusWarnings?.restoreBlockingReason ?? '').length > 0;
|
|
}
|
|
|
|
export function canCancelMigration(migration: DatabaseMigration | undefined): boolean {
|
|
const status = getMigrationStatus(migration);
|
|
return hasMigrationOperationId(migration)
|
|
&& (status === loc.MigrationState.InProgress
|
|
|| status === loc.MigrationState.Retriable
|
|
|| status === loc.MigrationState.Creating
|
|
|| status === loc.MigrationState.ReadyForCutover
|
|
|| status === loc.MigrationState.UploadingFullBackup
|
|
|| status === loc.MigrationState.UploadingLogBackup
|
|
|| status === loc.MigrationState.Restoring);
|
|
}
|
|
|
|
export function canDeleteMigration(migration: DatabaseMigration | undefined): boolean {
|
|
const status = getMigrationStatus(migration);
|
|
return status === loc.MigrationState.Canceled
|
|
|| status === loc.MigrationState.Failed
|
|
|| status === loc.MigrationState.Retriable
|
|
|| status === loc.MigrationState.Succeeded;
|
|
}
|
|
|
|
export function canRetryMigration(migration: DatabaseMigration | undefined): boolean {
|
|
const status = getMigrationStatus(migration);
|
|
return status === loc.MigrationState.Canceled
|
|
|| status === loc.MigrationState.Retriable
|
|
|| status === loc.MigrationState.Failed
|
|
|| status === loc.MigrationState.Succeeded;
|
|
}
|
|
|
|
export function canCutoverMigration(migration: DatabaseMigration | undefined): boolean {
|
|
const status = getMigrationStatus(migration);
|
|
return hasMigrationOperationId(migration)
|
|
&& isOnlineMigration(migration)
|
|
&& (status === loc.MigrationState.ReadyForCutover || status === loc.MigrationState.InProgress)
|
|
&& isFullBackupRestored(migration)
|
|
// if MI migration, must have no restore blocking reason
|
|
&& !(getMigrationTargetType(migration) === loc.SQL_MANAGED_INSTANCE && hasRestoreBlockingReason(migration));
|
|
}
|
|
|
|
export function isActiveMigration(migration: DatabaseMigration | undefined): boolean {
|
|
const status = getMigrationStatus(migration);
|
|
return status === loc.MigrationState.Completing
|
|
|| status === loc.MigrationState.Retriable
|
|
|| status === loc.MigrationState.Creating
|
|
|| status === loc.MigrationState.InProgress
|
|
|| status === loc.MigrationState.ReadyForCutover
|
|
|| status === loc.MigrationState.UploadingFullBackup
|
|
|| status === loc.MigrationState.UploadingLogBackup
|
|
|| status === loc.MigrationState.Restoring;
|
|
}
|
|
|
|
export function isFullBackupRestored(migration: DatabaseMigration | undefined): boolean {
|
|
const fileName = migration?.properties?.migrationStatusDetails?.lastRestoredFilename ?? '';
|
|
return migration?.properties?.migrationStatusDetails?.isFullBackupRestored
|
|
|| fileName.length > 0;
|
|
}
|
|
|
|
export function isOnlineMigration(migration: DatabaseMigration | undefined): boolean {
|
|
return getMigrationModeEnum(migration) === MigrationMode.ONLINE;
|
|
}
|
|
|
|
export function selectDatabasesFromList(selectedDbs: string[], databaseTableValues: azdata.DeclarativeTableCellValue[][]): azdata.DeclarativeTableCellValue[][] {
|
|
const TABLE_CHECKBOX_INDEX = 0;
|
|
const TABLE_DB_NAME_INDEX = 1;
|
|
const sourceDatabaseNames = selectedDbs?.map(dbName => dbName.toLocaleLowerCase()) || [];
|
|
if (sourceDatabaseNames?.length > 0) {
|
|
for (let i in databaseTableValues) {
|
|
const row = databaseTableValues[i];
|
|
const dbName = (row[TABLE_DB_NAME_INDEX].value as string)?.toLocaleLowerCase();
|
|
if (sourceDatabaseNames.indexOf(dbName) > -1) {
|
|
row[TABLE_CHECKBOX_INDEX].value = true;
|
|
}
|
|
}
|
|
}
|
|
return databaseTableValues;
|
|
}
|