mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-14 01:25:37 -05:00
679 lines
24 KiB
TypeScript
679 lines
24 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 * as vscode from 'vscode';
|
|
import { MigrationWizardPage } from '../models/migrationWizardPage';
|
|
import { MigrationMode, MigrationStateModel, NetworkContainerType, StateChangeEvent } from '../models/stateMachine';
|
|
import { CreateSqlMigrationServiceDialog } from '../dialog/createSqlMigrationService/createSqlMigrationServiceDialog';
|
|
import * as constants from '../constants/strings';
|
|
import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
|
|
import { getFullResourceGroupFromId, getSqlMigrationService, getSqlMigrationServiceMonitoringData, SqlVMServer } from '../api/azure';
|
|
import { IconPathHelper } from '../constants/iconPathHelper';
|
|
import { logError, TelemetryViews } from '../telemetry';
|
|
import * as utils from '../api/utils';
|
|
import * as styles from '../constants/styles';
|
|
|
|
export class IntergrationRuntimePage extends MigrationWizardPage {
|
|
private _view!: azdata.ModelView;
|
|
private _statusLoadingComponent!: azdata.LoadingComponent;
|
|
private _subscriptionDropdown!: azdata.DropDownComponent;
|
|
private _location!: azdata.TextComponent;
|
|
private _resourceGroupDropdown!: azdata.DropDownComponent;
|
|
private _dmsDropdown!: azdata.DropDownComponent;
|
|
private _dmsInfoContainer!: azdata.FlexContainer;
|
|
private _dmsStatusInfoBox!: azdata.InfoBoxComponent;
|
|
private _authKeyTable!: azdata.DeclarativeTableComponent;
|
|
private _refreshButton!: azdata.ButtonComponent;
|
|
private _onlineButton!: azdata.RadioButtonComponent;
|
|
private _offlineButton!: azdata.RadioButtonComponent;
|
|
private _modeContainer!: azdata.FlexContainer;
|
|
private _radioButtonContainer!: azdata.FlexContainer;
|
|
private _networkShareButton!: azdata.RadioButtonComponent;
|
|
private _blobContainerButton!: azdata.RadioButtonComponent;
|
|
private _sqlVmPageBlobInfoBox!: azdata.TextComponent;
|
|
private _originalMigrationMode!: MigrationMode;
|
|
private _disposables: vscode.Disposable[] = [];
|
|
|
|
constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) {
|
|
super(wizard, azdata.window.createWizardPage(constants.IR_PAGE_TITLE), migrationStateModel);
|
|
this.migrationStateModel._databaseBackup.migrationMode =
|
|
this.migrationStateModel._databaseBackup.migrationMode ||
|
|
this.migrationStateModel.isSqlDbTarget
|
|
? MigrationMode.OFFLINE
|
|
: MigrationMode.ONLINE;
|
|
}
|
|
|
|
protected async registerContent(view: azdata.ModelView): Promise<void> {
|
|
this._view = view;
|
|
|
|
this._statusLoadingComponent = view.modelBuilder.loadingComponent()
|
|
.withItem(this.createDMSDetailsContainer())
|
|
.component();
|
|
|
|
this._dmsInfoContainer = this._view.modelBuilder.flexContainer()
|
|
.withItems([this._statusLoadingComponent])
|
|
.component();
|
|
|
|
this._radioButtonContainer = this.createBackupLocationComponent();
|
|
this._modeContainer = this.migrationModeContainer();
|
|
|
|
const form = view.modelBuilder.formContainer()
|
|
.withFormItems([
|
|
{ component: this._modeContainer },
|
|
{ component: this._radioButtonContainer },
|
|
{ component: this.migrationServiceDropdownContainer() },
|
|
{ component: this._dmsInfoContainer }])
|
|
.withProps({ CSSStyles: { 'padding-top': '0' } })
|
|
.component();
|
|
|
|
this._disposables.push(
|
|
this._view.onClosed(e =>
|
|
this._disposables.forEach(
|
|
d => { try { d.dispose(); } catch { } })));
|
|
|
|
await view.initializeModel(form);
|
|
}
|
|
|
|
private migrationModeContainer(): azdata.FlexContainer {
|
|
const buttonGroup = 'migrationMode';
|
|
this._onlineButton = this._view.modelBuilder.radioButton()
|
|
.withProps({
|
|
label: constants.DATABASE_BACKUP_MIGRATION_MODE_ONLINE_LABEL,
|
|
name: buttonGroup,
|
|
checked: this.migrationStateModel._databaseBackup.migrationMode === MigrationMode.ONLINE,
|
|
CSSStyles: { ...styles.LABEL_CSS, },
|
|
}).component();
|
|
const onlineDescription = this._view.modelBuilder.text()
|
|
.withProps({
|
|
value: constants.DATABASE_BACKUP_MIGRATION_MODE_ONLINE_DESCRIPTION,
|
|
CSSStyles: { ...styles.NOTE_CSS, 'margin-left': '20px' }
|
|
}).component();
|
|
this._disposables.push(
|
|
this._onlineButton.onDidChangeCheckedState(checked => {
|
|
if (checked) {
|
|
this.migrationStateModel._databaseBackup.migrationMode = MigrationMode.ONLINE;
|
|
this.migrationStateModel.refreshDatabaseBackupPage = true;
|
|
}
|
|
}));
|
|
|
|
this._offlineButton = this._view.modelBuilder.radioButton()
|
|
.withProps({
|
|
label: constants.DATABASE_BACKUP_MIGRATION_MODE_OFFLINE_LABEL,
|
|
name: buttonGroup,
|
|
checked: this.migrationStateModel._databaseBackup.migrationMode === MigrationMode.OFFLINE,
|
|
CSSStyles: { ...styles.LABEL_CSS, 'margin-top': '12px' },
|
|
}).component();
|
|
const offlineDescription = this._view.modelBuilder.text()
|
|
.withProps({
|
|
value: constants.DATABASE_BACKUP_MIGRATION_MODE_OFFLINE_DESCRIPTION,
|
|
CSSStyles: { ...styles.NOTE_CSS, 'margin-left': '20px' }
|
|
}).component();
|
|
this._disposables.push(
|
|
this._offlineButton.onDidChangeCheckedState(checked => {
|
|
if (checked) {
|
|
this.migrationStateModel._databaseBackup.migrationMode = MigrationMode.OFFLINE;
|
|
this.migrationStateModel.refreshDatabaseBackupPage = true;
|
|
}
|
|
}));
|
|
|
|
const flexContainer = this._view.modelBuilder.flexContainer()
|
|
.withItems([
|
|
this._onlineButton,
|
|
onlineDescription,
|
|
this._offlineButton,
|
|
offlineDescription]
|
|
).withLayout({ flexFlow: 'column' })
|
|
.component();
|
|
|
|
return flexContainer;
|
|
}
|
|
|
|
private createBackupLocationComponent(): azdata.FlexContainer {
|
|
const buttonGroup = 'networkContainer';
|
|
|
|
const selectLocationText = this._view.modelBuilder.text()
|
|
.withProps({
|
|
value: constants.DATABASE_BACKUP_PAGE_DESCRIPTION,
|
|
CSSStyles: { ...styles.BODY_CSS }
|
|
}).component();
|
|
|
|
this._networkShareButton = this._view.modelBuilder.radioButton()
|
|
.withProps({
|
|
name: buttonGroup,
|
|
label: constants.DATABASE_BACKUP_NC_NETWORK_SHARE_RADIO_LABEL,
|
|
checked: this.migrationStateModel.isBackupContainerNetworkShare,
|
|
CSSStyles: { ...styles.BODY_CSS, 'margin': '0' }
|
|
}).component();
|
|
|
|
this._disposables.push(
|
|
this._networkShareButton.onDidChangeCheckedState(async checked => {
|
|
if (checked) {
|
|
this.migrationStateModel._databaseBackup.networkContainerType = NetworkContainerType.NETWORK_SHARE;
|
|
this.migrationStateModel.refreshDatabaseBackupPage = true;
|
|
|
|
const hasService = this.migrationStateModel._sqlMigrationService !== undefined;
|
|
await utils.updateControlDisplay(this._dmsInfoContainer, hasService);
|
|
if (hasService) {
|
|
await this.loadStatus();
|
|
}
|
|
}
|
|
}));
|
|
|
|
this._blobContainerButton = this._view.modelBuilder.radioButton()
|
|
.withProps({
|
|
name: buttonGroup,
|
|
label: constants.DATABASE_BACKUP_NC_BLOB_STORAGE_RADIO_LABEL,
|
|
checked: this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.BLOB_CONTAINER,
|
|
CSSStyles: { ...styles.BODY_CSS, 'margin': '0' }
|
|
}).component();
|
|
|
|
this._disposables.push(
|
|
this._blobContainerButton.onDidChangeCheckedState(async checked => {
|
|
if (checked) {
|
|
this.migrationStateModel._databaseBackup.networkContainerType = NetworkContainerType.BLOB_CONTAINER;
|
|
await utils.updateControlDisplay(this._dmsInfoContainer, false);
|
|
this.migrationStateModel.refreshDatabaseBackupPage = true;
|
|
}
|
|
}));
|
|
|
|
this._sqlVmPageBlobInfoBox = this._view.modelBuilder.infoBox()
|
|
.withProps({
|
|
text: constants.DATABASE_BACKUP_SQL_VM_PAGE_BLOB_INFO,
|
|
style: 'information',
|
|
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
|
CSSStyles: { ...styles.BODY_CSS, 'display': 'none' },
|
|
links: [
|
|
{
|
|
text: constants.DATABASE_BACKUP_SQL_VM_PAGE_BLOB_URL_LABEL,
|
|
url: 'https://aka.ms/dms-migrations-troubleshooting'
|
|
}
|
|
]
|
|
}).component();
|
|
|
|
const flexContainer = this._view.modelBuilder.flexContainer()
|
|
.withItems([
|
|
selectLocationText,
|
|
this._blobContainerButton,
|
|
this._networkShareButton,
|
|
this._sqlVmPageBlobInfoBox
|
|
])
|
|
.withLayout({ flexFlow: 'column' })
|
|
.component();
|
|
|
|
return flexContainer;
|
|
}
|
|
|
|
public async onPageEnter(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
|
const isSqlDbTarget = this.migrationStateModel.isSqlDbTarget;
|
|
const isSqlVmTarget = this.migrationStateModel.isSqlVmTarget;
|
|
const isNetworkShare = this.migrationStateModel.isBackupContainerNetworkShare;
|
|
|
|
this.wizard.registerNavigationValidator((pageChangeInfo) => {
|
|
this.wizard.message = { text: '' };
|
|
if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
|
|
return true;
|
|
}
|
|
const isSqlDbTarget = this.migrationStateModel.isSqlDbTarget;
|
|
if (!isSqlDbTarget && !this._networkShareButton.checked && !this._blobContainerButton.checked) {
|
|
this.wizard.message = {
|
|
level: azdata.window.MessageLevel.Error,
|
|
text: constants.SERVICE_SELECTION_LOCATION_MESSAGE,
|
|
};
|
|
return false;
|
|
}
|
|
|
|
const state = this.migrationStateModel._sqlMigrationService?.properties?.integrationRuntimeState;
|
|
if (!this.migrationStateModel._sqlMigrationService) {
|
|
this.wizard.message = {
|
|
level: azdata.window.MessageLevel.Error,
|
|
text: constants.INVALID_SERVICE_ERROR
|
|
};
|
|
return false;
|
|
}
|
|
if ((isSqlDbTarget || isNetworkShare) && state !== 'Online') {
|
|
this.wizard.message = {
|
|
level: azdata.window.MessageLevel.Error,
|
|
text: constants.SERVICE_OFFLINE_ERROR
|
|
};
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
|
|
if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
|
|
return;
|
|
}
|
|
|
|
await utils.updateControlDisplay(this._modeContainer, !isSqlDbTarget);
|
|
this._onlineButton.enabled = !isSqlDbTarget;
|
|
|
|
if (isSqlDbTarget) {
|
|
this.migrationStateModel._databaseBackup.migrationMode = MigrationMode.OFFLINE;
|
|
this._offlineButton.checked = true;
|
|
}
|
|
this._originalMigrationMode = this.migrationStateModel._databaseBackup.migrationMode;
|
|
|
|
this._networkShareButton.checked = this.migrationStateModel.isBackupContainerNetworkShare;
|
|
this._blobContainerButton.checked = this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.BLOB_CONTAINER;
|
|
await utils.updateControlDisplay(
|
|
this._radioButtonContainer,
|
|
!isSqlDbTarget);
|
|
|
|
// if target SQL VM version is <= 2014, disable IR scenario and show info box
|
|
const shouldDisableIrScenario = isSqlVmTarget && utils.isTargetSqlVm2014OrBelow(this.migrationStateModel._targetServerInstance as SqlVMServer);
|
|
this._networkShareButton.enabled = !shouldDisableIrScenario;
|
|
await utils.updateControlDisplay(this._sqlVmPageBlobInfoBox, shouldDisableIrScenario, 'block');
|
|
|
|
// always pre-select blob scenario
|
|
this.migrationStateModel._databaseBackup.networkContainerType = NetworkContainerType.BLOB_CONTAINER;
|
|
this._blobContainerButton.checked = true;
|
|
|
|
await this.loadSubscriptionsDropdown();
|
|
|
|
this._location.value = this.migrationStateModel._location.displayName;
|
|
|
|
await utils.updateControlDisplay(
|
|
this._dmsInfoContainer,
|
|
isSqlDbTarget || isNetworkShare);
|
|
}
|
|
|
|
public async onPageLeave(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
|
this.wizard.registerNavigationValidator(pageChangeInfo => true);
|
|
this.wizard.message = { text: '' };
|
|
if (this._originalMigrationMode !== this.migrationStateModel._databaseBackup.migrationMode) {
|
|
this.migrationStateModel.refreshDatabaseBackupPage = true;
|
|
}
|
|
}
|
|
|
|
protected async handleStateChange(e: StateChangeEvent): Promise<void> {
|
|
}
|
|
|
|
private migrationServiceDropdownContainer(): azdata.FlexContainer {
|
|
const descriptionText = this._view.modelBuilder.text()
|
|
.withProps({
|
|
value: constants.IR_PAGE_DESCRIPTION,
|
|
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
|
CSSStyles: { ...styles.BODY_CSS, 'margin-bottom': '16px' }
|
|
}).component();
|
|
|
|
const subscriptionLabel = this._view.modelBuilder.text()
|
|
.withProps({
|
|
value: constants.SUBSCRIPTION,
|
|
CSSStyles: { ...styles.LABEL_CSS }
|
|
}).component();
|
|
|
|
this._subscriptionDropdown = this._view.modelBuilder.dropDown()
|
|
.withProps({
|
|
ariaLabel: constants.MIGRATION_SERVICE_SELECT_SERVICE_LABEL,
|
|
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
|
editable: true,
|
|
required: true,
|
|
fireOnTextChange: true,
|
|
placeholder: constants.SELECT_A_SERVICE,
|
|
CSSStyles: { 'margin': '0' }
|
|
}).component();
|
|
|
|
this._disposables.push(
|
|
this._subscriptionDropdown.onValueChanged(async (value) => {
|
|
if (value && value !== 'undefined' && value !== constants.SERVICE_NOT_FOUND) {
|
|
const selectedSubscription = this.migrationStateModel._subscriptions.find(
|
|
sub => `${sub.name} - ${sub.id}` === value);
|
|
this.migrationStateModel._sqlMigrationServiceSubscription = (selectedSubscription)
|
|
? selectedSubscription
|
|
: undefined!;
|
|
} else {
|
|
this.migrationStateModel._sqlMigrationServiceSubscription = undefined!;
|
|
}
|
|
await utils.clearDropDown(this._resourceGroupDropdown);
|
|
await this.loadResourceGroupDropdown();
|
|
}));
|
|
|
|
const locationLabel = this._view.modelBuilder.text()
|
|
.withProps({
|
|
value: constants.LOCATION,
|
|
CSSStyles: { ...styles.LABEL_CSS, 'margin-top': '1em' }
|
|
}).component();
|
|
this._location = this._view.modelBuilder.text()
|
|
.withProps({
|
|
enabled: false,
|
|
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
|
CSSStyles: { 'margin': '0' }
|
|
}).component();
|
|
|
|
const resourceGroupLabel = this._view.modelBuilder.text()
|
|
.withProps({
|
|
value: constants.RESOURCE_GROUP,
|
|
requiredIndicator: true,
|
|
CSSStyles: { ...styles.LABEL_CSS }
|
|
}).component();
|
|
this._resourceGroupDropdown = this._view.modelBuilder.dropDown()
|
|
.withProps({
|
|
ariaLabel: constants.RESOURCE_GROUP,
|
|
placeholder: constants.SELECT_RESOURCE_GROUP,
|
|
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
|
editable: true,
|
|
required: true,
|
|
fireOnTextChange: true,
|
|
CSSStyles: { 'margin-top': '-1em' }
|
|
}).component();
|
|
this._disposables.push(
|
|
this._resourceGroupDropdown.onValueChanged(
|
|
async (value) => {
|
|
if (value && value !== 'undefined' && value !== constants.RESOURCE_GROUP_NOT_FOUND) {
|
|
const selectedResourceGroup = this.migrationStateModel._resourceGroups.find(rg => rg.name === value);
|
|
this.migrationStateModel._sqlMigrationServiceResourceGroup = (selectedResourceGroup)
|
|
? selectedResourceGroup
|
|
: undefined!;
|
|
}
|
|
else {
|
|
this.migrationStateModel._sqlMigrationServiceResourceGroup = undefined!;
|
|
}
|
|
await utils.clearDropDown(this._dmsDropdown);
|
|
this.loadDmsDropdown();
|
|
}));
|
|
|
|
const migrationServiceDropdownLabel = this._view.modelBuilder.text()
|
|
.withProps({
|
|
value: constants.IR_PAGE_TITLE,
|
|
requiredIndicator: true,
|
|
CSSStyles: { ...styles.LABEL_CSS }
|
|
}).component();
|
|
this._dmsDropdown = this._view.modelBuilder.dropDown()
|
|
.withProps({
|
|
ariaLabel: constants.IR_PAGE_TITLE,
|
|
placeholder: constants.SELECT_RESOURCE_GROUP_PROMPT,
|
|
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
|
editable: true,
|
|
required: true,
|
|
fireOnTextChange: true,
|
|
CSSStyles: { 'margin-top': '-1em' }
|
|
}).component();
|
|
this._disposables.push(
|
|
this._dmsDropdown.onValueChanged(
|
|
async (value) => {
|
|
if (value && value !== 'undefined' && value !== constants.SQL_MIGRATION_SERVICE_NOT_FOUND_ERROR) {
|
|
this.wizard.message = { text: '' };
|
|
const resourceGroupName = this.migrationStateModel._sqlMigrationServiceResourceGroup.name.toLowerCase();
|
|
const selectedDms = this.migrationStateModel._sqlMigrationServices.find(
|
|
dms => dms.name === value
|
|
&& dms.properties.resourceGroup.toLowerCase() === resourceGroupName);
|
|
|
|
const showShirStatus = selectedDms !== undefined &&
|
|
(this.migrationStateModel.isSqlDbTarget ||
|
|
this.migrationStateModel.isBackupContainerNetworkShare);
|
|
|
|
this.migrationStateModel._sqlMigrationService = selectedDms;
|
|
await utils.updateControlDisplay(
|
|
this._dmsInfoContainer,
|
|
showShirStatus);
|
|
|
|
if (showShirStatus) {
|
|
await this.loadStatus();
|
|
}
|
|
} else {
|
|
this.migrationStateModel._sqlMigrationService = undefined;
|
|
await utils.updateControlDisplay(this._dmsInfoContainer, false);
|
|
}
|
|
}));
|
|
|
|
const createNewMigrationService = this._view.modelBuilder.hyperlink()
|
|
.withProps({
|
|
label: constants.CREATE_NEW,
|
|
ariaLabel: constants.CREATE_NEW_MIGRATION_SERVICE,
|
|
url: '',
|
|
CSSStyles: { ...styles.BODY_CSS }
|
|
}).component();
|
|
|
|
this._disposables.push(
|
|
createNewMigrationService.onDidClick(
|
|
async (e) => {
|
|
const dialog = new CreateSqlMigrationServiceDialog();
|
|
const createdDmsResult = await dialog.createNewDms(
|
|
this.migrationStateModel,
|
|
this._resourceGroupDropdown.value
|
|
? (<azdata.CategoryValue>this._resourceGroupDropdown.value).displayName
|
|
: '');
|
|
|
|
this.migrationStateModel._sqlMigrationServiceResourceGroup = createdDmsResult.resourceGroup;
|
|
this.migrationStateModel._sqlMigrationService = createdDmsResult.service;
|
|
|
|
await this.loadResourceGroupDropdown();
|
|
}));
|
|
|
|
return this._view.modelBuilder.flexContainer()
|
|
.withItems([
|
|
descriptionText,
|
|
subscriptionLabel,
|
|
this._subscriptionDropdown,
|
|
locationLabel,
|
|
this._location,
|
|
resourceGroupLabel,
|
|
this._resourceGroupDropdown,
|
|
migrationServiceDropdownLabel,
|
|
this._dmsDropdown,
|
|
createNewMigrationService])
|
|
.withLayout({ flexFlow: 'column' })
|
|
.component();
|
|
}
|
|
|
|
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: { ...styles.LABEL_CSS, 'width': '130px' }
|
|
}).component();
|
|
|
|
this._refreshButton = this._view.modelBuilder.button()
|
|
.withProps({
|
|
iconWidth: '18px',
|
|
iconHeight: '18px',
|
|
iconPath: IconPathHelper.refresh,
|
|
height: '18px',
|
|
width: '18px',
|
|
ariaLabel: constants.REFRESH,
|
|
}).component();
|
|
|
|
this._disposables.push(
|
|
this._refreshButton.onDidClick(
|
|
async (e) => await this.loadStatus()));
|
|
|
|
const connectionLabelContainer = this._view.modelBuilder.flexContainer()
|
|
.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: { ...styles.BODY_CSS }
|
|
}).component();
|
|
|
|
const instructions = utils.createRegistrationInstructions(this._view, false);
|
|
|
|
this._authKeyTable = utils.createAuthenticationKeyTable(this._view, '50px', '500px');
|
|
|
|
statusContainer.addItems([
|
|
this._dmsStatusInfoBox,
|
|
instructions,
|
|
this._authKeyTable]);
|
|
|
|
container.addItems([
|
|
connectionLabelContainer,
|
|
statusContainer]);
|
|
|
|
return container;
|
|
}
|
|
|
|
public async loadSubscriptionsDropdown(): Promise<void> {
|
|
try {
|
|
this._subscriptionDropdown.loading = true;
|
|
this.migrationStateModel._subscriptions = await utils.getAzureSubscriptions(
|
|
this.migrationStateModel._azureAccount,
|
|
this.migrationStateModel._azureTenant?.id);
|
|
|
|
const sub = this.migrationStateModel._sqlMigrationServiceSubscription
|
|
?? this.migrationStateModel._targetSubscription;
|
|
|
|
this._subscriptionDropdown.values = await utils.getAzureSubscriptionsDropdownValues(
|
|
this.migrationStateModel._subscriptions);
|
|
|
|
utils.selectDefaultDropdownValue(this._subscriptionDropdown, sub?.id, false);
|
|
} catch (e) {
|
|
logError(TelemetryViews.IntegrationRuntimePage, 'Error loadSubscriptionsDropdown', e);
|
|
} finally {
|
|
this._subscriptionDropdown.loading = false;
|
|
}
|
|
}
|
|
|
|
public async loadResourceGroupDropdown(): Promise<void> {
|
|
try {
|
|
this._resourceGroupDropdown.loading = true;
|
|
const account = this.migrationStateModel._azureAccount;
|
|
const subscription = this.migrationStateModel._sqlMigrationServiceSubscription;
|
|
const serviceId = this.migrationStateModel._sqlMigrationService?.id;
|
|
const resourceGroup = this.migrationStateModel._sqlMigrationServiceResourceGroup?.name ??
|
|
serviceId !== undefined
|
|
? getFullResourceGroupFromId(serviceId!)
|
|
: undefined;
|
|
|
|
const migrationServices = await utils.getAzureSqlMigrationServices(
|
|
account,
|
|
subscription);
|
|
|
|
const resourceGroups = utils.getServiceResourceGroupsByLocation(
|
|
migrationServices,
|
|
this.migrationStateModel._location);
|
|
|
|
this._resourceGroupDropdown.values = utils.getResourceDropdownValues(
|
|
resourceGroups,
|
|
constants.RESOURCE_GROUP_NOT_FOUND);
|
|
|
|
this.migrationStateModel._sqlMigrationServices = migrationServices;
|
|
this.migrationStateModel._resourceGroups = resourceGroups;
|
|
utils.selectDefaultDropdownValue(this._resourceGroupDropdown, resourceGroup, false);
|
|
} catch (e) {
|
|
logError(TelemetryViews.IntegrationRuntimePage, 'Error loadResourceGroupDropdown', e);
|
|
} finally {
|
|
this._resourceGroupDropdown.loading = false;
|
|
}
|
|
}
|
|
|
|
public loadDmsDropdown(): void {
|
|
try {
|
|
this._dmsDropdown.loading = true;
|
|
const serviceId = this.migrationStateModel._sqlMigrationService?.id;
|
|
|
|
this._dmsDropdown.values = utils.getAzureResourceDropdownValues(
|
|
this.migrationStateModel._sqlMigrationServices,
|
|
this.migrationStateModel._location,
|
|
this.migrationStateModel._sqlMigrationServiceResourceGroup?.name,
|
|
constants.SQL_MIGRATION_SERVICE_NOT_FOUND_ERROR);
|
|
|
|
utils.selectDefaultDropdownValue(
|
|
this._dmsDropdown,
|
|
serviceId,
|
|
false);
|
|
} catch (e) {
|
|
logError(TelemetryViews.IntegrationRuntimePage, 'Error loadDmsDropdown', e);
|
|
} finally {
|
|
this._dmsDropdown.loading = false;
|
|
}
|
|
}
|
|
|
|
private _lastIn = 0;
|
|
private async loadStatus(): Promise<void> {
|
|
const callSequence = ++this._lastIn;
|
|
let serviceName = '';
|
|
try {
|
|
if (callSequence === this._lastIn) {
|
|
this._statusLoadingComponent.loading = true;
|
|
}
|
|
|
|
const service = this.migrationStateModel._sqlMigrationService;
|
|
if (service) {
|
|
const account = this.migrationStateModel._azureAccount;
|
|
const subscription = this.migrationStateModel._sqlMigrationServiceSubscription;
|
|
const resourceGroup = service.properties.resourceGroup;
|
|
const location = service.location;
|
|
serviceName = service.name;
|
|
if (service?.properties?.integrationRuntimeState) {
|
|
service.properties.integrationRuntimeState = undefined;
|
|
}
|
|
|
|
const migrationService = await getSqlMigrationService(
|
|
account,
|
|
subscription,
|
|
resourceGroup,
|
|
location,
|
|
serviceName);
|
|
|
|
// exit if new call has started
|
|
if (callSequence !== this._lastIn) { return; }
|
|
|
|
const migrationServiceMonitoringStatus = await getSqlMigrationServiceMonitoringData(
|
|
account,
|
|
subscription,
|
|
resourceGroup,
|
|
location,
|
|
serviceName);
|
|
|
|
const nodeNames = migrationServiceMonitoringStatus.nodes.map(
|
|
node => node.nodeName);
|
|
|
|
// exit if new call has started
|
|
if (callSequence !== this._lastIn) { return; }
|
|
|
|
const state = migrationService.properties.integrationRuntimeState;
|
|
if (state === 'Online') {
|
|
await this._dmsStatusInfoBox.updateProperties(<azdata.InfoBoxComponentProperties>{
|
|
text: constants.SERVICE_READY(serviceName, nodeNames.join(', '), true),
|
|
style: 'success'
|
|
});
|
|
} else {
|
|
await this._dmsStatusInfoBox.updateProperties(<azdata.InfoBoxComponentProperties>{
|
|
text: constants.SERVICE_NOT_READY(serviceName, true),
|
|
style: 'error'
|
|
});
|
|
}
|
|
|
|
// exit if new call has started
|
|
if (callSequence !== this._lastIn) { return; }
|
|
|
|
await utils.refreshAuthenticationKeyTable(this._view, this._authKeyTable, account, subscription, resourceGroup, location, migrationService);
|
|
|
|
this.migrationStateModel._sqlMigrationService = migrationService;
|
|
this.migrationStateModel._sqlMigrationServiceSubscription = subscription;
|
|
this.migrationStateModel._nodeNames = nodeNames;
|
|
}
|
|
} catch (e) {
|
|
await this._dmsStatusInfoBox.updateProperties(<azdata.InfoBoxComponentProperties>{
|
|
text: constants.SERVICE_ERROR_NOT_READY(serviceName, e.message),
|
|
style: 'error'
|
|
});
|
|
|
|
logError(TelemetryViews.IntegrationRuntimePage, 'Error loadStatus', e);
|
|
} finally {
|
|
if (callSequence === this._lastIn) {
|
|
this._statusLoadingComponent.loading = false;
|
|
}
|
|
}
|
|
}
|
|
}
|