mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-31 09:35:39 -05:00
SQL-Migration: enable cross subscription service migration (#22876)
* x subscription migration support * refresh after cutover * fix service irregular status load behavior * queue service status requests, fix typo * add migationTargetServerName helper method * save context before api call
This commit is contained in:
@@ -10,7 +10,7 @@ import { MigrationMode, MigrationStateModel, NetworkContainerType, StateChangeEv
|
||||
import { CreateSqlMigrationServiceDialog } from '../dialog/createSqlMigrationService/createSqlMigrationServiceDialog';
|
||||
import * as constants from '../constants/strings';
|
||||
import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
|
||||
import { getFullResourceGroupFromId, getLocationDisplayName, getSqlMigrationService, getSqlMigrationServiceAuthKeys, getSqlMigrationServiceMonitoringData, SqlVMServer } from '../api/azure';
|
||||
import { getFullResourceGroupFromId, getSqlMigrationService, getSqlMigrationServiceAuthKeys, getSqlMigrationServiceMonitoringData, SqlVMServer } from '../api/azure';
|
||||
import { IconPathHelper } from '../constants/iconPathHelper';
|
||||
import { logError, TelemetryViews } from '../telemetry';
|
||||
import * as utils from '../api/utils';
|
||||
@@ -19,7 +19,7 @@ import * as styles from '../constants/styles';
|
||||
export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
private _view!: azdata.ModelView;
|
||||
private _statusLoadingComponent!: azdata.LoadingComponent;
|
||||
private _subscription!: azdata.TextComponent;
|
||||
private _subscriptionDropdown!: azdata.DropDownComponent;
|
||||
private _location!: azdata.TextComponent;
|
||||
private _resourceGroupDropdown!: azdata.DropDownComponent;
|
||||
private _dmsDropdown!: azdata.DropDownComponent;
|
||||
@@ -27,7 +27,6 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
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;
|
||||
@@ -157,8 +156,13 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
this._networkShareButton.onDidChangeCheckedState(async checked => {
|
||||
if (checked) {
|
||||
this.migrationStateModel._databaseBackup.networkContainerType = NetworkContainerType.NETWORK_SHARE;
|
||||
await utils.updateControlDisplay(this._dmsInfoContainer, true);
|
||||
this.migrationStateModel.refreshDatabaseBackupPage = true;
|
||||
|
||||
const hasService = this.migrationStateModel._sqlMigrationService !== undefined;
|
||||
await utils.updateControlDisplay(this._dmsInfoContainer, hasService);
|
||||
if (hasService) {
|
||||
await this.loadStatus();
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -271,15 +275,13 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
this.migrationStateModel._databaseBackup.networkContainerType = NetworkContainerType.BLOB_CONTAINER;
|
||||
this._blobContainerButton.checked = true;
|
||||
|
||||
this._subscription.value = this.migrationStateModel._targetSubscription.name;
|
||||
this._location.value = await getLocationDisplayName(
|
||||
this.migrationStateModel._targetServerInstance.location);
|
||||
await this.loadSubscriptionsDropdown();
|
||||
|
||||
this._location.value = this.migrationStateModel._location.displayName;
|
||||
|
||||
await utils.updateControlDisplay(
|
||||
this._dmsInfoContainer,
|
||||
isSqlDbTarget || isNetworkShare);
|
||||
|
||||
await this.loadResourceGroupDropdown();
|
||||
}
|
||||
|
||||
public async onPageLeave(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
||||
@@ -306,13 +308,32 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
value: constants.SUBSCRIPTION,
|
||||
CSSStyles: { ...styles.LABEL_CSS }
|
||||
}).component();
|
||||
this._subscription = this._view.modelBuilder.text()
|
||||
|
||||
this._subscriptionDropdown = this._view.modelBuilder.dropDown()
|
||||
.withProps({
|
||||
enabled: false,
|
||||
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 this.loadResourceGroupDropdown();
|
||||
}));
|
||||
|
||||
const locationLabel = this._view.modelBuilder.text()
|
||||
.withProps({
|
||||
value: constants.LOCATION,
|
||||
@@ -349,8 +370,11 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
this.migrationStateModel._sqlMigrationServiceResourceGroup = (selectedResourceGroup)
|
||||
? selectedResourceGroup
|
||||
: undefined!;
|
||||
this.populateDms();
|
||||
}
|
||||
else {
|
||||
this.migrationStateModel._sqlMigrationServiceResourceGroup = undefined!;
|
||||
}
|
||||
this.loadDmsDropdown();
|
||||
}));
|
||||
|
||||
const migrationServiceDropdownLabel = this._view.modelBuilder.text()
|
||||
@@ -379,15 +403,18 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
dms => dms.name === value
|
||||
&& dms.properties.resourceGroup.toLowerCase() === resourceGroupName);
|
||||
|
||||
if (selectedDms) {
|
||||
this.migrationStateModel._sqlMigrationService = selectedDms;
|
||||
await this.loadStatus();
|
||||
}
|
||||
const showShirStatus = selectedDms !== undefined &&
|
||||
(this.migrationStateModel.isSqlDbTarget ||
|
||||
this.migrationStateModel.isBackupContainerNetworkShare);
|
||||
|
||||
this.migrationStateModel._sqlMigrationService = selectedDms;
|
||||
await utils.updateControlDisplay(
|
||||
this._dmsInfoContainer,
|
||||
this.migrationStateModel.isSqlDbTarget ||
|
||||
this.migrationStateModel.isBackupContainerNetworkShare);
|
||||
showShirStatus);
|
||||
|
||||
if (showShirStatus) {
|
||||
await this.loadStatus();
|
||||
}
|
||||
} else {
|
||||
this.migrationStateModel._sqlMigrationService = undefined;
|
||||
await utils.updateControlDisplay(this._dmsInfoContainer, false);
|
||||
@@ -414,15 +441,15 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
|
||||
this.migrationStateModel._sqlMigrationServiceResourceGroup = createdDmsResult.resourceGroup;
|
||||
this.migrationStateModel._sqlMigrationService = createdDmsResult.service;
|
||||
|
||||
await this.loadResourceGroupDropdown();
|
||||
this.populateDms();
|
||||
}));
|
||||
|
||||
return this._view.modelBuilder.flexContainer()
|
||||
.withItems([
|
||||
descriptionText,
|
||||
subscriptionLabel,
|
||||
this._subscription,
|
||||
this._subscriptionDropdown,
|
||||
locationLabel,
|
||||
this._location,
|
||||
resourceGroupLabel,
|
||||
@@ -534,48 +561,71 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
authenticationKeysLabel,
|
||||
this._authKeyTable]);
|
||||
|
||||
this._connectionStatusLoader = this._view.modelBuilder.loadingComponent()
|
||||
.withItem(statusContainer)
|
||||
.withProps({ loading: false })
|
||||
.component();
|
||||
|
||||
container.addItems([
|
||||
connectionLabelContainer,
|
||||
this._connectionStatusLoader]);
|
||||
statusContainer]);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
public async loadSubscriptionsDropdown(): Promise<void> {
|
||||
try {
|
||||
this._subscriptionDropdown.loading = true;
|
||||
this.migrationStateModel._subscriptions = await utils.getAzureSubscriptions(
|
||||
this.migrationStateModel._azureAccount);
|
||||
|
||||
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;
|
||||
this._dmsDropdown.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;
|
||||
|
||||
this.migrationStateModel._sqlMigrationServices = await utils.getAzureSqlMigrationServices(
|
||||
this.migrationStateModel._azureAccount,
|
||||
this.migrationStateModel._targetSubscription);
|
||||
const migrationServices = await utils.getAzureSqlMigrationServices(
|
||||
account,
|
||||
subscription);
|
||||
|
||||
this.migrationStateModel._resourceGroups = utils.getServiceResourceGroupsByLocation(
|
||||
this.migrationStateModel._sqlMigrationServices,
|
||||
const resourceGroups = utils.getServiceResourceGroupsByLocation(
|
||||
migrationServices,
|
||||
this.migrationStateModel._location);
|
||||
|
||||
this._resourceGroupDropdown.values = utils.getResourceDropdownValues(
|
||||
this.migrationStateModel._resourceGroups,
|
||||
resourceGroups,
|
||||
constants.RESOURCE_GROUP_NOT_FOUND);
|
||||
|
||||
const resourceGroup = this.migrationStateModel._sqlMigrationService
|
||||
? getFullResourceGroupFromId(this.migrationStateModel._sqlMigrationService?.id)
|
||||
: undefined;
|
||||
this.migrationStateModel._sqlMigrationServices = migrationServices;
|
||||
this.migrationStateModel._resourceGroups = resourceGroups;
|
||||
utils.selectDefaultDropdownValue(this._resourceGroupDropdown, resourceGroup, false);
|
||||
} catch (e) {
|
||||
logError(TelemetryViews.IntegrationRuntimePage, 'Error loadResourceGroupDropdown', e);
|
||||
} finally {
|
||||
this._dmsDropdown.loading = false;
|
||||
this._resourceGroupDropdown.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
public populateDms(): void {
|
||||
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,
|
||||
@@ -584,53 +634,77 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
|
||||
utils.selectDefaultDropdownValue(
|
||||
this._dmsDropdown,
|
||||
this.migrationStateModel._sqlMigrationService?.id,
|
||||
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 {
|
||||
this._statusLoadingComponent.loading = true;
|
||||
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;
|
||||
}
|
||||
|
||||
if (this.migrationStateModel._sqlMigrationService) {
|
||||
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;
|
||||
account,
|
||||
subscription,
|
||||
resourceGroup,
|
||||
location,
|
||||
serviceName);
|
||||
|
||||
// exit if new call has started
|
||||
if (callSequence !== this._lastIn) { return; }
|
||||
|
||||
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(
|
||||
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 migrationServiceAuthKeys = await getSqlMigrationServiceAuthKeys(
|
||||
this.migrationStateModel._azureAccount,
|
||||
this.migrationStateModel._targetSubscription,
|
||||
this.migrationStateModel._sqlMigrationService.properties.resourceGroup,
|
||||
this.migrationStateModel._sqlMigrationService.location,
|
||||
this.migrationStateModel._sqlMigrationService!.name);
|
||||
account,
|
||||
subscription,
|
||||
resourceGroup,
|
||||
location,
|
||||
serviceName);
|
||||
|
||||
// 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(
|
||||
this.migrationStateModel._sqlMigrationService!.name,
|
||||
this.migrationStateModel._nodeNames.join(', ')),
|
||||
text: constants.SERVICE_READY(serviceName, nodeNames.join(', ')),
|
||||
style: 'success'
|
||||
});
|
||||
} else {
|
||||
await this._dmsStatusInfoBox.updateProperties(<azdata.InfoBoxComponentProperties>{
|
||||
text: constants.SERVICE_NOT_READY(
|
||||
this.migrationStateModel._sqlMigrationService!.name),
|
||||
text: constants.SERVICE_NOT_READY(serviceName),
|
||||
style: 'error'
|
||||
});
|
||||
}
|
||||
@@ -655,12 +729,26 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
}
|
||||
]];
|
||||
|
||||
// exit if new call has started
|
||||
if (callSequence !== this._lastIn) { return; }
|
||||
|
||||
await this._authKeyTable.setDataValues(data);
|
||||
|
||||
this.migrationStateModel._sqlMigrationService = migrationService;
|
||||
this.migrationStateModel._sqlMigrationServiceSubscription = subscription;
|
||||
this.migrationStateModel._nodeNames = nodeNames;
|
||||
}
|
||||
} catch (e) {
|
||||
logError(TelemetryViews.IntegrationRuntimePage, 'ErrorLoadingStatus', 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 {
|
||||
this._statusLoadingComponent.loading = false;
|
||||
if (callSequence === this._lastIn) {
|
||||
this._statusLoadingComponent.loading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user