mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-13 17:22:15 -05:00
Migration extensions - UI fixes and vBump (#15199)
* Fixing Migration Cutover Dialog Adding support for target file share Fixing request body Correcting localized strings * Redesigned IR page Adding additional details in migration status dialog * vbump * Fixed the perpetual loading * Fixed duration logic * Adding icon for migration extension * Adding helper commenst to util function localizing some strings logging console errors * enabling cutover buttons for ignored files
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 50 KiB |
@@ -1,32 +1,27 @@
|
||||
<svg viewBox="0 0 371 329" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<path d="M357.6 148.598C357.071 132.361 350.039 116.822 337.765 104.764C325.49 92.7068 308.774 84.9197 290.614 82.7983C289.488 60.043 278.302 38.5964 259.478 23.1003C240.653 7.60415 215.704 -0.694702 190.032 -6.17467e-05C169.318 -0.301247 149.007 5.08827 131.885 15.4291C114.762 25.77 101.667 40.5556 94.3959 57.7577C72.5117 60.1492 52.3148 69.4064 37.4581 83.8552C22.6013 98.304 14.0655 116.99 13.3943 136.535C14.307 158.515 25.0232 179.275 43.1878 194.252C61.3524 209.23 85.4793 217.199 110.266 216.409H275.155C276.527 216.511 277.906 216.511 279.278 216.409C299.717 216.368 319.332 209.261 333.957 196.599C348.582 183.937 357.063 166.717 357.6 148.598Z" fill="url(#paint0_linear)"/>
|
||||
<path d="M186.526 191.734C136.029 191.734 95.2191 180.219 95.2191 165.962V303.228C95.2191 317.302 136.441 328.817 185.29 329H186.526C236.818 329 277.628 317.485 277.628 303.228V165.962C277.628 180.219 236.818 191.734 186.526 191.734Z" fill="url(#paint1_linear)"/>
|
||||
<path d="M277.628 165.962C277.628 180.219 236.405 191.734 186.526 191.734C136.647 191.734 95.2191 180.219 95.2191 165.962C95.2191 151.706 136.441 140.191 186.526 140.191C236.611 140.191 277.628 151.706 277.628 165.962Z" fill="#E8E8E8"/>
|
||||
<path d="M256.4 164.5C256.4 173.456 225.071 180.95 186.529 180.95C147.986 180.95 116.451 173.456 116.451 164.5C116.451 155.544 147.78 148.05 186.529 148.05C225.277 148.05 256.4 155.361 256.4 164.5Z" fill="#50E6FF"/>
|
||||
<path d="M186.526 167.79C167.799 167.403 149.112 169.497 131.082 174.004C149.093 178.609 167.786 180.766 186.526 180.402C205.264 180.741 223.953 178.584 241.97 174.004C223.934 169.524 205.252 167.43 186.526 167.79Z" fill="#32BEDD"/>
|
||||
<path d="M129.646 91.389L183.028 43.5012C183.605 42.9761 184.294 42.5588 185.055 42.2738C185.816 41.9888 186.634 41.842 187.46 41.842C188.286 41.842 189.104 41.9888 189.865 42.2738C190.626 42.5588 191.315 42.9761 191.891 43.5012L245.274 91.389C245.732 91.7153 246.056 92.1662 246.197 92.672C246.338 93.1779 246.287 93.7107 246.053 94.1885C245.818 94.6663 245.414 95.0626 244.9 95.3164C244.387 95.5701 243.794 95.6673 243.213 95.5929H210.441C209.676 95.5929 208.942 95.8625 208.401 96.3423C207.86 96.8222 207.556 97.4731 207.556 98.1517V156.823C207.588 157.095 207.552 157.369 207.449 157.626C207.347 157.883 207.181 158.116 206.963 158.309C206.746 158.502 206.483 158.649 206.193 158.74C205.903 158.831 205.594 158.863 205.288 158.834H169.425C168.824 158.834 168.247 158.622 167.822 158.245C167.397 157.868 167.158 157.357 167.158 156.823V97.6034C167.218 97.0011 167.017 96.4012 166.596 95.9258C166.174 95.4503 165.565 95.1352 164.891 95.0445H131.501C131.055 94.9871 130.632 94.8309 130.272 94.5903C129.912 94.3497 129.627 94.0326 129.442 93.6681C129.257 93.3037 129.179 92.9038 129.214 92.5054C129.25 92.107 129.398 91.723 129.646 91.389Z" fill="#F2F2F2"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear" x1="185.497" y1="216.409" x2="185.497" y2="-5.72786e-05" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#0078D4"/>
|
||||
<stop offset="0.16" stop-color="#1380DA"/>
|
||||
<stop offset="0.53" stop-color="#3C91E5"/>
|
||||
<stop offset="0.82" stop-color="#559CEC"/>
|
||||
<stop offset="1" stop-color="#5EA0EF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear" x1="95.2191" y1="247.481" x2="277.628" y2="247.481" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#32BEDD"/>
|
||||
<stop offset="0.06" stop-color="#37C5E3"/>
|
||||
<stop offset="0.3" stop-color="#49DDF7"/>
|
||||
<stop offset="0.45" stop-color="#50E6FF"/>
|
||||
<stop offset="0.55" stop-color="#50E6FF"/>
|
||||
<stop offset="0.7" stop-color="#49DDF7"/>
|
||||
<stop offset="0.94" stop-color="#37C5E3"/>
|
||||
<stop offset="1" stop-color="#32BEDD"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0">
|
||||
<rect width="371" height="329" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<svg id="b15e05d5-34e2-4c79-a9a5-c1b52415ef11" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32" viewBox="0 0 18 18">
|
||||
<defs>
|
||||
<linearGradient id="bde8e97f-66c0-43a9-a1d7-5ab946144ffc" x1="12.282" y1="11.436" x2="12.282" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#005ba1"/>
|
||||
<stop offset="0.525" stop-color="#0074cd"/>
|
||||
<stop offset="1" stop-color="#0078d4"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="f113b540-5195-433b-b39c-6990b98ebd7e" x1="7.923" y1="18" x2="7.923" y2="3.129" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#0078d4"/>
|
||||
<stop offset="0.16" stop-color="#1380da"/>
|
||||
<stop offset="0.53" stop-color="#3c91e5"/>
|
||||
<stop offset="0.82" stop-color="#559cec"/>
|
||||
<stop offset="1" stop-color="#5ea0ef"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="ecdaa277-bf04-4a14-a013-fa7c95492ff5" x1="308.186" y1="365.104" x2="308.186" y2="371.623" gradientTransform="matrix(1, 0, 0, -1, -298, 382)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#32bedd"/>
|
||||
<stop offset="0.175" stop-color="#32caea"/>
|
||||
<stop offset="0.41" stop-color="#32d2f2"/>
|
||||
<stop offset="0.775" stop-color="#32d4f5"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect x="6.564" width="11.436" height="11.436" rx="1" fill="url(#bde8e97f-66c0-43a9-a1d7-5ab946144ffc)"/>
|
||||
<rect y="3.129" width="15.846" height="14.871" rx="1" fill="url(#f113b540-5195-433b-b39c-6990b98ebd7e)"/>
|
||||
<path d="M9.341,7.449V4.311h-.83V8.123h2.27V7.449ZM2.751,5.908a1.722,1.722,0,0,1-.471-.286.405.405,0,0,1-.11-.3.313.313,0,0,1,.138-.277.63.63,0,0,1,.388-.11,1.5,1.5,0,0,1,.923.267V4.413A2.382,2.382,0,0,0,2.7,4.265a1.514,1.514,0,0,0-1.006.314A1,1,0,0,0,1.3,5.4c0,.471.295.84.923,1.117a2.634,2.634,0,0,1,.572.332.39.39,0,0,1,.139.3.351.351,0,0,1-.148.286.747.747,0,0,1-.415.1,1.533,1.533,0,0,1-1.006-.387V8a2.01,2.01,0,0,0,.987.222,1.737,1.737,0,0,0,1.089-.3.994.994,0,0,0,.305-.84.972.972,0,0,0-.231-.646A2.246,2.246,0,0,0,2.751,5.908ZM7.588,7.375a2.16,2.16,0,0,0,.3-1.163,2.145,2.145,0,0,0-.3-.978,1.671,1.671,0,0,0-.646-.692,1.846,1.846,0,0,0-.923-.24,1.942,1.942,0,0,0-1,.249,1.717,1.717,0,0,0-.674.683,2.258,2.258,0,0,0-.24,1.052,2.094,2.094,0,0,0,.221.923,1.627,1.627,0,0,0,.637.683,1.9,1.9,0,0,0,.923.277l.794.923H7.8L6.665,8.077A1.654,1.654,0,0,0,7.588,7.375Zm-.923-.23a.871.871,0,0,1-.7.323.846.846,0,0,1-.7-.333,1.4,1.4,0,0,1-.268-.923,1.412,1.412,0,0,1,.268-.923.921.921,0,0,1,.72-.341.8.8,0,0,1,.692.341,1.5,1.5,0,0,1,.249.923A1.353,1.353,0,0,1,6.665,7.145Z" fill="#fff"/>
|
||||
<path d="M14.78,14.852a2.058,2.058,0,0,0-1.788-1.981,2.608,2.608,0,0,0-2.685-2.492,2.68,2.68,0,0,0-2.554,1.738,2.465,2.465,0,0,0-2.162,2.372,2.5,2.5,0,0,0,2.586,2.4h4.4a.673.673,0,0,0,.11,0A2.1,2.1,0,0,0,14.78,14.852Z" fill="url(#ecdaa277-bf04-4a14-a013-fa7c95492ff5)"/>
|
||||
<path d="M8.719,13.691l1.519-1.536a.175.175,0,0,1,.248,0l0,0,1.518,1.536a.076.076,0,0,1,.012.107.077.077,0,0,1-.071.028h-.932a.082.082,0,0,0-.082.082h0V15.79a.058.058,0,0,1-.052.064.024.024,0,0,1-.012,0H9.851a.065.065,0,0,1-.065-.064v-1.9a.075.075,0,0,0-.064-.082h-.95a.075.075,0,0,1-.064-.086A.068.068,0,0,1,8.719,13.691Z" fill="#f2f2f2"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.2 KiB |
@@ -2,7 +2,7 @@
|
||||
"name": "sql-migration",
|
||||
"displayName": "%displayName%",
|
||||
"description": "%description%",
|
||||
"version": "0.0.9",
|
||||
"version": "0.0.10",
|
||||
"publisher": "Microsoft",
|
||||
"preview": true,
|
||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt",
|
||||
|
||||
@@ -350,19 +350,8 @@ export interface StartDatabaseMigrationRequest {
|
||||
targetLocation: {
|
||||
storageAccountResourceId: string,
|
||||
accountKey: string,
|
||||
}
|
||||
sourceLocation: {
|
||||
fileShare?: {
|
||||
path: string,
|
||||
username: string,
|
||||
password: string,
|
||||
},
|
||||
azureBlob?: {
|
||||
storageAccountResourceId: string,
|
||||
accountKey: string,
|
||||
blobContainerName: string
|
||||
}
|
||||
},
|
||||
sourceLocation: SourceLocation
|
||||
},
|
||||
sourceSqlConnection: {
|
||||
authentication: string,
|
||||
@@ -454,8 +443,8 @@ export interface BackupSetInfo {
|
||||
}
|
||||
|
||||
export interface SourceLocation {
|
||||
fileShare: DatabaseMigrationFileShare;
|
||||
azureBlob: DatabaseMigrationAzureBlob;
|
||||
fileShare?: DatabaseMigrationFileShare;
|
||||
azureBlob?: DatabaseMigrationAzureBlob;
|
||||
}
|
||||
|
||||
export interface TargetLocation {
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { DAYS, HRS, MINUTE, SEC } from '../constants/strings';
|
||||
|
||||
export function deepClone<T>(obj: T): T {
|
||||
if (!obj || typeof obj !== 'object') {
|
||||
return obj;
|
||||
@@ -40,3 +42,27 @@ export function getSqlServerName(majorVersion: number): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a wordy time difference between start and end time.
|
||||
* @returns stringified duration like '10.0 days', '12.0 hrs', '1.0 min'
|
||||
*/
|
||||
export function convertTimeDifferenceToDuration(startTime: Date, endTime: Date): string {
|
||||
const time = endTime.getTime() - startTime.getTime();
|
||||
let seconds = (time / 1000).toFixed(1);
|
||||
let minutes = (time / (1000 * 60)).toFixed(1);
|
||||
let hours = (time / (1000 * 60 * 60)).toFixed(1);
|
||||
let days = (time / (1000 * 60 * 60 * 24)).toFixed(1);
|
||||
if (time / 1000 < 60) {
|
||||
return SEC(parseFloat(seconds));
|
||||
}
|
||||
else if (time / (1000 * 60) < 60) {
|
||||
return MINUTE(parseFloat(minutes));
|
||||
}
|
||||
else if (time / (1000 * 60 * 60) < 24) {
|
||||
return HRS(parseFloat(hours));
|
||||
}
|
||||
else {
|
||||
return DAYS(parseFloat(days));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,7 +270,7 @@ export const EASTUS2EUAP = localize('sql.migration.eastus2euap', 'East US 2 EUAP
|
||||
|
||||
//Migration cutover dialog
|
||||
export const MIGRATION_CUTOVER = localize('sql.migration.cutover', "Migration cutover");
|
||||
export const SOURCE_DATABASE = localize('sql.migration.source.database', "Source database");
|
||||
export const SOURCE_DATABASE = localize('sql.migration.source.database', "Source database name");
|
||||
export const SOURCE_SERVER = localize('sql.migration.source.server', "Source server");
|
||||
export const SOURCE_VERSION = localize('sql.migration.source.version', "Source version");
|
||||
export const TARGET_DATABASE_NAME = localize('sql.migration.target.database.name', "Target database name");
|
||||
@@ -306,7 +306,12 @@ export const SEARCH_FOR_MIGRATIONS = localize('sql.migration.search.for.migratio
|
||||
export const ONLINE = localize('sql.migration.online', "Online");
|
||||
export const OFFLINE = localize('sql.migration.offline', "Offline");
|
||||
export const DATABASE = localize('sql.migration.database', "Database");
|
||||
export const TARGET_AZURE_SQL_INSTANCE_NAME = localize('sql.migration.target.azure.sql.instance.name', "Target Azure SQL Instance Name");
|
||||
export const DATABASE_MIGRATION_SERVICE = localize('sql.migration.database.migration.service', "Database Migration Service");
|
||||
export const DURATION = localize('sql.migration.duration', "Duration");
|
||||
export const AZURE_SQL_TARGET = localize('sql.migration.azure.sql.target', "Azure SQL Target");
|
||||
export const SQL_MANAGED_INSTANCE = localize('sql.migration.sql.managed.instance', "SQL Managed Instance");
|
||||
export const SQL_VIRTUAL_MACHINE = localize('sql.migration.sql.virtual.machine', "SQL Virtual Machine");
|
||||
export const TARGET_AZURE_SQL_INSTANCE_NAME = localize('sql.migration.target.azure.sql.instance.name', "Azure SQL Target Name");
|
||||
export const MIGRATION_MODE = localize('sql.migration.cutover.type', "Migration Mode");
|
||||
export const START_TIME = localize('sql.migration.start.time', "Start Time");
|
||||
export const FINISH_TIME = localize('sql.migration.finish.time', "Finish Time");
|
||||
@@ -330,7 +335,19 @@ export function STATUS_WARNING_COUNT(status: string, count: number): string {
|
||||
return localize('sql.migration.status.error.count.multiple', "{0} ({1} Errors)", status, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function HRS(hrs: number): string {
|
||||
return hrs > 1 ? localize('sql.migration.hrs', "{0} hrs", hrs) : localize('sql.migration.hr', "{0} hr", hrs);
|
||||
}
|
||||
export function DAYS(days: number): string {
|
||||
return days > 1 ? localize('sql.migration.days', "{0} days", days) : localize('sql.migration.day', "{0} day", days);
|
||||
}
|
||||
export function MINUTE(mins: number): string {
|
||||
return mins > 1 ? localize('sql.migration.mins', "{0} mins", mins) : localize('sql.migration.min', "{0} min", mins);
|
||||
}
|
||||
export function SEC(sec: number): string {
|
||||
return localize('sql.migration.sec', "{0} sec", sec);
|
||||
}
|
||||
|
||||
//Source Credentials page.
|
||||
|
||||
@@ -212,7 +212,7 @@ export class DashboardWidget {
|
||||
height: maxHeight,
|
||||
iconHeight: 32,
|
||||
iconPath: taskMetaData.iconPath,
|
||||
iconWidth: 36,
|
||||
iconWidth: 32,
|
||||
label: taskMetaData.title,
|
||||
title: taskMetaData.title,
|
||||
width: maxWidth,
|
||||
|
||||
@@ -58,7 +58,9 @@ export class CreateSqlMigrationServiceDialog {
|
||||
text: ''
|
||||
};
|
||||
this._statusLoadingComponent.loading = true;
|
||||
this._formSubmitButton.enabled = false;
|
||||
this.migrationServiceResourceGroupDropdown.loading = false;
|
||||
this.setFormEnabledState(false);
|
||||
|
||||
|
||||
const subscription = this.migrationStateModel._targetSubscription;
|
||||
const resourceGroup = (this.migrationServiceResourceGroupDropdown.value as azdata.CategoryValue).name;
|
||||
@@ -70,7 +72,7 @@ export class CreateSqlMigrationServiceDialog {
|
||||
if (formValidationErrors.length > 0) {
|
||||
this.setDialogMessage(formValidationErrors);
|
||||
this._statusLoadingComponent.loading = false;
|
||||
this._formSubmitButton.enabled = true;
|
||||
this.setFormEnabledState(true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -79,7 +81,7 @@ export class CreateSqlMigrationServiceDialog {
|
||||
if (this.createdMigrationService.error) {
|
||||
this.setDialogMessage(`${this.createdMigrationService.error.code} : ${this.createdMigrationService.error.message}`);
|
||||
this._statusLoadingComponent.loading = false;
|
||||
this._formSubmitButton.enabled = true;
|
||||
this.setFormEnabledState(true);
|
||||
return;
|
||||
}
|
||||
this._dialogObject.message = {
|
||||
@@ -93,7 +95,7 @@ export class CreateSqlMigrationServiceDialog {
|
||||
console.log(e);
|
||||
this.setDialogMessage(e.message);
|
||||
this._statusLoadingComponent.loading = false;
|
||||
this._formSubmitButton.enabled = true;
|
||||
this.setFormEnabledState(true);
|
||||
return;
|
||||
}
|
||||
});
|
||||
@@ -138,17 +140,24 @@ export class CreateSqlMigrationServiceDialog {
|
||||
this._dialogObject.cancelButton.onClick((e) => {
|
||||
});
|
||||
this._dialogObject.okButton.onClick((e) => {
|
||||
this.irPage.populateMigrationService(this.createdMigrationService, this.createdMigrationServiceNodeNames);
|
||||
this.irPage.populateMigrationService(this.createdMigrationService, this.createdMigrationServiceNodeNames, (this.migrationServiceResourceGroupDropdown.value as azdata.CategoryValue).name);
|
||||
});
|
||||
}
|
||||
|
||||
private async migrationServiceDropdownContainer(): Promise<azdata.FlexContainer> {
|
||||
const dialogDescription = this._view.modelBuilder.text().withProps({
|
||||
value: constants.MIGRATION_SERVICE_DIALOG_DESCRIPTION
|
||||
value: constants.MIGRATION_SERVICE_DIALOG_DESCRIPTION,
|
||||
CSSStyles: {
|
||||
'font-size': '13px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
const subscriptionDropdownLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.SUBSCRIPTION
|
||||
value: constants.SUBSCRIPTION,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold'
|
||||
}
|
||||
}).component();
|
||||
|
||||
this.migrationServiceSubscription = this._view.modelBuilder.inputBox().withProps({
|
||||
@@ -157,7 +166,11 @@ export class CreateSqlMigrationServiceDialog {
|
||||
}).component();
|
||||
|
||||
const resourceGroupDropdownLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.RESOURCE_GROUP
|
||||
value: constants.RESOURCE_GROUP,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold'
|
||||
}
|
||||
}).component();
|
||||
|
||||
this.migrationServiceResourceGroupDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||
@@ -165,13 +178,21 @@ export class CreateSqlMigrationServiceDialog {
|
||||
}).component();
|
||||
|
||||
const migrationServiceNameLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.NAME
|
||||
value: constants.NAME,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold'
|
||||
}
|
||||
}).component();
|
||||
|
||||
this.migrationServiceNameText = this._view.modelBuilder.inputBox().component();
|
||||
|
||||
const locationDropdownLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.LOCATION
|
||||
value: constants.LOCATION,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold'
|
||||
}
|
||||
}).component();
|
||||
|
||||
this.migrationServiceLocation = this._view.modelBuilder.inputBox().withProps({
|
||||
@@ -181,7 +202,11 @@ export class CreateSqlMigrationServiceDialog {
|
||||
}).component();
|
||||
|
||||
const targetlabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.TARGET
|
||||
value: constants.TARGET,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold'
|
||||
}
|
||||
}).component();
|
||||
|
||||
const targetText = this._view.modelBuilder.inputBox().withProps({
|
||||
@@ -259,20 +284,30 @@ export class CreateSqlMigrationServiceDialog {
|
||||
const setupIRHeadingText = this._view.modelBuilder.text().withProps({
|
||||
value: constants.SERVICE_CONTAINER_HEADING,
|
||||
CSSStyles: {
|
||||
'font-weight': 'bold'
|
||||
'font-weight': 'bold',
|
||||
'font-size': '13px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
const setupIRdescription1 = this._view.modelBuilder.text().withProps({
|
||||
value: constants.SERVICE_CONTAINER_DESCRIPTION1,
|
||||
CSSStyles: {
|
||||
'font-size': '13px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
const setupIRdescription2 = this._view.modelBuilder.text().withProps({
|
||||
value: constants.SERVICE_CONTAINER_DESCRIPTION2,
|
||||
CSSStyles: {
|
||||
'font-size': '13px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
const irSetupStep1Text = this._view.modelBuilder.text().withProps({
|
||||
value: constants.SERVICE_STEP1,
|
||||
CSSStyles: {
|
||||
'font-size': '13px'
|
||||
},
|
||||
links: [
|
||||
{
|
||||
text: constants.SERVICE_STEP1_LINK,
|
||||
@@ -282,7 +317,10 @@ export class CreateSqlMigrationServiceDialog {
|
||||
}).component();
|
||||
|
||||
const irSetupStep2Text = this._view.modelBuilder.text().withProps({
|
||||
value: constants.SERVICE_STEP2
|
||||
value: constants.SERVICE_STEP2,
|
||||
CSSStyles: {
|
||||
'font-size': '13px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
const irSetupStep3Text = this._view.modelBuilder.hyperlink().withProps({
|
||||
@@ -290,7 +328,8 @@ export class CreateSqlMigrationServiceDialog {
|
||||
url: '',
|
||||
CSSStyles: {
|
||||
'margin-top': '10px',
|
||||
'margin-bottom': '10px'
|
||||
'margin-bottom': '10px',
|
||||
'font-size': '13px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
@@ -311,14 +350,23 @@ export class CreateSqlMigrationServiceDialog {
|
||||
});
|
||||
|
||||
|
||||
this._connectionStatus = this._view.modelBuilder.infoBox().component();
|
||||
this._connectionStatus = this._view.modelBuilder.infoBox().withProps({
|
||||
text: '',
|
||||
style: 'error',
|
||||
CSSStyles: {
|
||||
'font-size': '13px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
this._connectionStatus.CSSStyles = {
|
||||
'width': '350px'
|
||||
};
|
||||
|
||||
const refreshLoadingIndicator = this._view.modelBuilder.loadingComponent().withProps({
|
||||
loading: false
|
||||
loading: false,
|
||||
CSSStyles: {
|
||||
'font-size': '13px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
|
||||
@@ -330,7 +378,10 @@ export class CreateSqlMigrationServiceDialog {
|
||||
width: '50px',
|
||||
isReadOnly: true,
|
||||
rowCssStyles: {
|
||||
'text-align': 'center'
|
||||
'font-size': '13px'
|
||||
},
|
||||
headerCssStyles: {
|
||||
'font-size': '13px'
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -339,20 +390,23 @@ export class CreateSqlMigrationServiceDialog {
|
||||
width: '500px',
|
||||
isReadOnly: true,
|
||||
rowCssStyles: {
|
||||
overflow: 'scroll'
|
||||
'font-size': '13px'
|
||||
},
|
||||
headerCssStyles: {
|
||||
'font-size': '13px'
|
||||
}
|
||||
},
|
||||
{
|
||||
displayName: '',
|
||||
valueType: azdata.DeclarativeDataType.component,
|
||||
width: '15px',
|
||||
isReadOnly: true,
|
||||
},
|
||||
{
|
||||
displayName: '',
|
||||
valueType: azdata.DeclarativeDataType.component,
|
||||
width: '15px',
|
||||
width: '30px',
|
||||
isReadOnly: true,
|
||||
rowCssStyles: {
|
||||
'font-size': '13px'
|
||||
},
|
||||
headerCssStyles: {
|
||||
'font-size': '13px'
|
||||
}
|
||||
}
|
||||
],
|
||||
CSSStyles: {
|
||||
@@ -399,14 +453,20 @@ export class CreateSqlMigrationServiceDialog {
|
||||
if (state === 'Online') {
|
||||
this._connectionStatus.updateProperties(<azdata.InfoBoxComponentProperties>{
|
||||
text: constants.SERVICE_READY(this.createdMigrationService!.name, this.createdMigrationServiceNodeNames.join(', ')),
|
||||
style: 'success'
|
||||
style: 'success',
|
||||
CSSStyles: {
|
||||
'font-size': '13px'
|
||||
}
|
||||
});
|
||||
this._dialogObject.okButton.enabled = true;
|
||||
} else {
|
||||
this._connectionStatus.text = constants.SERVICE_NOT_READY(this.createdMigrationService!.name);
|
||||
this._connectionStatus.updateProperties(<azdata.InfoBoxComponentProperties>{
|
||||
text: constants.SERVICE_NOT_READY(this.createdMigrationService!.name),
|
||||
style: 'warning'
|
||||
style: 'warning',
|
||||
CSSStyles: {
|
||||
'font-size': '13px'
|
||||
}
|
||||
});
|
||||
this._dialogObject.okButton.enabled = false;
|
||||
}
|
||||
@@ -461,10 +521,7 @@ export class CreateSqlMigrationServiceDialog {
|
||||
value: keys.authKey1
|
||||
},
|
||||
{
|
||||
value: this._copyKey1Button
|
||||
},
|
||||
{
|
||||
value: this._refreshKey1Button
|
||||
value: this._view.modelBuilder.flexContainer().withItems([this._copyKey1Button, this._refreshKey1Button]).component()
|
||||
}
|
||||
],
|
||||
[
|
||||
@@ -475,10 +532,7 @@ export class CreateSqlMigrationServiceDialog {
|
||||
value: keys.authKey2
|
||||
},
|
||||
{
|
||||
value: this._copyKey2Button
|
||||
},
|
||||
{
|
||||
value: this._refreshKey2Button
|
||||
value: this._view.modelBuilder.flexContainer().withItems([this._copyKey2Button, this._refreshKey2Button]).component()
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -492,4 +546,10 @@ export class CreateSqlMigrationServiceDialog {
|
||||
level: level
|
||||
};
|
||||
}
|
||||
|
||||
private setFormEnabledState(enable: boolean): void {
|
||||
this._formSubmitButton.enabled = enable;
|
||||
this.migrationServiceResourceGroupDropdown.enabled = enable;
|
||||
this.migrationServiceNameText.enabled = enable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ export class MigrationCutoverDialog {
|
||||
private _targetVersion!: azdata.TextComponent;
|
||||
private _migrationStatus!: azdata.TextComponent;
|
||||
private _fullBackupFile!: azdata.TextComponent;
|
||||
private _backupLocation!: azdata.TextComponent;
|
||||
private _lastAppliedLSN!: azdata.TextComponent;
|
||||
private _lastAppliedBackupFile!: azdata.TextComponent;
|
||||
private _lastAppliedBackupTakenOn!: azdata.TextComponent;
|
||||
@@ -44,7 +45,7 @@ export class MigrationCutoverDialog {
|
||||
|
||||
constructor(migration: MigrationContext) {
|
||||
this._model = new MigrationCutoverDialogModel(migration);
|
||||
this._dialogObject = azdata.window.createModelViewDialog(loc.MIGRATION_CUTOVER, 'MigrationCutoverDialog', 1000);
|
||||
this._dialogObject = azdata.window.createModelViewDialog('', 'MigrationCutoverDialog', 1000);
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
@@ -65,17 +66,17 @@ export class MigrationCutoverDialog {
|
||||
|
||||
flexServer.addItem(sourceDatabase.flexContainer, {
|
||||
CSSStyles: {
|
||||
'width': '150px'
|
||||
'width': '200px'
|
||||
}
|
||||
});
|
||||
flexServer.addItem(sourceDetails.flexContainer, {
|
||||
CSSStyles: {
|
||||
'width': '150px'
|
||||
'width': '200px'
|
||||
}
|
||||
});
|
||||
flexServer.addItem(sourceVersion.flexContainer, {
|
||||
CSSStyles: {
|
||||
'width': '150px'
|
||||
'width': '200px'
|
||||
}
|
||||
});
|
||||
|
||||
@@ -93,26 +94,28 @@ export class MigrationCutoverDialog {
|
||||
|
||||
flexTarget.addItem(targetDatabase.flexContainer, {
|
||||
CSSStyles: {
|
||||
'width': '230px'
|
||||
'width': '200px'
|
||||
}
|
||||
});
|
||||
flexTarget.addItem(targetServer.flexContainer, {
|
||||
CSSStyles: {
|
||||
'width': '230px'
|
||||
'width': '200px'
|
||||
}
|
||||
});
|
||||
flexTarget.addItem(targetVersion.flexContainer, {
|
||||
CSSStyles: {
|
||||
'width': '230px'
|
||||
'width': '200px'
|
||||
}
|
||||
});
|
||||
|
||||
const migrationStatus = this.createInfoField(loc.MIGRATION_STATUS, '');
|
||||
const fullBackupFileOn = this.createInfoField(loc.FULL_BACKUP_FILES, '');
|
||||
const backupLocation = this.createInfoField(loc.BACKUP_LOCATION, '');
|
||||
|
||||
|
||||
this._migrationStatus = migrationStatus.text;
|
||||
this._fullBackupFile = fullBackupFileOn.text;
|
||||
this._backupLocation = backupLocation.text;
|
||||
|
||||
const flexStatus = view.modelBuilder.flexContainer().withLayout({
|
||||
flexFlow: 'column'
|
||||
@@ -120,12 +123,17 @@ export class MigrationCutoverDialog {
|
||||
|
||||
flexStatus.addItem(migrationStatus.flexContainer, {
|
||||
CSSStyles: {
|
||||
'width': '180px'
|
||||
'width': '200px'
|
||||
}
|
||||
});
|
||||
flexStatus.addItem(fullBackupFileOn.flexContainer, {
|
||||
CSSStyles: {
|
||||
'width': '180px'
|
||||
'width': '200px'
|
||||
}
|
||||
});
|
||||
flexStatus.addItem(backupLocation.flexContainer, {
|
||||
CSSStyles: {
|
||||
'width': '200px'
|
||||
}
|
||||
});
|
||||
|
||||
@@ -133,6 +141,7 @@ export class MigrationCutoverDialog {
|
||||
const lastAppliedBackup = this.createInfoField(loc.LAST_APPLIED_BACKUP_FILES, '');
|
||||
const lastAppliedBackupOn = this.createInfoField(loc.LAST_APPLIED_BACKUP_FILES_TAKEN_ON, '');
|
||||
|
||||
|
||||
this._lastAppliedLSN = lastSSN.text;
|
||||
this._lastAppliedBackupFile = lastAppliedBackup.text;
|
||||
this._lastAppliedBackupTakenOn = lastAppliedBackupOn.text;
|
||||
@@ -142,22 +151,22 @@ export class MigrationCutoverDialog {
|
||||
}).component();
|
||||
flexFile.addItem(lastSSN.flexContainer, {
|
||||
CSSStyles: {
|
||||
'width': '230px'
|
||||
'width': '200px'
|
||||
}
|
||||
});
|
||||
flexFile.addItem(lastAppliedBackup.flexContainer, {
|
||||
CSSStyles: {
|
||||
'width': '230px'
|
||||
'width': '200px'
|
||||
}
|
||||
});
|
||||
flexFile.addItem(lastAppliedBackupOn.flexContainer, {
|
||||
CSSStyles: {
|
||||
'width': '230px'
|
||||
'width': '200px'
|
||||
}
|
||||
});
|
||||
const flexInfo = view.modelBuilder.flexContainer().withProps({
|
||||
CSSStyles: {
|
||||
'width': '700px'
|
||||
'width': '800px',
|
||||
}
|
||||
}).component();
|
||||
|
||||
@@ -165,7 +174,7 @@ export class MigrationCutoverDialog {
|
||||
flex: '0',
|
||||
CSSStyles: {
|
||||
'flex': '0',
|
||||
'width': '150px'
|
||||
'width': '200px'
|
||||
}
|
||||
});
|
||||
|
||||
@@ -173,7 +182,7 @@ export class MigrationCutoverDialog {
|
||||
flex: '0',
|
||||
CSSStyles: {
|
||||
'flex': '0',
|
||||
'width': '230px'
|
||||
'width': '200px'
|
||||
}
|
||||
});
|
||||
|
||||
@@ -181,7 +190,7 @@ export class MigrationCutoverDialog {
|
||||
flex: '0',
|
||||
CSSStyles: {
|
||||
'flex': '0',
|
||||
'width': '180px'
|
||||
'width': '200px'
|
||||
}
|
||||
});
|
||||
|
||||
@@ -240,11 +249,17 @@ export class MigrationCutoverDialog {
|
||||
const formBuilder = view.modelBuilder.formContainer().withFormItems(
|
||||
[
|
||||
{
|
||||
component: await this.migrationContainerHeader()
|
||||
component: this.migrationContainerHeader()
|
||||
},
|
||||
{
|
||||
component: this._view.modelBuilder.separator().withProps({ width: '800px' }).component()
|
||||
},
|
||||
{
|
||||
component: flexInfo
|
||||
},
|
||||
{
|
||||
component: this._view.modelBuilder.separator().withProps({ width: '800px' }).component()
|
||||
},
|
||||
{
|
||||
component: this._fileCount
|
||||
},
|
||||
@@ -267,30 +282,59 @@ export class MigrationCutoverDialog {
|
||||
|
||||
|
||||
private migrationContainerHeader(): azdata.FlexContainer {
|
||||
const header = this._view.modelBuilder.flexContainer().withLayout({
|
||||
const sqlDatbaseLogo = this._view.modelBuilder.image().withProps({
|
||||
iconPath: IconPathHelper.sqlDatabaseLogo,
|
||||
iconHeight: '32px',
|
||||
iconWidth: '32px',
|
||||
width: '32px',
|
||||
height: '32px'
|
||||
}).component();
|
||||
|
||||
this._databaseTitleName = this._view.modelBuilder.text().withProps({
|
||||
CSSStyles: {
|
||||
'font-size': 'large',
|
||||
'width': '400px'
|
||||
'font-size': '16px',
|
||||
'font-weight': 'bold',
|
||||
'margin': '0px'
|
||||
},
|
||||
value: this._model._migration.migrationContext.name
|
||||
value: this._model._migration.migrationContext.properties.sourceDatabaseName
|
||||
}).component();
|
||||
|
||||
header.addItem(this._databaseTitleName, {
|
||||
flex: '0',
|
||||
const databaseSubTitle = this._view.modelBuilder.text().withProps({
|
||||
CSSStyles: {
|
||||
'width': '500px'
|
||||
'font-size': '10px',
|
||||
'margin': '5px 0px'
|
||||
},
|
||||
value: loc.DATABASE
|
||||
}).component();
|
||||
|
||||
const titleContainer = this._view.modelBuilder.flexContainer().withItems([
|
||||
this._databaseTitleName,
|
||||
databaseSubTitle
|
||||
]).withLayout({
|
||||
'flexFlow': 'column'
|
||||
}).component();
|
||||
|
||||
|
||||
const titleLogoContainer = this._view.modelBuilder.flexContainer().component();
|
||||
|
||||
titleLogoContainer.addItem(sqlDatbaseLogo, {
|
||||
flex: '0'
|
||||
});
|
||||
titleLogoContainer.addItem(titleContainer, {
|
||||
CSSStyles: {
|
||||
'margin-left': '5px'
|
||||
}
|
||||
});
|
||||
|
||||
const headerActions = this._view.modelBuilder.flexContainer().withLayout({
|
||||
}).component();
|
||||
|
||||
this._cutoverButton = this._view.modelBuilder.button().withProps({
|
||||
iconPath: IconPathHelper.cutover,
|
||||
iconHeight: '14px',
|
||||
iconWidth: '12px',
|
||||
label: 'Start Cutover',
|
||||
height: '55px',
|
||||
height: '20px',
|
||||
width: '100px',
|
||||
enabled: false
|
||||
}).component();
|
||||
@@ -307,11 +351,8 @@ export class MigrationCutoverDialog {
|
||||
}
|
||||
});
|
||||
|
||||
header.addItem(this._cutoverButton, {
|
||||
flex: '0',
|
||||
CSSStyles: {
|
||||
'width': '100px'
|
||||
}
|
||||
headerActions.addItem(this._cutoverButton, {
|
||||
flex: '0'
|
||||
});
|
||||
|
||||
this._cancelButton = this._view.modelBuilder.button().withProps({
|
||||
@@ -319,19 +360,16 @@ export class MigrationCutoverDialog {
|
||||
iconHeight: '16px',
|
||||
iconWidth: '16px',
|
||||
label: loc.CANCEL_MIGRATION,
|
||||
height: '55px',
|
||||
width: '130px'
|
||||
height: '20px',
|
||||
width: '120px'
|
||||
}).component();
|
||||
|
||||
this._cancelButton.onDidClick((e) => {
|
||||
this.cancelMigration();
|
||||
});
|
||||
|
||||
header.addItem(this._cancelButton, {
|
||||
flex: '0',
|
||||
CSSStyles: {
|
||||
'width': '130px'
|
||||
}
|
||||
headerActions.addItem(this._cancelButton, {
|
||||
flex: '0'
|
||||
});
|
||||
|
||||
|
||||
@@ -340,19 +378,16 @@ export class MigrationCutoverDialog {
|
||||
iconHeight: '16px',
|
||||
iconWidth: '16px',
|
||||
label: 'Refresh',
|
||||
height: '55px',
|
||||
width: '100px'
|
||||
height: '20px',
|
||||
width: '65px'
|
||||
}).component();
|
||||
|
||||
this._refreshButton.onDidClick((e) => {
|
||||
this.refreshStatus();
|
||||
});
|
||||
|
||||
header.addItem(this._refreshButton, {
|
||||
headerActions.addItem(this._refreshButton, {
|
||||
flex: '0',
|
||||
CSSStyles: {
|
||||
'width': '100px'
|
||||
}
|
||||
});
|
||||
|
||||
this._copyDatabaseMigrationDetails = this._view.modelBuilder.button().withProps({
|
||||
@@ -360,8 +395,8 @@ export class MigrationCutoverDialog {
|
||||
iconHeight: '16px',
|
||||
iconWidth: '16px',
|
||||
label: loc.COPY_MIGRATION_DETAILS,
|
||||
height: '55px',
|
||||
width: '100px'
|
||||
height: '20px',
|
||||
width: '150px'
|
||||
}).component();
|
||||
|
||||
this._copyDatabaseMigrationDetails.onDidClick(async (e) => {
|
||||
@@ -378,22 +413,34 @@ export class MigrationCutoverDialog {
|
||||
vscode.window.showInformationMessage(loc.DETAILS_COPIED);
|
||||
});
|
||||
|
||||
header.addItem(this._copyDatabaseMigrationDetails, {
|
||||
headerActions.addItem(this._copyDatabaseMigrationDetails, {
|
||||
flex: '0',
|
||||
CSSStyles: {
|
||||
'width': '100px'
|
||||
'margin-left': '5px'
|
||||
}
|
||||
});
|
||||
|
||||
this._refreshLoader = this._view.modelBuilder.loadingComponent().withProps({
|
||||
loading: false,
|
||||
height: '55px'
|
||||
height: '15px'
|
||||
}).component();
|
||||
|
||||
header.addItem(this._refreshLoader, {
|
||||
headerActions.addItem(this._refreshLoader, {
|
||||
flex: '0',
|
||||
CSSStyles: {
|
||||
'margin-top': '15px'
|
||||
'margin-left': '16px'
|
||||
}
|
||||
});
|
||||
|
||||
const header = this._view.modelBuilder.flexContainer().withItems([
|
||||
titleLogoContainer
|
||||
]).withLayout({
|
||||
flexFlow: 'column'
|
||||
}).component();
|
||||
|
||||
header.addItem(headerActions, {
|
||||
'CSSStyles': {
|
||||
'margin-top': '16px'
|
||||
}
|
||||
});
|
||||
|
||||
@@ -461,19 +508,19 @@ export class MigrationCutoverDialog {
|
||||
|
||||
this._sourceDatabase.value = sourceDatabaseName;
|
||||
this._serverName.value = sqlServerName;
|
||||
this._serverVersion.value = `${sqlServerVersion}
|
||||
${sqlServerInfo.serverVersion}`;
|
||||
this._serverVersion.value = `${sqlServerVersion} ${sqlServerInfo.serverVersion}`;
|
||||
|
||||
this._targetDatabase.value = targetDatabaseName;
|
||||
this._targetServer.value = targetServerName;
|
||||
this._targetVersion.value = targetServerVersion;
|
||||
|
||||
this._migrationStatus.value = migrationStatusTextValue;
|
||||
this._fullBackupFile.value = fullBackupFileName!;
|
||||
this._migrationStatus.value = migrationStatusTextValue ?? '---';
|
||||
this._fullBackupFile.value = fullBackupFileName! ?? '-';
|
||||
this._backupLocation.value = this._model._migration.migrationContext.properties.backupConfiguration?.sourceLocation?.fileShare?.path! ?? '-';
|
||||
|
||||
this._lastAppliedLSN.value = lastAppliedSSN!;
|
||||
this._lastAppliedBackupFile.value = this._model.migrationStatus.properties.migrationStatusDetails?.lastRestoredFilename;
|
||||
this._lastAppliedBackupTakenOn.value = lastAppliedBackupFileTakenOn! ? new Date(lastAppliedBackupFileTakenOn).toLocaleString() : '';
|
||||
this._lastAppliedLSN.value = lastAppliedSSN! ?? '-';
|
||||
this._lastAppliedBackupFile.value = this._model.migrationStatus.properties.migrationStatusDetails?.lastRestoredFilename ?? '-';
|
||||
this._lastAppliedBackupTakenOn.value = lastAppliedBackupFileTakenOn! ? new Date(lastAppliedBackupFileTakenOn).toLocaleString() : '-';
|
||||
|
||||
this._fileCount.value = loc.ACTIVE_BACKUP_FILES_ITEMS(tableData.length);
|
||||
|
||||
@@ -495,7 +542,7 @@ export class MigrationCutoverDialog {
|
||||
}
|
||||
|
||||
if (migrationStatusTextValue === MigrationStatus.InProgress) {
|
||||
const fileNotRestored = await tableData.some(file => file.status !== 'Restored');
|
||||
const fileNotRestored = await tableData.some(file => file.status !== 'Restored' && file.status !== 'Ignored');
|
||||
this._cutoverButton.enabled = !fileNotRestored;
|
||||
this._cancelButton.enabled = true;
|
||||
} else {
|
||||
|
||||
@@ -10,6 +10,7 @@ import { MigrationContext, MigrationLocalStorage } from '../../models/migrationL
|
||||
import { MigrationCutoverDialog } from '../migrationCutover/migrationCutoverDialog';
|
||||
import { MigrationCategory, MigrationStatusDialogModel } from './migrationStatusDialogModel';
|
||||
import * as loc from '../../constants/strings';
|
||||
import { convertTimeDifferenceToDuration } from '../../api/utils';
|
||||
export class MigrationStatusDialog {
|
||||
private _model: MigrationStatusDialogModel;
|
||||
private _dialogObject!: azdata.window.Dialog;
|
||||
@@ -138,7 +139,7 @@ export class MigrationStatusDialog {
|
||||
const migrationRow: azdata.DeclarativeTableCellValue[] = [];
|
||||
|
||||
const databaseHyperLink = this._view.modelBuilder.hyperlink().withProps({
|
||||
label: migration.migrationContext.name,
|
||||
label: migration.migrationContext.properties.sourceDatabaseName,
|
||||
url: ''
|
||||
}).component();
|
||||
databaseHyperLink.onDidClick(async (e) => {
|
||||
@@ -148,13 +149,10 @@ export class MigrationStatusDialog {
|
||||
value: databaseHyperLink,
|
||||
});
|
||||
|
||||
const targetMigrationIcon = this._view.modelBuilder.image().withProps({
|
||||
iconPath: (migration.targetManagedInstance.type === 'microsoft.sql/managedinstances') ? IconPathHelper.sqlMiLogo : IconPathHelper.sqlVmLogo,
|
||||
iconWidth: '16px',
|
||||
iconHeight: '16px',
|
||||
width: '32px',
|
||||
height: '20px'
|
||||
}).component();
|
||||
migrationRow.push({
|
||||
value: (migration.targetManagedInstance.type === 'microsoft.sql/managedinstances') ? loc.SQL_MANAGED_INSTANCE : loc.SQL_VIRTUAL_MACHINE
|
||||
});
|
||||
|
||||
const sqlMigrationName = this._view.modelBuilder.hyperlink().withProps({
|
||||
label: migration.targetManagedInstance.name,
|
||||
url: ''
|
||||
@@ -163,25 +161,20 @@ export class MigrationStatusDialog {
|
||||
vscode.window.showInformationMessage(loc.COMING_SOON);
|
||||
});
|
||||
|
||||
const sqlMigrationContainer = this._view.modelBuilder.flexContainer().withProps({
|
||||
CSSStyles: {
|
||||
'justify-content': 'left'
|
||||
}
|
||||
}).component();
|
||||
sqlMigrationContainer.addItem(targetMigrationIcon, {
|
||||
flex: '0',
|
||||
CSSStyles: {
|
||||
'width': '32px'
|
||||
}
|
||||
});
|
||||
sqlMigrationContainer.addItem(sqlMigrationName,
|
||||
{
|
||||
CSSStyles: {
|
||||
'width': 'auto'
|
||||
}
|
||||
});
|
||||
migrationRow.push({
|
||||
value: sqlMigrationContainer
|
||||
value: sqlMigrationName
|
||||
});
|
||||
|
||||
const dms = this._view.modelBuilder.hyperlink().withProps({
|
||||
label: migration.controller.name,
|
||||
url: ''
|
||||
}).component();
|
||||
dms.onDidClick((e) => {
|
||||
vscode.window.showInformationMessage(loc.COMING_SOON);
|
||||
});
|
||||
|
||||
migrationRow.push({
|
||||
value: dms
|
||||
});
|
||||
|
||||
migrationRow.push({
|
||||
@@ -209,6 +202,17 @@ export class MigrationStatusDialog {
|
||||
value: loc.STATUS_WARNING_COUNT(migrationStatus, warningCount)
|
||||
});
|
||||
|
||||
let duration;
|
||||
if (migration.migrationContext.properties.endedOn) {
|
||||
duration = convertTimeDifferenceToDuration(new Date(migration.migrationContext.properties.startedOn), new Date(migration.migrationContext.properties.endedOn));
|
||||
} else {
|
||||
duration = convertTimeDifferenceToDuration(new Date(migration.migrationContext.properties.startedOn), new Date());
|
||||
}
|
||||
|
||||
migrationRow.push({
|
||||
value: (migration.migrationContext.properties.startedOn) ? duration : '---'
|
||||
});
|
||||
|
||||
migrationRow.push({
|
||||
value: (migration.migrationContext.properties.startedOn) ? new Date(migration.migrationContext.properties.startedOn).toLocaleString() : '---'
|
||||
});
|
||||
@@ -237,14 +241,16 @@ export class MigrationStatusDialog {
|
||||
const rowCssStyle: azdata.CssStyles = {
|
||||
'border': 'none',
|
||||
'text-align': 'left',
|
||||
'border-bottom': '1px solid'
|
||||
'border-bottom': '1px solid',
|
||||
};
|
||||
|
||||
const headerCssStyles: azdata.CssStyles = {
|
||||
'border': 'none',
|
||||
'text-align': 'left',
|
||||
'border-bottom': '1px solid',
|
||||
'font-weight': 'bold'
|
||||
'font-weight': 'bold',
|
||||
'padding-left': '0px',
|
||||
'padding-right': '0px'
|
||||
};
|
||||
|
||||
this._statusTable = this._view.modelBuilder.declarativeTable().withProps({
|
||||
@@ -252,7 +258,15 @@ export class MigrationStatusDialog {
|
||||
{
|
||||
displayName: loc.DATABASE,
|
||||
valueType: azdata.DeclarativeDataType.component,
|
||||
width: '100px',
|
||||
width: '90px',
|
||||
isReadOnly: true,
|
||||
rowCssStyles: rowCssStyle,
|
||||
headerCssStyles: headerCssStyles
|
||||
},
|
||||
{
|
||||
displayName: loc.AZURE_SQL_TARGET,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
width: '140px',
|
||||
isReadOnly: true,
|
||||
rowCssStyles: rowCssStyle,
|
||||
headerCssStyles: headerCssStyles
|
||||
@@ -260,7 +274,15 @@ export class MigrationStatusDialog {
|
||||
{
|
||||
displayName: loc.TARGET_AZURE_SQL_INSTANCE_NAME,
|
||||
valueType: azdata.DeclarativeDataType.component,
|
||||
width: '170px',
|
||||
width: '160px',
|
||||
isReadOnly: true,
|
||||
rowCssStyles: rowCssStyle,
|
||||
headerCssStyles: headerCssStyles
|
||||
},
|
||||
{
|
||||
displayName: loc.DATABASE_MIGRATION_SERVICE,
|
||||
valueType: azdata.DeclarativeDataType.component,
|
||||
width: '150px',
|
||||
isReadOnly: true,
|
||||
rowCssStyles: rowCssStyle,
|
||||
headerCssStyles: headerCssStyles
|
||||
@@ -281,6 +303,14 @@ export class MigrationStatusDialog {
|
||||
rowCssStyles: rowCssStyle,
|
||||
headerCssStyles: headerCssStyles
|
||||
},
|
||||
{
|
||||
displayName: loc.DURATION,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
width: '55px',
|
||||
isReadOnly: true,
|
||||
rowCssStyles: rowCssStyle,
|
||||
headerCssStyles: headerCssStyles
|
||||
},
|
||||
{
|
||||
displayName: loc.START_TIME,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
|
||||
@@ -27,11 +27,13 @@ export class MigrationLocalStorage {
|
||||
if (migration.sourceConnectionProfile.serverName === connectionProfile.serverName) {
|
||||
if (refreshStatus) {
|
||||
try {
|
||||
const backupConfiguration = migration.migrationContext.properties.backupConfiguration;
|
||||
migration.migrationContext = await getMigrationStatus(
|
||||
migration.azureAccount,
|
||||
migration.subscription,
|
||||
migration.migrationContext
|
||||
);
|
||||
migration.migrationContext.properties.backupConfiguration = backupConfiguration;
|
||||
if (migration.asyncUrl) {
|
||||
migration.asyncOperationResult = await getMigrationAsyncOperationDetails(
|
||||
migration.azureAccount,
|
||||
|
||||
@@ -602,10 +602,10 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
}
|
||||
|
||||
|
||||
public async getSqlMigrationServiceValues(subscription: azureResource.AzureResourceSubscription, managedInstance: SqlManagedInstance): Promise<azdata.CategoryValue[]> {
|
||||
public async getSqlMigrationServiceValues(subscription: azureResource.AzureResourceSubscription, managedInstance: SqlManagedInstance, resourceGroupName: string): Promise<azdata.CategoryValue[]> {
|
||||
let sqlMigrationServiceValues: azdata.CategoryValue[] = [];
|
||||
try {
|
||||
this._sqlMigrationServices = (await getSqlMigrationServices(this._azureAccount, subscription, managedInstance.location)).filter(sms => sms.location.toLowerCase() === this._targetServerInstance.location.toLowerCase());
|
||||
this._sqlMigrationServices = (await getSqlMigrationServices(this._azureAccount, subscription, managedInstance.location)).filter(sms => sms.location.toLowerCase() === this._targetServerInstance.location.toLowerCase() && sms.properties.resourceGroup.toLowerCase() === resourceGroupName?.toLowerCase());
|
||||
this._sqlMigrationServices.forEach((sqlMigrationService) => {
|
||||
sqlMigrationServiceValues.push({
|
||||
name: sqlMigrationService.id,
|
||||
@@ -687,6 +687,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
this._targetDatabaseNames[i],
|
||||
requestBody
|
||||
);
|
||||
response.databaseMigration.properties.backupConfiguration = requestBody.properties.backupConfiguration!;
|
||||
if (response.status === 201 || response.status === 200) {
|
||||
MigrationLocalStorage.saveMigration(
|
||||
currentConnection!,
|
||||
|
||||
@@ -48,23 +48,26 @@ export class AccountsSelectionPage extends MigrationWizardPage {
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||
})
|
||||
.withValidation((c) => {
|
||||
if ((<azdata.CategoryValue>c.value).displayName === constants.ACCOUNT_SELECTION_PAGE_NO_LINKED_ACCOUNTS_ERROR) {
|
||||
if (c.value) {
|
||||
if ((<azdata.CategoryValue>c.value).displayName === constants.ACCOUNT_SELECTION_PAGE_NO_LINKED_ACCOUNTS_ERROR) {
|
||||
this.wizard.message = {
|
||||
text: constants.ACCOUNT_SELECTION_PAGE_NO_LINKED_ACCOUNTS_ERROR,
|
||||
level: azdata.window.MessageLevel.Error
|
||||
};
|
||||
return false;
|
||||
}
|
||||
if (this.migrationStateModel._azureAccount?.isStale) {
|
||||
this.wizard.message = {
|
||||
text: constants.ACCOUNT_STALE_ERROR(this.migrationStateModel._azureAccount)
|
||||
};
|
||||
return false;
|
||||
}
|
||||
this.wizard.message = {
|
||||
text: constants.ACCOUNT_SELECTION_PAGE_NO_LINKED_ACCOUNTS_ERROR,
|
||||
level: azdata.window.MessageLevel.Error
|
||||
text: ''
|
||||
};
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
if (this.migrationStateModel._azureAccount?.isStale) {
|
||||
this.wizard.message = {
|
||||
text: constants.ACCOUNT_STALE_ERROR(this.migrationStateModel._azureAccount)
|
||||
};
|
||||
return false;
|
||||
}
|
||||
this.wizard.message = {
|
||||
text: ''
|
||||
};
|
||||
return true;
|
||||
return false;
|
||||
}).component();
|
||||
|
||||
this._azureAccountsDropdown.onValueChanged(async (value) => {
|
||||
|
||||
@@ -9,17 +9,30 @@ import { MigrationWizardPage } from '../models/migrationWizardPage';
|
||||
import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
|
||||
import { CreateSqlMigrationServiceDialog } from '../dialog/createSqlMigrationService/createSqlMigrationServiceDialog';
|
||||
import * as constants from '../constants/strings';
|
||||
import { createInformationRow, WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
|
||||
import { getSqlMigrationService, getSqlMigrationServiceAuthKeys, getSqlMigrationServiceMonitoringData, SqlMigrationService } from '../api/azure';
|
||||
import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
|
||||
import { getLocationDisplayName, getSqlMigrationService, getSqlMigrationServiceAuthKeys, getSqlMigrationServiceMonitoringData, SqlMigrationService } from '../api/azure';
|
||||
import { IconPathHelper } from '../constants/iconPathHelper';
|
||||
|
||||
export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
|
||||
private migrationServiceDropdown!: azdata.DropDownComponent;
|
||||
private _view!: azdata.ModelView;
|
||||
private _form!: azdata.FormBuilder;
|
||||
private _statusLoadingComponent!: azdata.LoadingComponent;
|
||||
private _migrationDetailsContainer!: azdata.FlexContainer;
|
||||
private _subscription!: azdata.InputBoxComponent;
|
||||
private _location!: azdata.InputBoxComponent;
|
||||
private _resourceGroupDropdown!: azdata.DropDownComponent;
|
||||
private _dmsDropdown!: azdata.DropDownComponent;
|
||||
|
||||
private _dmsStatusInfoBox!: azdata.InfoBoxComponent;
|
||||
private _authKeyTable!: azdata.DeclarativeTableComponent;
|
||||
private _refreshButton!: azdata.ButtonComponent;
|
||||
private _connectionStatusLoader!: azdata.LoadingComponent;
|
||||
|
||||
private _copy1!: azdata.ButtonComponent;
|
||||
private _copy2!: azdata.ButtonComponent;
|
||||
private _refresh1!: azdata.ButtonComponent;
|
||||
private _refresh2!: azdata.ButtonComponent;
|
||||
|
||||
|
||||
constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) {
|
||||
super(wizard, azdata.window.createWizardPage(constants.IR_PAGE_TITLE), migrationStateModel);
|
||||
@@ -30,7 +43,10 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
|
||||
const createNewMigrationService = view.modelBuilder.hyperlink().withProps({
|
||||
label: constants.CREATE_NEW,
|
||||
url: ''
|
||||
url: '',
|
||||
CSSStyles: {
|
||||
'font-size': '13px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
createNewMigrationService.onDidClick((e) => {
|
||||
@@ -38,10 +54,7 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
dialog.initialize();
|
||||
});
|
||||
|
||||
this._migrationDetailsContainer = view.modelBuilder.flexContainer().withLayout({
|
||||
flexFlow: 'column'
|
||||
}).component();
|
||||
this._statusLoadingComponent = view.modelBuilder.loadingComponent().withItem(this._migrationDetailsContainer).component();
|
||||
this._statusLoadingComponent = view.modelBuilder.loadingComponent().withItem(this.createDMSDetailsContainer()).component();
|
||||
|
||||
this._form = view.modelBuilder.formContainer()
|
||||
.withFormItems(
|
||||
@@ -104,60 +117,305 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
|
||||
private migrationServiceDropdownContainer(): azdata.FlexContainer {
|
||||
const descriptionText = this._view.modelBuilder.text().withProps({
|
||||
value: constants.IR_PAGE_DESCRIPTION
|
||||
value: constants.IR_PAGE_DESCRIPTION,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
}
|
||||
}).component();
|
||||
|
||||
const subscriptionLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.SUBSCRIPTION,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold',
|
||||
}
|
||||
}).component();
|
||||
this._subscription = this._view.modelBuilder.inputBox().withProps({
|
||||
enabled: false,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
}).component();
|
||||
|
||||
const locationLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.LOCATION,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold',
|
||||
}
|
||||
}).component();
|
||||
this._location = this._view.modelBuilder.inputBox().withProps({
|
||||
enabled: false,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
}).component();
|
||||
|
||||
|
||||
const resourceGroupLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.RESOURCE_GROUP,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold',
|
||||
}
|
||||
}).component();
|
||||
this._resourceGroupDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
editable: true
|
||||
}).component();
|
||||
|
||||
this._resourceGroupDropdown.onValueChanged(async (value) => {
|
||||
if (value) {
|
||||
this.populateDms(value);
|
||||
}
|
||||
});
|
||||
|
||||
const migrationServcieDropdownLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.SELECT_A_SQL_MIGRATION_SERVICE
|
||||
value: constants.IR_PAGE_TITLE,
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'font-weight': 'bold',
|
||||
}
|
||||
}).component();
|
||||
|
||||
this.migrationServiceDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||
this._dmsDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||
required: true,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
editable: true
|
||||
}).component();
|
||||
|
||||
this.migrationServiceDropdown.onValueChanged(async (value) => {
|
||||
if (value.selected) {
|
||||
this._dmsDropdown.onValueChanged(async (value) => {
|
||||
if (value && value !== constants.SQL_MIGRATION_SERVICE_NOT_FOUND_ERROR) {
|
||||
this.wizard.message = {
|
||||
text: ''
|
||||
};
|
||||
this.migrationStateModel._sqlMigrationService = this.migrationStateModel.getMigrationService(value.index);
|
||||
if (value !== constants.SQL_MIGRATION_SERVICE_NOT_FOUND_ERROR) {
|
||||
await this.loadMigrationServiceStatus();
|
||||
}
|
||||
const selectedIndex = (<azdata.CategoryValue[]>this._dmsDropdown.values)?.findIndex((v) => v.displayName === value);
|
||||
this.migrationStateModel._sqlMigrationService = this.migrationStateModel.getMigrationService(selectedIndex);
|
||||
this.loadMigrationServiceStatus();
|
||||
}
|
||||
});
|
||||
|
||||
const flexContainer = this._view.modelBuilder.flexContainer().withItems([
|
||||
descriptionText,
|
||||
subscriptionLabel,
|
||||
this._subscription,
|
||||
locationLabel,
|
||||
this._location,
|
||||
resourceGroupLabel,
|
||||
this._resourceGroupDropdown,
|
||||
migrationServcieDropdownLabel,
|
||||
this.migrationServiceDropdown
|
||||
this._dmsDropdown
|
||||
]).withLayout({
|
||||
flexFlow: 'column'
|
||||
}).component();
|
||||
return flexContainer;
|
||||
}
|
||||
|
||||
public async populateMigrationService(sqlMigrationService?: SqlMigrationService, serviceNodes?: string[]): Promise<void> {
|
||||
this.migrationServiceDropdown.loading = true;
|
||||
private createDMSDetailsContainer(): azdata.FlexContainer {
|
||||
const container = this._view.modelBuilder.flexContainer().withLayout({
|
||||
flexFlow: 'column'
|
||||
}).component();
|
||||
|
||||
const connectionStatusLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.SERVICE_CONNECTION_STATUS,
|
||||
CSSStyles: {
|
||||
'font-weight': 'bold',
|
||||
'font-size': '13px',
|
||||
'width': '130px',
|
||||
'margin': '0'
|
||||
}
|
||||
}).component();
|
||||
|
||||
this._refreshButton = this._view.modelBuilder.button().withProps({
|
||||
iconWidth: '18px',
|
||||
iconHeight: '18px',
|
||||
iconPath: IconPathHelper.refresh,
|
||||
height: '18px',
|
||||
width: '18px'
|
||||
}).component();
|
||||
|
||||
this._refreshButton.onDidClick((e) => {
|
||||
this.loadStatus();
|
||||
});
|
||||
|
||||
const connectionLabelContainer = this._view.modelBuilder.flexContainer().withProps({
|
||||
CSSStyles: {
|
||||
'margin-bottom': '13px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
connectionLabelContainer.addItem(connectionStatusLabel, {
|
||||
flex: '0'
|
||||
});
|
||||
|
||||
connectionLabelContainer.addItem(this._refreshButton, {
|
||||
flex: '0',
|
||||
CSSStyles: { 'margin-right': '10px' }
|
||||
});
|
||||
|
||||
const statusContainer = this._view.modelBuilder.flexContainer().withLayout({
|
||||
flexFlow: 'column'
|
||||
}).component();
|
||||
|
||||
this._dmsStatusInfoBox = this._view.modelBuilder.infoBox().withProps({
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
style: 'error',
|
||||
text: '',
|
||||
CSSStyles: {
|
||||
'font-size': '13px'
|
||||
}
|
||||
}).withValidation(component => {
|
||||
if (component.style === 'error') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}).component();
|
||||
|
||||
const authenticationKeysLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.AUTHENTICATION_KEYS,
|
||||
CSSStyles: {
|
||||
'font-weight': 'bold',
|
||||
'font-size': '13px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
|
||||
this._copy1 = this._view.modelBuilder.button().withProps({
|
||||
iconPath: IconPathHelper.copy,
|
||||
}).component();
|
||||
|
||||
this._copy1.onDidClick(async (e) => {
|
||||
await vscode.env.clipboard.writeText(<string>this._authKeyTable.dataValues![0][1].value);
|
||||
vscode.window.showInformationMessage(constants.SERVICE_KEY_COPIED_HELP);
|
||||
});
|
||||
|
||||
this._copy2 = this._view.modelBuilder.button().withProps({
|
||||
iconPath: IconPathHelper.copy
|
||||
}).component();
|
||||
|
||||
this._copy2.onDidClick(async (e) => {
|
||||
await vscode.env.clipboard.writeText(<string>this._authKeyTable.dataValues![1][1].value);
|
||||
vscode.window.showInformationMessage(constants.SERVICE_KEY_COPIED_HELP);
|
||||
});
|
||||
|
||||
this._refresh1 = this._view.modelBuilder.button().withProps({
|
||||
iconPath: IconPathHelper.refresh
|
||||
}).component();
|
||||
|
||||
this._refresh2 = this._view.modelBuilder.button().withProps({
|
||||
iconPath: IconPathHelper.refresh,
|
||||
}).component();
|
||||
|
||||
this._authKeyTable = this._view.modelBuilder.declarativeTable().withProps({
|
||||
columns: [
|
||||
{
|
||||
displayName: constants.NAME,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
width: '50px',
|
||||
isReadOnly: true,
|
||||
rowCssStyles: {
|
||||
'font-size': '13px'
|
||||
},
|
||||
headerCssStyles: {
|
||||
'font-size': '13px'
|
||||
}
|
||||
},
|
||||
{
|
||||
displayName: constants.AUTH_KEY_COLUMN_HEADER,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
width: '500px',
|
||||
isReadOnly: true,
|
||||
rowCssStyles: {
|
||||
'font-size': '13px',
|
||||
|
||||
},
|
||||
headerCssStyles: {
|
||||
'font-size': '13px'
|
||||
}
|
||||
},
|
||||
{
|
||||
displayName: '',
|
||||
valueType: azdata.DeclarativeDataType.component,
|
||||
width: '30px',
|
||||
isReadOnly: true,
|
||||
rowCssStyles: {
|
||||
'font-size': '13px'
|
||||
},
|
||||
headerCssStyles: {
|
||||
'font-size': '13px'
|
||||
}
|
||||
}
|
||||
],
|
||||
CSSStyles: {
|
||||
'margin-top': '5px',
|
||||
'width': WIZARD_INPUT_COMPONENT_WIDTH
|
||||
}
|
||||
}).component();
|
||||
|
||||
statusContainer.addItems([
|
||||
this._dmsStatusInfoBox,
|
||||
authenticationKeysLabel,
|
||||
this._authKeyTable
|
||||
]);
|
||||
|
||||
this._connectionStatusLoader = this._view.modelBuilder.loadingComponent().withItem(
|
||||
statusContainer
|
||||
).component();
|
||||
|
||||
container.addItems(
|
||||
[
|
||||
connectionLabelContainer,
|
||||
this._connectionStatusLoader
|
||||
]
|
||||
);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
public async populateMigrationService(sqlMigrationService?: SqlMigrationService, serviceNodes?: string[], resourceGroupName?: string): Promise<void> {
|
||||
this._resourceGroupDropdown.loading = true;
|
||||
this._dmsDropdown.loading = true;
|
||||
if (sqlMigrationService && serviceNodes) {
|
||||
this.migrationStateModel._sqlMigrationService = sqlMigrationService;
|
||||
this.migrationStateModel._nodeNames = serviceNodes;
|
||||
}
|
||||
|
||||
try {
|
||||
this.migrationServiceDropdown.values = await this.migrationStateModel.getSqlMigrationServiceValues(this.migrationStateModel._targetSubscription, this.migrationStateModel._targetServerInstance);
|
||||
if (this.migrationStateModel._sqlMigrationService) {
|
||||
this.migrationServiceDropdown.value = {
|
||||
name: this.migrationStateModel._sqlMigrationService.id,
|
||||
displayName: this.migrationStateModel._sqlMigrationService.name
|
||||
};
|
||||
this._subscription.value = this.migrationStateModel._targetSubscription.name;
|
||||
this._location.value = await getLocationDisplayName(this.migrationStateModel._targetServerInstance.location);
|
||||
this._resourceGroupDropdown.values = await this.migrationStateModel.getAzureResourceGroupDropdownValues(this.migrationStateModel._targetSubscription);
|
||||
if (resourceGroupName) {
|
||||
const index = (<azdata.CategoryValue[]>this._resourceGroupDropdown.values).findIndex(v => v.displayName.toLowerCase() === resourceGroupName.toLowerCase());
|
||||
if (resourceGroupName.toLowerCase() === (<azdata.CategoryValue>this._resourceGroupDropdown.value).displayName.toLowerCase()) {
|
||||
this.populateDms(resourceGroupName);
|
||||
} else {
|
||||
this._resourceGroupDropdown.value = this._resourceGroupDropdown.values[index];
|
||||
}
|
||||
} else {
|
||||
this.migrationStateModel._sqlMigrationService = this.migrationStateModel.getMigrationService(0);
|
||||
this._resourceGroupDropdown.value = this._resourceGroupDropdown.values[0];
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
this.migrationServiceDropdown.loading = false;
|
||||
this._resourceGroupDropdown.loading = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async populateDms(resourceGroupName: string): Promise<void> {
|
||||
this._dmsDropdown.loading = true;
|
||||
try {
|
||||
this._dmsDropdown.values = await this.migrationStateModel.getSqlMigrationServiceValues(this.migrationStateModel._targetSubscription, this.migrationStateModel._targetServerInstance, resourceGroupName);
|
||||
let index = -1;
|
||||
if (this.migrationStateModel._sqlMigrationService) {
|
||||
index = (<azdata.CategoryValue[]>this._dmsDropdown.values).findIndex(v => v.displayName.toLowerCase() === this.migrationStateModel._sqlMigrationService.name.toLowerCase());
|
||||
}
|
||||
if (index !== -1) {
|
||||
this._dmsDropdown.value = this._dmsDropdown.values[index];
|
||||
} else {
|
||||
this._dmsDropdown.value = this._dmsDropdown.values[0];
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} finally {
|
||||
this._dmsDropdown.loading = false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -165,8 +423,17 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
private async loadMigrationServiceStatus(): Promise<void> {
|
||||
this._statusLoadingComponent.loading = true;
|
||||
try {
|
||||
this._migrationDetailsContainer.clearItems();
|
||||
await this.loadStatus();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
this._statusLoadingComponent.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async loadStatus(): Promise<void> {
|
||||
this._connectionStatusLoader.loading = true;
|
||||
try {
|
||||
if (this.migrationStateModel._sqlMigrationService) {
|
||||
const migrationService = await getSqlMigrationService(
|
||||
this.migrationStateModel._azureAccount,
|
||||
@@ -181,9 +448,7 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
this.migrationStateModel._sqlMigrationService.properties.resourceGroup,
|
||||
this.migrationStateModel._sqlMigrationService.location,
|
||||
this.migrationStateModel._sqlMigrationService!.name);
|
||||
this.migrationStateModel._nodeNames = migrationServiceMonitoringStatus.nodes.map((node) => {
|
||||
return node.nodeName;
|
||||
});
|
||||
this.migrationStateModel._nodeNames = migrationServiceMonitoringStatus.nodes.map(node => node.nodeName);
|
||||
const migrationServiceAuthKeys = await getSqlMigrationServiceAuthKeys(
|
||||
this.migrationStateModel._azureAccount,
|
||||
this.migrationStateModel._targetSubscription,
|
||||
@@ -192,228 +457,61 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
this.migrationStateModel._sqlMigrationService!.name
|
||||
);
|
||||
|
||||
const migrationServiceTitle = this._view.modelBuilder.text().withProps({
|
||||
value: constants.SQL_MIGRATION_SERVICE_DETAILS_HEADER(migrationService.name),
|
||||
CSSStyles: {
|
||||
'font-weight': 'bold'
|
||||
}
|
||||
}).component();
|
||||
|
||||
const connectionStatusLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.SERVICE_CONNECTION_STATUS,
|
||||
CSSStyles: {
|
||||
'font-weight': 'bold',
|
||||
'width': '150px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
const refreshStatus = this._view.modelBuilder.button().withProps({
|
||||
label: constants.REFRESH,
|
||||
secondary: true,
|
||||
width: '50px'
|
||||
}).component();
|
||||
|
||||
|
||||
|
||||
const connectionLabelContainer = this._view.modelBuilder.flexContainer().withLayout({
|
||||
flexFlow: 'row',
|
||||
alignItems: 'center'
|
||||
}).withItems(
|
||||
[
|
||||
connectionStatusLabel,
|
||||
refreshStatus
|
||||
],
|
||||
{
|
||||
CSSStyles: { 'margin-right': '5px' }
|
||||
}
|
||||
).component();
|
||||
|
||||
const connectionStatus = this._view.modelBuilder.infoBox().component();
|
||||
const connectionStatusLoader = this._view.modelBuilder.loadingComponent().withItem(connectionStatus).withProps({
|
||||
loading: false
|
||||
}).component();
|
||||
refreshStatus.onDidClick(async (e) => {
|
||||
connectionStatusLoader.loading = true;
|
||||
|
||||
const migrationService = await getSqlMigrationService(
|
||||
this.migrationStateModel._azureAccount,
|
||||
this.migrationStateModel._targetSubscription,
|
||||
this.migrationStateModel._sqlMigrationService.properties.resourceGroup,
|
||||
this.migrationStateModel._sqlMigrationService.location,
|
||||
this.migrationStateModel._sqlMigrationService.name);
|
||||
this.migrationStateModel._sqlMigrationService = migrationService;
|
||||
const migrationServiceMonitoringStatus = await getSqlMigrationServiceMonitoringData(
|
||||
this.migrationStateModel._azureAccount,
|
||||
this.migrationStateModel._targetSubscription,
|
||||
this.migrationStateModel._sqlMigrationService.properties.resourceGroup,
|
||||
this.migrationStateModel._sqlMigrationService.location,
|
||||
this.migrationStateModel._sqlMigrationService!.name);
|
||||
this.migrationStateModel._nodeNames = migrationServiceMonitoringStatus.nodes.map((node) => {
|
||||
return node.nodeName;
|
||||
});
|
||||
|
||||
const state = migrationService.properties.integrationRuntimeState;
|
||||
if (state === 'Online') {
|
||||
connectionStatus.updateProperties(<azdata.InfoBoxComponentProperties>{
|
||||
text: constants.SERVICE_READY(this.migrationStateModel._sqlMigrationService!.name, this.migrationStateModel._nodeNames.join(', ')),
|
||||
style: 'success'
|
||||
});
|
||||
} else {
|
||||
connectionStatus.updateProperties(<azdata.InfoBoxComponentProperties>{
|
||||
text: constants.SERVICE_NOT_READY(this.migrationStateModel._sqlMigrationService!.name),
|
||||
style: 'error'
|
||||
});
|
||||
}
|
||||
|
||||
connectionStatusLoader.loading = false;
|
||||
this.migrationStateModel._nodeNames = migrationServiceMonitoringStatus.nodes.map((node) => {
|
||||
return node.nodeName;
|
||||
});
|
||||
|
||||
const state = migrationService.properties.integrationRuntimeState;
|
||||
if (migrationService) {
|
||||
if (state === 'Online') {
|
||||
connectionStatus.updateProperties(<azdata.InfoBoxComponentProperties>{
|
||||
text: constants.SERVICE_READY(this.migrationStateModel._sqlMigrationService!.name, this.migrationStateModel._nodeNames.join(', ')),
|
||||
style: 'success'
|
||||
});
|
||||
} else {
|
||||
connectionStatus.updateProperties(<azdata.InfoBoxComponentProperties>{
|
||||
text: constants.SERVICE_NOT_READY(this.migrationStateModel._sqlMigrationService!.name),
|
||||
style: 'error'
|
||||
});
|
||||
}
|
||||
if (state === 'Online') {
|
||||
this._dmsStatusInfoBox.updateProperties(<azdata.InfoBoxComponentProperties>{
|
||||
text: constants.SERVICE_READY(this.migrationStateModel._sqlMigrationService!.name, this.migrationStateModel._nodeNames.join(', ')),
|
||||
style: 'success'
|
||||
});
|
||||
} else {
|
||||
this._dmsStatusInfoBox.updateProperties(<azdata.InfoBoxComponentProperties>{
|
||||
text: constants.SERVICE_NOT_READY(this.migrationStateModel._sqlMigrationService!.name),
|
||||
style: 'error'
|
||||
});
|
||||
}
|
||||
|
||||
const authenticationKeysLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.AUTHENTICATION_KEYS,
|
||||
CSSStyles: {
|
||||
'font-weight': 'bold'
|
||||
}
|
||||
}).component();
|
||||
this._dmsStatusInfoBox.validate();
|
||||
|
||||
const migrationServiceAuthKeyTable = this._view.modelBuilder.declarativeTable().withProps({
|
||||
columns: [
|
||||
|
||||
|
||||
const data = [
|
||||
[
|
||||
{
|
||||
displayName: constants.NAME,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
width: '50px',
|
||||
isReadOnly: true,
|
||||
rowCssStyles: {
|
||||
'text-align': 'center'
|
||||
}
|
||||
value: constants.SERVICE_KEY1_LABEL
|
||||
},
|
||||
{
|
||||
displayName: constants.AUTH_KEY_COLUMN_HEADER,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
width: '500px',
|
||||
isReadOnly: true,
|
||||
rowCssStyles: {
|
||||
overflow: 'scroll'
|
||||
}
|
||||
value: migrationServiceAuthKeys.authKey1
|
||||
},
|
||||
{
|
||||
displayName: '',
|
||||
valueType: azdata.DeclarativeDataType.component,
|
||||
width: '15px',
|
||||
isReadOnly: true,
|
||||
},
|
||||
{
|
||||
displayName: '',
|
||||
valueType: azdata.DeclarativeDataType.component,
|
||||
width: '15px',
|
||||
isReadOnly: true,
|
||||
value: this._view.modelBuilder.flexContainer().withItems([this._copy1, this._refresh1]).component()
|
||||
}
|
||||
],
|
||||
CSSStyles: {
|
||||
'margin-top': '5px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
|
||||
const copyKey1Button = this._view.modelBuilder.button().withProps({
|
||||
iconPath: IconPathHelper.copy
|
||||
}).component();
|
||||
|
||||
copyKey1Button.onDidClick((e) => {
|
||||
vscode.env.clipboard.writeText(<string>migrationServiceAuthKeyTable.dataValues![0][1].value);
|
||||
vscode.window.showInformationMessage(constants.SERVICE_KEY_COPIED_HELP);
|
||||
});
|
||||
|
||||
const copyKey2Button = this._view.modelBuilder.button().withProps({
|
||||
iconPath: IconPathHelper.copy
|
||||
}).component();
|
||||
|
||||
copyKey2Button.onDidClick((e) => {
|
||||
vscode.env.clipboard.writeText(<string>migrationServiceAuthKeyTable.dataValues![1][1].value);
|
||||
vscode.window.showInformationMessage(constants.SERVICE_KEY_COPIED_HELP);
|
||||
});
|
||||
|
||||
const refreshKey1Button = this._view.modelBuilder.button().withProps({
|
||||
iconPath: IconPathHelper.refresh
|
||||
}).component();
|
||||
|
||||
refreshKey1Button.onDidClick((e) => {//TODO: add refresh logic
|
||||
});
|
||||
|
||||
const refreshKey2Button = this._view.modelBuilder.button().withProps({
|
||||
iconPath: IconPathHelper.refresh
|
||||
}).component();
|
||||
|
||||
refreshKey2Button.onDidClick((e) => {//TODO: add refresh logic
|
||||
});
|
||||
|
||||
migrationServiceAuthKeyTable.updateProperties({
|
||||
dataValues: [
|
||||
[
|
||||
{
|
||||
value: constants.SERVICE_KEY1_LABEL
|
||||
},
|
||||
{
|
||||
value: migrationServiceAuthKeys.authKey1
|
||||
},
|
||||
{
|
||||
value: copyKey1Button
|
||||
},
|
||||
{
|
||||
value: refreshKey1Button
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
value: constants.SERVICE_KEY2_LABEL
|
||||
},
|
||||
{
|
||||
value: migrationServiceAuthKeys.authKey2
|
||||
},
|
||||
{
|
||||
value: copyKey2Button
|
||||
},
|
||||
{
|
||||
value: refreshKey2Button
|
||||
}
|
||||
]
|
||||
]
|
||||
});
|
||||
|
||||
this._migrationDetailsContainer.addItems(
|
||||
[
|
||||
migrationServiceTitle,
|
||||
createInformationRow(this._view, constants.SUBSCRIPTION, this.migrationStateModel._targetSubscription.name),
|
||||
createInformationRow(this._view, constants.RESOURCE_GROUP, migrationService.properties.resourceGroup),
|
||||
createInformationRow(this._view, constants.LOCATION, await this.migrationStateModel.getLocationDisplayName(migrationService.location)),
|
||||
connectionLabelContainer,
|
||||
connectionStatusLoader,
|
||||
authenticationKeysLabel,
|
||||
migrationServiceAuthKeyTable
|
||||
{
|
||||
value: constants.SERVICE_KEY2_LABEL
|
||||
},
|
||||
{
|
||||
value: migrationServiceAuthKeys.authKey2
|
||||
},
|
||||
{
|
||||
value: this._view.modelBuilder.flexContainer().withItems([this._copy2, this._refresh2]).component()
|
||||
}
|
||||
]
|
||||
);
|
||||
];
|
||||
|
||||
this._authKeyTable.updateProperties({
|
||||
dataValues: data
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
this._migrationDetailsContainer.clearItems();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} finally {
|
||||
this._statusLoadingComponent.loading = false;
|
||||
this._connectionStatusLoader.loading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user