diff --git a/modules/web/src/app/settings/admin/presets/dialog/steps/settings/component.ts b/modules/web/src/app/settings/admin/presets/dialog/steps/settings/component.ts index b422233b6a..69e5b51fbe 100644 --- a/modules/web/src/app/settings/admin/presets/dialog/steps/settings/component.ts +++ b/modules/web/src/app/settings/admin/presets/dialog/steps/settings/component.ts @@ -27,6 +27,7 @@ import {distinctUntilChanged, filter, map, switchMap, takeUntil} from 'rxjs/oper enum Controls { Settings = 'settings', Datacenter = 'datacenter', + IsEditable = 'isEditable', } @Component({ @@ -72,6 +73,7 @@ export class PresetSettingsStepComponent extends BaseFormValidator implements On this.form = this._builder.group({ [Controls.Settings]: this._builder.control(''), [Controls.Datacenter]: this._builder.control(''), + [Controls.IsEditable]: this._builder.control(false), }); this._presetDialogService.providerChanges.pipe(takeUntil(this._unsubscribe)).subscribe(provider => { @@ -79,6 +81,7 @@ export class PresetSettingsStepComponent extends BaseFormValidator implements On this.form.removeControl(Controls.Settings); this.form.addControl(Controls.Settings, this._builder.control('')); this.form.get(Controls.Datacenter).setValue(AutocompleteInitialState); + this.form.get(Controls.IsEditable).setValue(false); this.form.updateValueAndValidity(); }); @@ -102,18 +105,31 @@ export class PresetSettingsStepComponent extends BaseFormValidator implements On .pipe(filter(form => !!form)) .pipe(map(form => form[AutocompleteControls.Main])) .pipe(takeUntil(this._unsubscribe)) - .subscribe(datacenter => this._update(datacenter)); + .subscribe(datacenter => this._updateDatacenter(datacenter)); + + this.form + .get(Controls.IsEditable) + .valueChanges.pipe(takeUntil(this._unsubscribe)) + .subscribe(value => this._updateIsEditable(value)); + } + + isExternal(): boolean { + return EXTERNAL_NODE_PROVIDERS.includes(this.provider); + } + + isProvider(...provider: NodeProvider[]): boolean { + return provider.includes(this.provider); } private _filterByProvider(datacenters: Datacenter[]): string[] { return datacenters.filter(dc => dc.spec.provider === this.provider).map(dc => dc.metadata.name); } - private _update(datacenter: string): void { + private _updateDatacenter(datacenter: string): void { this._presetDialogService.preset.spec[this._presetDialogService.provider].datacenter = datacenter; } - isExternal(): boolean { - return EXTERNAL_NODE_PROVIDERS.includes(this.provider); + private _updateIsEditable(value: boolean): void { + this._presetDialogService.preset.spec[this._presetDialogService.provider].isEditable = value; } } diff --git a/modules/web/src/app/settings/admin/presets/dialog/steps/settings/template.html b/modules/web/src/app/settings/admin/presets/dialog/steps/settings/template.html index 9306674f6a..38cc7e3b6d 100644 --- a/modules/web/src/app/settings/admin/presets/dialog/steps/settings/template.html +++ b/modules/web/src/app/settings/admin/presets/dialog/steps/settings/template.html @@ -90,6 +90,12 @@
Optional Restrictions
+ + Is Editable + + - Object.values(Controls).forEach(control => { - this.isPresetSelected = !!preset; - this._enable(!this.isPresetSelected, control); - }) - ); + this._presets.presetDetailedChanges.pipe(takeUntil(this._unsubscribe)).subscribe(preset => { + this.isPresetSelected = !!preset; + this._enable(!this.isPresetSelected, Controls.Domain); + this._enable(!this.isPresetSelected, Controls.Credentials); + if (!this.isPresetSelected) { + this._enable(!this.isPresetSelected, Controls.FloatingIPPool); + } else { + const providerSettings = preset?.providers.find(provider => provider.name === NodeProvider.OPENSTACK); + // check if floatingIPPool key exists and is empty + const enableFloatingIPPool = + (providerSettings.isEditable && providerSettings.openstack?.floatingIPPool === null) || + providerSettings.openstack?.floatingIPPool === ''; + this._enable(enableFloatingIPPool, Controls.FloatingIPPool); + if (enableFloatingIPPool) { + this.onCredentialsChange(null, preset.name); + } + } + }); this.form.valueChanges .pipe(filter(_ => this._clusterSpecService.provider === NodeProvider.OPENSTACK)) + .pipe(filter(_ => !this._presets.preset)) .pipe(takeUntil(this._unsubscribe)) .subscribe(_ => this._presets.enablePresets(OpenstackCloudSpec.isEmpty(this._clusterSpecService.cluster.spec.cloud.openstack)) @@ -153,9 +166,9 @@ export class OpenstackProviderBasicComponent extends BaseFormValidator implement return ''; } - onCredentialsChange(credentials: OpenstackCredentials): void { + onCredentialsChange(credentials?: OpenstackCredentials, preset?: string): void { this._clearFloatingIPPool(); - this._floatingIPPoolListObservable(credentials) + this._floatingIPPoolListObservable(credentials, preset) .pipe(take(1)) .subscribe((floatingIPPools: OpenstackFloatingIPPool[]) => { this.floatingIPPools = floatingIPPools; @@ -171,13 +184,18 @@ export class OpenstackProviderBasicComponent extends BaseFormValidator implement (!!this._clusterSpecService.cluster.spec.cloud.openstack?.applicationCredentialID && !!this._clusterSpecService.cluster.spec.cloud.openstack?.applicationCredentialSecret) || (!!this._clusterSpecService.cluster.spec.cloud.openstack?.username && - !!this._clusterSpecService.cluster.spec.cloud.openstack?.password) + !!this._clusterSpecService.cluster.spec.cloud.openstack?.password) || + this.isPresetSelected ); } - private _floatingIPPoolListObservable(credentials: OpenstackCredentials): Observable { + private _floatingIPPoolListObservable( + credentials?: OpenstackCredentials, + preset?: string + ): Observable { return this._presets .provider(NodeProvider.OPENSTACK) + .credential(preset) .domain(this._clusterSpecService.cluster.spec.cloud.openstack.domain) .applicationCredentialID(credentials?.applicationCredentialID) .applicationCredentialPassword(credentials?.applicationCredentialSecret) diff --git a/modules/web/src/app/wizard/step/provider-settings/provider/extended/openstack/application/component.ts b/modules/web/src/app/wizard/step/provider-settings/provider/extended/openstack/application/component.ts index 4e5c434987..fb368e07ff 100644 --- a/modules/web/src/app/wizard/step/provider-settings/provider/extended/openstack/application/component.ts +++ b/modules/web/src/app/wizard/step/provider-settings/provider/extended/openstack/application/component.ts @@ -104,6 +104,7 @@ export class OpenstackProviderExtendedAppCredentialsComponent private _applicationCredentialID = ''; private _applicationCredentialSecret = ''; + private _preset = ''; constructor( private readonly _builder: FormBuilder, @@ -124,12 +125,23 @@ export class OpenstackProviderExtendedAppCredentialsComponent [Controls.IPv6SubnetPool]: this._builder.control(''), }); - this._presets.presetChanges.pipe(takeUntil(this._unsubscribe)).subscribe(preset => - Object.values(Controls).forEach(control => { - this.isPresetSelected = !!preset; - this._enable(!this.isPresetSelected, control); - }) - ); + this._presets.presetDetailedChanges.pipe(takeUntil(this._unsubscribe)).subscribe(preset => { + this.isPresetSelected = !!preset; + const providerSettings = preset?.providers.find(provider => provider.name === NodeProvider.OPENSTACK); + if (providerSettings.isEditable && providerSettings.openstack) { + const openstack = providerSettings.openstack; + // enable if preset is editable and values are defined but empty + this._enable(openstack.securityGroups === null || openstack.securityGroups === '', Controls.SecurityGroup); + this._enable(openstack.network === null || openstack.network === '', Controls.Network); + this._enable(openstack.subnetID === null || openstack.subnetID === '', Controls.IPv4SubnetID); + this._enable(true, Controls.IPv6SubnetID); + this._enable(true, Controls.IPv6SubnetPool); + } else { + Object.values(Controls).forEach(control => { + this._enable(!this.isPresetSelected, control); + }); + } + }); this._credentialsTypeService.credentialsTypeChanges .pipe(takeUntil(this._unsubscribe)) @@ -163,7 +175,11 @@ export class OpenstackProviderExtendedAppCredentialsComponent ) .subscribe(null); - merge(this._clusterSpecService.clusterChanges, this._credentialsTypeService.credentialsTypeChanges) + merge( + this._clusterSpecService.clusterChanges, + this._credentialsTypeService.credentialsTypeChanges, + this._presets.presetChanges + ) .pipe(filter(_ => this._clusterSpecService.provider === NodeProvider.OPENSTACK)) .pipe(debounceTime(this._debounceTime)) .pipe( @@ -175,13 +191,26 @@ export class OpenstackProviderExtendedAppCredentialsComponent } }) ) - .pipe(filter(_ => this._hasApplicationCredentials())) + .pipe(filter(_ => this._hasApplicationCredentials() || this.isPresetSelected)) .pipe(filter(_ => this._areCredentialsChanged())) - .pipe(tap(_ => this._securityGroupListObservable().subscribe(this._loadSecurityGroups.bind(this)))) - .pipe(tap(_ => this._networkListObservable().subscribe(this._loadNetworks.bind(this)))) + .pipe( + tap( + _ => + this.form.get(Controls.SecurityGroup).enabled && + this._securityGroupListObservable().subscribe(this._loadSecurityGroups.bind(this)) + ) + ) + .pipe( + tap( + _ => + this.form.get(Controls.Network).enabled && + this._networkListObservable().subscribe(this._loadNetworks.bind(this)) + ) + ) .pipe( tap(_ => - Cluster.isDualStackNetworkSelected(this._clusterSpecService.cluster) + Cluster.isDualStackNetworkSelected(this._clusterSpecService.cluster) && + this.form.get(Controls.IPv6SubnetPool).enabled ? this._subnetPoolListObservable().subscribe(this._loadSubnetPools.bind(this)) : null ) @@ -225,7 +254,7 @@ export class OpenstackProviderExtendedAppCredentialsComponent case Controls.SecurityGroup: case Controls.Network: case Controls.IPv6SubnetPool: - return this._hasApplicationCredentials() ? '' : 'Please enter your credentials first.'; + return this._hasApplicationCredentials() || this.isPresetSelected ? '' : 'Please enter your credentials first.'; case Controls.IPv4SubnetID: case Controls.IPv6SubnetID: return this._canLoadSubnet() ? '' : 'Please enter your credentials and network first.'; @@ -302,6 +331,11 @@ export class OpenstackProviderExtendedAppCredentialsComponent credentialsChanged = true; } + if (this._preset !== this._presets.preset) { + this._preset = this._presets.preset; + credentialsChanged = true; + } + return credentialsChanged; } @@ -311,12 +345,16 @@ export class OpenstackProviderExtendedAppCredentialsComponent } private _canLoadSubnet(): boolean { - return this._hasApplicationCredentials() && !!this._clusterSpecService.cluster.spec.cloud.openstack.network; + return ( + (this._hasApplicationCredentials() || this.isPresetSelected) && + !!this._clusterSpecService.cluster.spec.cloud.openstack.network + ); } private _securityGroupListObservable(): Observable { return this._presets .provider(NodeProvider.OPENSTACK) + .credential(this._presets.preset) .applicationCredentialID(this._clusterSpecService.cluster.spec.cloud.openstack.applicationCredentialID) .applicationCredentialPassword(this._clusterSpecService.cluster.spec.cloud.openstack.applicationCredentialSecret) .datacenter(this._clusterSpecService.cluster.spec.cloud.dc) @@ -346,6 +384,7 @@ export class OpenstackProviderExtendedAppCredentialsComponent private _networkListObservable(): Observable { return this._presets .provider(NodeProvider.OPENSTACK) + .credential(this._presets.preset) .applicationCredentialID(this._clusterSpecService.cluster.spec.cloud.openstack.applicationCredentialID) .applicationCredentialPassword(this._clusterSpecService.cluster.spec.cloud.openstack.applicationCredentialSecret) .datacenter(this._clusterSpecService.cluster.spec.cloud.dc) @@ -375,6 +414,7 @@ export class OpenstackProviderExtendedAppCredentialsComponent private _subnetIDListObservable(): Observable { return this._presets .provider(NodeProvider.OPENSTACK) + .credential(this._presets.preset) .applicationCredentialID(this._clusterSpecService.cluster.spec.cloud.openstack.applicationCredentialID) .applicationCredentialPassword(this._clusterSpecService.cluster.spec.cloud.openstack.applicationCredentialSecret) .datacenter(this._clusterSpecService.cluster.spec.cloud.dc) @@ -408,6 +448,7 @@ export class OpenstackProviderExtendedAppCredentialsComponent private _subnetPoolListObservable(): Observable { return this._presets .provider(NodeProvider.OPENSTACK) + .credential(this._presets.preset) .applicationCredentialID(this._clusterSpecService.cluster.spec.cloud.openstack.applicationCredentialID) .applicationCredentialPassword(this._clusterSpecService.cluster.spec.cloud.openstack.applicationCredentialSecret) .datacenter(this._clusterSpecService.cluster.spec.cloud.dc) diff --git a/modules/web/src/app/wizard/step/provider-settings/provider/extended/openstack/default/component.ts b/modules/web/src/app/wizard/step/provider-settings/provider/extended/openstack/default/component.ts index 534ecb4954..4143164646 100644 --- a/modules/web/src/app/wizard/step/provider-settings/provider/extended/openstack/default/component.ts +++ b/modules/web/src/app/wizard/step/provider-settings/provider/extended/openstack/default/component.ts @@ -107,6 +107,7 @@ export class OpenstackProviderExtendedDefaultCredentialsComponent private _password = ''; private _project = ''; private _projectID = ''; + private _preset = ''; constructor( private readonly _builder: FormBuilder, @@ -127,12 +128,23 @@ export class OpenstackProviderExtendedDefaultCredentialsComponent [Controls.IPv6SubnetPool]: this._builder.control(''), }); - this._presets.presetChanges.pipe(takeUntil(this._unsubscribe)).subscribe(preset => - Object.values(Controls).forEach(control => { - this.isPresetSelected = !!preset; - this._enable(!this.isPresetSelected, control); - }) - ); + this._presets.presetDetailedChanges.pipe(takeUntil(this._unsubscribe)).subscribe(preset => { + this.isPresetSelected = !!preset; + const providerSettings = preset?.providers.find(provider => provider.name === NodeProvider.OPENSTACK); + if (providerSettings.isEditable && providerSettings.openstack) { + const openstack = providerSettings.openstack; + // enable if preset is editable and values are defined but empty + this._enable(openstack.securityGroups === null || openstack.securityGroups === '', Controls.SecurityGroup); + this._enable(openstack.network === null || openstack.network === '', Controls.Network); + this._enable(openstack.subnetID === null || openstack.subnetID === '', Controls.IPv4SubnetID); + this._enable(true, Controls.IPv6SubnetID); + this._enable(true, Controls.IPv6SubnetPool); + } else { + Object.values(Controls).forEach(control => { + this._enable(!this.isPresetSelected, control); + }); + } + }); this._credentialsTypeService.credentialsTypeChanges .pipe(takeUntil(this._unsubscribe)) @@ -166,7 +178,11 @@ export class OpenstackProviderExtendedDefaultCredentialsComponent ) .subscribe(null); - merge(this._clusterSpecService.clusterChanges, this._credentialsTypeService.credentialsTypeChanges) + merge( + this._clusterSpecService.clusterChanges, + this._credentialsTypeService.credentialsTypeChanges, + this._presets.presetChanges + ) .pipe(filter(_ => this._clusterSpecService.provider === NodeProvider.OPENSTACK)) .pipe(debounceTime(this._debounceTime)) .pipe( @@ -178,13 +194,26 @@ export class OpenstackProviderExtendedDefaultCredentialsComponent } }) ) - .pipe(filter(_ => this._hasRequiredCredentials())) + .pipe(filter(_ => this._hasRequiredCredentials() || this.isPresetSelected)) .pipe(filter(_ => this._areCredentialsChanged())) - .pipe(tap(_ => this._securityGroupListObservable().subscribe(this._loadSecurityGroups.bind(this)))) - .pipe(tap(_ => this._networkListObservable().subscribe(this._loadNetworks.bind(this)))) + .pipe( + tap( + _ => + this.form.get(Controls.SecurityGroup).enabled && + this._securityGroupListObservable().subscribe(this._loadSecurityGroups.bind(this)) + ) + ) + .pipe( + tap( + _ => + this.form.get(Controls.Network).enabled && + this._networkListObservable().subscribe(this._loadNetworks.bind(this)) + ) + ) .pipe( tap(_ => - Cluster.isDualStackNetworkSelected(this._clusterSpecService.cluster) + Cluster.isDualStackNetworkSelected(this._clusterSpecService.cluster) && + this.form.get(Controls.IPv6SubnetPool).enabled ? this._subnetPoolListObservable().subscribe(this._loadSubnetPools.bind(this)) : null ) @@ -228,7 +257,7 @@ export class OpenstackProviderExtendedDefaultCredentialsComponent case Controls.SecurityGroup: case Controls.Network: case Controls.IPv6SubnetPool: - return this._hasRequiredCredentials() ? '' : 'Please enter your credentials first.'; + return this._hasRequiredCredentials() || this.isPresetSelected ? '' : 'Please enter your credentials first.'; case Controls.IPv4SubnetID: case Controls.IPv6SubnetID: return this._canLoadSubnet() ? '' : 'Please enter your credentials and network first.'; @@ -301,6 +330,11 @@ export class OpenstackProviderExtendedDefaultCredentialsComponent private _areCredentialsChanged(): boolean { let credentialsChanged = false; + if (this._preset !== this._presets.preset) { + this._preset = this._presets.preset; + credentialsChanged = true; + } + if (this._clusterSpecService.cluster.spec.cloud.openstack?.domain !== this._domain) { this._domain = this._clusterSpecService.cluster.spec.cloud.openstack.domain; credentialsChanged = true; @@ -335,15 +369,20 @@ export class OpenstackProviderExtendedDefaultCredentialsComponent this._password = ''; this._project = ''; this._projectID = ''; + this._preset = ''; } private _canLoadSubnet(): boolean { - return this._hasBasicCredentials() && !!this._clusterSpecService.cluster.spec.cloud.openstack.network; + return ( + (this._hasBasicCredentials() || this.isPresetSelected) && + !!this._clusterSpecService.cluster.spec.cloud.openstack.network + ); } private _securityGroupListObservable(): Observable { return this._presets .provider(NodeProvider.OPENSTACK) + .credential(this._presets.preset) .domain(this._clusterSpecService.cluster.spec.cloud.openstack.domain) .username(this._clusterSpecService.cluster.spec.cloud.openstack.username) .password(this._clusterSpecService.cluster.spec.cloud.openstack.password) @@ -376,6 +415,7 @@ export class OpenstackProviderExtendedDefaultCredentialsComponent private _networkListObservable(): Observable { return this._presets .provider(NodeProvider.OPENSTACK) + .credential(this._presets.preset) .domain(this._clusterSpecService.cluster.spec.cloud.openstack.domain) .username(this._clusterSpecService.cluster.spec.cloud.openstack.username) .password(this._clusterSpecService.cluster.spec.cloud.openstack.password) @@ -408,6 +448,7 @@ export class OpenstackProviderExtendedDefaultCredentialsComponent private _subnetIDListObservable(): Observable { return this._presets .provider(NodeProvider.OPENSTACK) + .credential(this._presets.preset) .domain(this._clusterSpecService.cluster.spec.cloud.openstack.domain) .username(this._clusterSpecService.cluster.spec.cloud.openstack.username) .password(this._clusterSpecService.cluster.spec.cloud.openstack.password) @@ -443,6 +484,7 @@ export class OpenstackProviderExtendedDefaultCredentialsComponent private _subnetPoolListObservable(): Observable { return this._presets .provider(NodeProvider.OPENSTACK) + .credential(this._presets.preset) .domain(this._clusterSpecService.cluster.spec.cloud.openstack.domain) .username(this._clusterSpecService.cluster.spec.cloud.openstack.username) .password(this._clusterSpecService.cluster.spec.cloud.openstack.password)