NE: more unit tests and UI optimization

This commit is contained in:
Wei Zhou 2026-04-17 13:44:35 +02:00
parent cc3049d91d
commit d409852b45
8 changed files with 308 additions and 268 deletions

View File

@ -24,6 +24,7 @@ import java.util.List;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@ -53,4 +54,20 @@ public class NetworkTest {
Network.Provider transientProviderNew = Network.Provider.createTransientProvider("NetworkExtension");
assertTrue("List should contain the new transient provider with same name", providers.contains(transientProviderNew));
}
@Test
public void testCustomActionServiceLookup() {
Network.Service customAction = Network.Service.getService("CustomAction");
assertNotNull("CustomAction service should be available", customAction);
assertTrue("CustomAction should be part of the supported services list",
Network.Service.listAllServices().contains(customAction));
}
@Test
public void testTransientProviderIsNotGloballyRegistered() {
Network.Provider transientProvider = Network.Provider.createTransientProvider("TransientOnly");
assertNotNull(transientProvider);
assertNull("Transient provider should not be retrievable from the global registry",
Network.Provider.getProvider("TransientOnly"));
}
}

View File

@ -4984,7 +4984,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
@Override
public void expungeLbVmRefs(List<Long> vmIds, Long batchSize) {
if (CollectionUtils.isEmpty(getNetworkElementsIncludingExtensions()) || CollectionUtils.isEmpty(vmIds)) {
if (CollectionUtils.isEmpty(vmIds)) {
return;
}
for (NetworkElement element : getNetworkElementsIncludingExtensions()) {

View File

@ -40,6 +40,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
import java.io.File;
import java.security.InvalidParameterException;
@ -136,6 +137,8 @@ import com.cloud.network.dao.PhysicalNetworkDao;
import com.cloud.network.dao.PhysicalNetworkServiceProviderDao;
import com.cloud.network.dao.PhysicalNetworkVO;
import com.cloud.network.element.NetworkElement;
import com.cloud.network.vpc.Vpc;
import com.cloud.network.vpc.dao.VpcServiceMapDao;
import org.apache.cloudstack.extension.NetworkCustomActionProvider;
import com.cloud.org.Cluster;
import com.cloud.serializer.GsonHelper;
@ -207,6 +210,8 @@ public class ExtensionsManagerImplTest {
@Mock
private NetworkServiceMapDao networkServiceMapDao;
@Mock
private VpcServiceMapDao vpcServiceMapDao;
@Mock
private NetworkModel networkModel;
@Mock
@ -1830,6 +1835,231 @@ public class ExtensionsManagerImplTest {
}
}
@Test
public void runNetworkCustomAction_NoProviderFound_ReturnsFailureResponse() {
Network network = mock(Network.class);
when(network.getId()).thenReturn(11L);
ExtensionCustomActionVO actionVO = mock(ExtensionCustomActionVO.class);
when(actionVO.getId()).thenReturn(10L);
when(actionVO.getUuid()).thenReturn("action-uuid");
when(actionVO.getName()).thenReturn("dump-config");
when(extensionCustomActionDetailsDao.listDetailsKeyPairsWithVisibility(10L))
.thenReturn(new Pair<>(new HashMap<>(), new HashMap<>()));
ExtensionVO extensionVO = mock(ExtensionVO.class);
CustomActionResultResponse response = extensionsManager.runNetworkCustomAction(
network, actionVO, extensionVO, ExtensionCustomAction.ResourceType.Network, Collections.emptyMap());
assertFalse(response.getSuccess());
assertEquals("No network service provider found for this network", response.getResult().get(ApiConstants.DETAILS));
}
@Test
public void runNetworkCustomAction_ProviderElementMissing_ReturnsFailureResponse() {
Network network = mock(Network.class);
when(network.getId()).thenReturn(12L);
when(networkServiceMapDao.getProviderForServiceInNetwork(12L, Network.Service.CustomAction)).thenReturn("ExtProvider");
when(networkModel.getElementImplementingProvider("ExtProvider")).thenReturn(null);
ExtensionCustomActionVO actionVO = mock(ExtensionCustomActionVO.class);
when(actionVO.getId()).thenReturn(11L);
when(actionVO.getUuid()).thenReturn("action-uuid");
when(actionVO.getName()).thenReturn("dump-config");
when(extensionCustomActionDetailsDao.listDetailsKeyPairsWithVisibility(11L))
.thenReturn(new Pair<>(new HashMap<>(), new HashMap<>()));
ExtensionVO extensionVO = mock(ExtensionVO.class);
CustomActionResultResponse response = extensionsManager.runNetworkCustomAction(
network, actionVO, extensionVO, ExtensionCustomAction.ResourceType.Network, Collections.emptyMap());
assertFalse(response.getSuccess());
assertEquals("No network element found for provider: ExtProvider", response.getResult().get(ApiConstants.DETAILS));
}
@Test
public void runNetworkCustomAction_ProviderCannotHandle_ReturnsFailureResponse() {
Network network = mock(Network.class);
when(network.getId()).thenReturn(13L);
when(networkServiceMapDao.getProviderForServiceInNetwork(13L, Network.Service.CustomAction)).thenReturn("ExtProvider");
NetworkElement element = mock(NetworkElement.class, withSettings().extraInterfaces(NetworkCustomActionProvider.class));
NetworkCustomActionProvider provider = (NetworkCustomActionProvider) element;
when(networkModel.getElementImplementingProvider("ExtProvider")).thenReturn(element);
when(provider.canHandleCustomAction(network)).thenReturn(false);
ExtensionCustomActionVO actionVO = mock(ExtensionCustomActionVO.class);
when(actionVO.getId()).thenReturn(12L);
when(actionVO.getUuid()).thenReturn("action-uuid");
when(actionVO.getName()).thenReturn("dump-config");
when(extensionCustomActionDetailsDao.listDetailsKeyPairsWithVisibility(12L))
.thenReturn(new Pair<>(new HashMap<>(), new HashMap<>()));
ExtensionVO extensionVO = mock(ExtensionVO.class);
CustomActionResultResponse response = extensionsManager.runNetworkCustomAction(
network, actionVO, extensionVO, ExtensionCustomAction.ResourceType.Network, Collections.emptyMap());
assertFalse(response.getSuccess());
assertTrue(response.getResult().get(ApiConstants.DETAILS).contains("cannot handle custom action"));
}
@Test
public void runNetworkCustomAction_ProviderDoesNotImplementCustomAction_ReturnsFailureResponse() {
Network network = mock(Network.class);
when(network.getId()).thenReturn(131L);
when(networkServiceMapDao.getProviderForServiceInNetwork(131L, Network.Service.CustomAction)).thenReturn("ExtProvider");
NetworkElement element = mock(NetworkElement.class);
when(networkModel.getElementImplementingProvider("ExtProvider")).thenReturn(element);
ExtensionCustomActionVO actionVO = mock(ExtensionCustomActionVO.class);
when(actionVO.getId()).thenReturn(121L);
when(actionVO.getUuid()).thenReturn("action-uuid");
when(actionVO.getName()).thenReturn("dump-config");
when(extensionCustomActionDetailsDao.listDetailsKeyPairsWithVisibility(121L))
.thenReturn(new Pair<>(new HashMap<>(), new HashMap<>()));
ExtensionVO extensionVO = mock(ExtensionVO.class);
CustomActionResultResponse response = extensionsManager.runNetworkCustomAction(
network, actionVO, extensionVO, ExtensionCustomAction.ResourceType.Network, Collections.emptyMap());
assertFalse(response.getSuccess());
assertTrue(response.getResult().get(ApiConstants.DETAILS).contains("does not support custom actions"));
}
@Test
public void runNetworkCustomAction_SuccessfulExecution_ReturnsSuccessResponse() {
Network network = mock(Network.class);
when(network.getId()).thenReturn(14L);
when(networkServiceMapDao.getProviderForServiceInNetwork(14L, Network.Service.CustomAction)).thenReturn("ExtProvider");
NetworkElement element = mock(NetworkElement.class, withSettings().extraInterfaces(NetworkCustomActionProvider.class));
NetworkCustomActionProvider provider = (NetworkCustomActionProvider) element;
when(networkModel.getElementImplementingProvider("ExtProvider")).thenReturn(element);
when(provider.canHandleCustomAction(network)).thenReturn(true);
when(provider.runCustomAction(eq(network), eq("dump-config"), any())).thenReturn("dump-output");
ExtensionCustomActionVO actionVO = mock(ExtensionCustomActionVO.class);
when(actionVO.getId()).thenReturn(13L);
when(actionVO.getUuid()).thenReturn("action-uuid");
when(actionVO.getName()).thenReturn("dump-config");
when(extensionCustomActionDetailsDao.listDetailsKeyPairsWithVisibility(13L))
.thenReturn(new Pair<>(new HashMap<>(), new HashMap<>()));
ExtensionVO extensionVO = mock(ExtensionVO.class);
CustomActionResultResponse response = extensionsManager.runNetworkCustomAction(
network, actionVO, extensionVO, ExtensionCustomAction.ResourceType.Network, Collections.emptyMap());
assertTrue(response.getSuccess());
assertEquals("dump-output", response.getResult().get(ApiConstants.DETAILS));
}
@Test
public void runVpcCustomAction_ProviderNotCustomActionProvider_ReturnsFailureResponse() {
Vpc vpc = mock(Vpc.class);
when(vpc.getId()).thenReturn(21L);
when(vpcServiceMapDao.getProviderForServiceInVpc(21L, Network.Service.CustomAction)).thenReturn("VpcProvider");
NetworkElement element = mock(NetworkElement.class);
when(networkModel.getElementImplementingProvider("VpcProvider")).thenReturn(element);
ExtensionCustomActionVO actionVO = mock(ExtensionCustomActionVO.class);
when(actionVO.getId()).thenReturn(20L);
when(actionVO.getUuid()).thenReturn("action-uuid");
when(actionVO.getName()).thenReturn("dump-config");
when(extensionCustomActionDetailsDao.listDetailsKeyPairsWithVisibility(20L))
.thenReturn(new Pair<>(new HashMap<>(), new HashMap<>()));
ExtensionVO extensionVO = mock(ExtensionVO.class);
CustomActionResultResponse response = extensionsManager.runVpcCustomAction(
vpc, actionVO, extensionVO, ExtensionCustomAction.ResourceType.Vpc, Collections.emptyMap());
assertFalse(response.getSuccess());
assertTrue(response.getResult().get(ApiConstants.DETAILS).contains("does not support custom actions"));
}
@Test
public void runVpcCustomAction_NoProviderFound_ReturnsFailureResponse() {
Vpc vpc = mock(Vpc.class);
when(vpc.getId()).thenReturn(211L);
ExtensionCustomActionVO actionVO = mock(ExtensionCustomActionVO.class);
when(actionVO.getId()).thenReturn(201L);
when(actionVO.getUuid()).thenReturn("action-uuid");
when(actionVO.getName()).thenReturn("dump-config");
when(extensionCustomActionDetailsDao.listDetailsKeyPairsWithVisibility(201L))
.thenReturn(new Pair<>(new HashMap<>(), new HashMap<>()));
ExtensionVO extensionVO = mock(ExtensionVO.class);
CustomActionResultResponse response = extensionsManager.runVpcCustomAction(
vpc, actionVO, extensionVO, ExtensionCustomAction.ResourceType.Vpc, Collections.emptyMap());
assertFalse(response.getSuccess());
assertEquals("No VPC service provider found for this VPC", response.getResult().get(ApiConstants.DETAILS));
}
@Test
public void runVpcCustomAction_ProviderCannotHandleVpc_ReturnsFailureResponse() {
Vpc vpc = mock(Vpc.class);
when(vpc.getId()).thenReturn(212L);
when(vpcServiceMapDao.getProviderForServiceInVpc(212L, Network.Service.CustomAction)).thenReturn("VpcProvider");
NetworkElement element = mock(NetworkElement.class, withSettings().extraInterfaces(NetworkCustomActionProvider.class));
NetworkCustomActionProvider provider = (NetworkCustomActionProvider) element;
when(networkModel.getElementImplementingProvider("VpcProvider")).thenReturn(element);
when(provider.canHandleVpcCustomAction(vpc)).thenReturn(false);
ExtensionCustomActionVO actionVO = mock(ExtensionCustomActionVO.class);
when(actionVO.getId()).thenReturn(202L);
when(actionVO.getUuid()).thenReturn("action-uuid");
when(actionVO.getName()).thenReturn("dump-config");
when(extensionCustomActionDetailsDao.listDetailsKeyPairsWithVisibility(202L))
.thenReturn(new Pair<>(new HashMap<>(), new HashMap<>()));
ExtensionVO extensionVO = mock(ExtensionVO.class);
CustomActionResultResponse response = extensionsManager.runVpcCustomAction(
vpc, actionVO, extensionVO, ExtensionCustomAction.ResourceType.Vpc, Collections.emptyMap());
assertFalse(response.getSuccess());
assertTrue(response.getResult().get(ApiConstants.DETAILS).contains("cannot handle custom action"));
}
@Test
public void runVpcCustomAction_SuccessfulExecution_ReturnsSuccessResponse() {
Vpc vpc = mock(Vpc.class);
when(vpc.getId()).thenReturn(22L);
when(vpcServiceMapDao.getProviderForServiceInVpc(22L, Network.Service.CustomAction)).thenReturn("VpcProvider");
NetworkElement element = mock(NetworkElement.class, withSettings().extraInterfaces(NetworkCustomActionProvider.class));
NetworkCustomActionProvider provider = (NetworkCustomActionProvider) element;
when(networkModel.getElementImplementingProvider("VpcProvider")).thenReturn(element);
when(provider.canHandleVpcCustomAction(vpc)).thenReturn(true);
when(provider.runCustomAction(eq(vpc), eq("dump-config"), any())).thenReturn("vpc-dump-output");
ExtensionCustomActionVO actionVO = mock(ExtensionCustomActionVO.class);
when(actionVO.getId()).thenReturn(21L);
when(actionVO.getUuid()).thenReturn("action-uuid");
when(actionVO.getName()).thenReturn("dump-config");
when(extensionCustomActionDetailsDao.listDetailsKeyPairsWithVisibility(21L))
.thenReturn(new Pair<>(new HashMap<>(), new HashMap<>()));
ExtensionVO extensionVO = mock(ExtensionVO.class);
CustomActionResultResponse response = extensionsManager.runVpcCustomAction(
vpc, actionVO, extensionVO, ExtensionCustomAction.ResourceType.Vpc, Collections.emptyMap());
assertTrue(response.getSuccess());
assertEquals("vpc-dump-output", response.getResult().get(ApiConstants.DETAILS));
}
@Test
public void createCustomActionResponse_SetsBasicFields() {
ExtensionCustomAction action = mock(ExtensionCustomAction.class);

View File

@ -2839,4 +2839,3 @@ class TestNetworkExtensionNamespace(cloudstackTestCase):
self._teardown_extension()
except Exception:
pass

View File

@ -169,7 +169,7 @@ export default {
data () {
return {
roleTypes: [],
resourceTypeOptions: ['VirtualMachine', 'Network'],
resourceTypeOptions: ['VirtualMachine', 'Network', 'Vpc'],
loading: false
}
},
@ -204,13 +204,13 @@ export default {
updateResourceTypeByExtension (selectedExtension) {
const type = selectedExtension?.type
if (type === 'NetworkOrchestrator') {
this.resourceTypeOptions = ['Network']
this.resourceTypeOptions = ['Network', 'Vpc']
this.form.resourcetype = 'Network'
} else if (type === 'Orchestrator') {
this.resourceTypeOptions = ['VirtualMachine']
this.form.resourcetype = 'VirtualMachine'
} else {
this.resourceTypeOptions = ['VirtualMachine', 'Network']
this.resourceTypeOptions = ['VirtualMachine', 'Network', 'Vpc']
if (!this.form.resourcetype) {
this.form.resourcetype = 'VirtualMachine'
}

View File

@ -36,6 +36,7 @@
<template v-if="column.key === 'actions'">
<span style="margin-right: 5px">
<tooltip-button
v-if="'updateRegisteredExtension' in $store.getters.apis"
:tooltip="$t('label.action.update.extension.resource')"
type="default"
icon="edit-outlined"

View File

@ -60,10 +60,10 @@
<a-radio-button value="isolated">
{{ $t('label.isolated') }}
</a-radio-button>
<a-radio-button value="l2" v-if="form.provider !== 'NSX' && form.provider !== 'Netris' && !isExternalNetworkProvider">
<a-radio-button value="l2" v-if="form.provider !== 'NSX' && form.provider !== 'Netris'">
{{ $t('label.l2') }}
</a-radio-button>
<a-radio-button value="shared" v-if="form.provider !== 'NSX' && form.provider !== 'Netris' && !isExternalNetworkProvider">
<a-radio-button value="shared" v-if="form.provider !== 'NSX' && form.provider !== 'Netris'">
{{ $t('label.shared') }}
</a-radio-button>
</a-radio-group>
@ -138,7 +138,7 @@
<a-select-option key="" >{{ }}</a-select-option>
<a-select-option :value="'NSX'" :label="$t('label.nsx')"> {{ $t('label.nsx') }} </a-select-option>
<a-select-option :value="'Netris'" :label="$t('label.netris')"> {{ $t('label.netris') }} </a-select-option>
</a-select>
</a-select>
</a-form-item>
</a-col>
</a-row>
@ -208,7 +208,7 @@
</a-form-item>
<a-row :gutter="12">
<a-col :md="12" :lg="12">
<a-form-item name="promiscuousmode" ref="promiscuousmode" v-if="form.provider !== 'NSX' && form.provider !== 'Netris' && !isExternalNetworkProvider">
<a-form-item name="promiscuousmode" ref="promiscuousmode" v-if="form.provider !== 'NSX' && form.provider !== 'Netris'">
<template #label>
<tooltip-label :title="$t('label.promiscuousmode')" :tooltip="$t('message.network.offering.promiscuous.mode')"/>
</template>
@ -303,8 +303,8 @@
<CheckBoxSelectPair
:resourceKey="item.name"
:checkBoxLabel="item.description"
:forExternalNetProvider="form.provider === 'NSX' || form.provider === 'Netris' || isExternalNetworkProvider"
:defaultCheckBoxValue="form.provider === 'NSX' || form.provider === 'Netris' || isExternalNetworkProvider"
:forExternalNetProvider="form.provider === 'NSX' || form.provider === 'Netris'"
:defaultCheckBoxValue="form.provider === 'NSX' || form.provider === 'Netris'"
:selectOptions="!supportedServiceLoading ? item.provider: []"
@handle-checkselectpair-change="handleSupportedServiceChange"/>
</a-list-item>
@ -669,20 +669,8 @@ export default {
description: 'Netris',
enabled: true
},
externalNetworkProviderObj: {
name: '',
description: 'External Network',
enabled: true
},
nsxSupportedServicesMap: {},
netrisSupportedServicesMap: {},
externalNetworkSupportedServicesMap: {},
availableExtensionProviders: []
}
},
computed: {
isExternalNetworkProvider () {
return this.availableExtensionProviders.some(e => e.name === this.provider)
netrisSupportedServicesMap: {}
}
},
beforeCreate () {
@ -744,30 +732,6 @@ export default {
this.fetchServiceOfferingData()
this.fetchIpv6NetworkOfferingConfiguration()
this.fetchRoutedNetworkConfiguration()
this.fetchExtensionProviders()
},
fetchExtensionProviders () {
// Load NetworkOrchestrator extensions that are registered to at least one
// physical network (i.e. have a corresponding NetworkServiceProvider entry).
// Only these can be selected as a provider when creating a network offering.
getAPI('listExtensions', { type: 'NetworkOrchestrator', state: 'Enabled' }).then(json => {
const allExts = (json.listextensionsresponse && json.listextensionsresponse.extension) || []
if (allExts.length === 0) {
this.availableExtensionProviders = []
return
}
// Filter to those which have at least one matching NSP (nsp name == extension name)
getAPI('listNetworkServiceProviders', {}).then(nspJson => {
const nsps = (nspJson.listnetworkserviceprovidersresponse && nspJson.listnetworkserviceprovidersresponse.networkserviceprovider) || []
const nspNames = new Set(nsps.map(n => n.name))
this.availableExtensionProviders = allExts.filter(e => nspNames.has(e.name))
}).catch(() => {
// Fallback: show all enabled extensions
this.availableExtensionProviders = allExts
})
}).catch(() => {
this.availableExtensionProviders = []
})
},
isAdmin () {
return isAdmin()
@ -942,7 +906,7 @@ export default {
this.supportedServiceLoading = true
var supportedServices = this.supportedServices
var self = this
if (this.provider !== 'NSX' && this.provider !== 'Netris' && !this.isExternalNetworkProvider) {
if (this.provider !== 'NSX' && this.provider !== 'Netris') {
if (this.networkmode === 'ROUTED' && this.guestType === 'isolated') {
supportedServices = supportedServices.filter(service => {
return !['SourceNat', 'StaticNat', 'Lb', 'PortForwarding', 'Vpn'].includes(service.name)
@ -953,11 +917,13 @@ export default {
var providers = svc.provider
providers.forEach(function (provider, providerIndex) {
if (self.forVpc) { // *** vpc ***
var enabledProviders = ['VpcVirtualRouter', 'Netscaler', 'BigSwitchBcf', 'ConfigDrive']
if (self.lbType === 'internalLb') {
enabledProviders.push('InternalLbVm')
// For VPC offerings, keep router-specific invalid providers disabled,
// but allow extension/external providers to be selected.
if (provider.name === 'InternalLbVm') {
provider.enabled = self.lbType === 'internalLb' && svc.name === 'Lb'
} else {
provider.enabled = !['VirtualRouter', 'Nsx', 'Netris'].includes(provider.name)
}
provider.enabled = enabledProviders.includes(provider.name)
} else { // *** non-vpc ***
provider.enabled = !['InternalLbVm', 'VpcVirtualRouter', 'Nsx', 'Netris'].includes(provider.name)
}
@ -979,8 +945,6 @@ export default {
return Object.keys(this.nsxSupportedServicesMap).includes(svc.name)
} else if (this.provider === 'Netris') {
return Object.keys(this.netrisSupportedServicesMap).includes(svc.name)
} else if (this.isExternalNetworkProvider) {
return Object.keys(this.externalNetworkSupportedServicesMap).includes(svc.name)
}
})
supportedServices = supportedServices.map(svc => {
@ -989,8 +953,6 @@ export default {
svc.provider = [this.NSX]
} else if (this.provider === 'Netris') {
svc.provider = [this.Netris]
} else if (this.isExternalNetworkProvider) {
svc.provider = [this.externalNetworkProviderObj]
}
} else {
if (this.forVpc) {
@ -1069,46 +1031,9 @@ export default {
...(this.forVpc && { NetworkACL: this.Netris }),
...(!this.forVpc && { Firewall: this.Netris })
}
} else if (this.isExternalNetworkProvider) {
// Extension-backed provider: services come from the extension's network.services detail.
// this.provider is the extension name (= NSP name)
const extProviderObj = {
name: this.provider,
description: this.provider,
enabled: true
}
const svcMap = { Dhcp: this.VR, Dns: this.VR, UserData: this.VR }
// Infer services from the selected extension's network.services detail
const extDef = this.availableExtensionProviders.find(e => e.name === this.provider)
const services = this._getExtensionServices(extDef)
if (services.length > 0) {
services.forEach(svc => {
if (!['Dhcp', 'Dns', 'UserData'].includes(svc)) {
svcMap[svc] = extProviderObj
}
})
} else {
// Default services if no capabilities declared
svcMap.SourceNat = extProviderObj
svcMap.StaticNat = extProviderObj
svcMap.PortForwarding = extProviderObj
svcMap.Firewall = extProviderObj
svcMap.Gateway = extProviderObj
}
this.externalNetworkSupportedServicesMap = svcMap
this.externalNetworkProviderObj = extProviderObj
}
this.fetchSupportedServiceData()
},
_getExtensionServices (extDef) {
if (!extDef || !extDef.details) return []
const servicesCsv = extDef.details['network.services']
if (servicesCsv && typeof servicesCsv === 'string') {
return servicesCsv.split(',').map(x => x.trim()).filter(x => x.length > 0)
}
return []
},
handleForNetworkModeChange (networkMode) {
this.networkmode = networkMode
this.fetchSupportedServiceData()

View File

@ -337,22 +337,9 @@ export default {
enabled: true
},
nsxSupportedServicesMap: {},
externalNetworkProviderObj: {
name: '',
description: 'External Network',
enabled: true
},
externalNetworkSupportedServicesMap: {},
availableExtensionProviders: [],
conservemode: false
}
},
computed: {
isExternalNetworkProvider () {
const selectedProvider = this.form?.provider || this.provider
return this.availableExtensionProviders.some(e => e.name === selectedProvider)
}
},
beforeCreate () {
this.apiParams = this.$getApiParams('createVPCOffering')
},
@ -397,25 +384,6 @@ export default {
this.fetchSupportedServiceData()
this.fetchIpv6NetworkOfferingConfiguration()
this.fetchRoutedNetworkConfiguration()
this.fetchExtensionProviders()
},
fetchExtensionProviders () {
getAPI('listExtensions', { type: 'NetworkOrchestrator', state: 'Enabled' }).then(json => {
const allExts = (json.listextensionsresponse && json.listextensionsresponse.extension) || []
if (allExts.length === 0) {
this.availableExtensionProviders = []
return
}
getAPI('listNetworkServiceProviders', {}).then(nspJson => {
const nsps = (nspJson.listnetworkserviceprovidersresponse && nspJson.listnetworkserviceprovidersresponse.networkserviceprovider) || []
const nspNames = new Set(nsps.map(n => n.name))
this.availableExtensionProviders = allExts.filter(e => nspNames.has(e.name))
}).catch(() => {
this.availableExtensionProviders = allExts
})
}).catch(() => {
this.availableExtensionProviders = []
})
},
isAdmin () {
return isAdmin()
@ -465,18 +433,7 @@ export default {
},
fetchSupportedServiceData () {
var services = []
if (this.isExternalNetworkProvider) {
const serviceMap = this._buildExternalVpcServiceMap()
Object.keys(serviceMap).forEach(serviceName => {
services.push({
name: serviceName,
enabled: true,
provider: Array.isArray(serviceMap[serviceName])
? serviceMap[serviceName]
: [serviceMap[serviceName]]
})
})
} else if (this.provider === 'NSX') {
if (this.provider === 'NSX') {
services.push({
name: 'Dhcp',
enabled: true,
@ -563,82 +520,48 @@ export default {
provider: [{ name: 'VpcVirtualRouter' }]
})
} else {
services.push({
name: 'Dhcp',
provider: [
{ name: 'VpcVirtualRouter' },
{ name: 'ConfigDrive' }
]
})
services.push({
name: 'Dns',
provider: [
{ name: 'VpcVirtualRouter' },
{ name: 'ConfigDrive' }
]
})
services.push({
name: 'Lb',
provider: [
{ name: 'VpcVirtualRouter' },
{ name: 'InternalLbVm' }
]
})
services.push({
name: 'Gateway',
provider: [
{ name: 'VpcVirtualRouter' },
{ name: 'BigSwitchBcf' }
]
})
services.push({
name: 'StaticNat',
provider: [
{ name: 'VpcVirtualRouter' },
{ name: 'BigSwitchBcf' }
]
})
services.push({
name: 'SourceNat',
provider: [
{ name: 'VpcVirtualRouter' },
{ name: 'BigSwitchBcf' }
]
})
services.push({
name: 'NetworkACL',
provider: [
{ name: 'VpcVirtualRouter' },
{ name: 'BigSwitchBcf' }
]
})
services.push({
name: 'PortForwarding',
provider: [{ name: 'VpcVirtualRouter' }]
})
services.push({
name: 'UserData',
provider: [
{ name: 'VpcVirtualRouter' },
{ name: 'ConfigDrive' }
]
})
services.push({
name: 'Vpn',
provider: [
{ name: 'VpcVirtualRouter' },
{ name: 'BigSwitchBcf' }
]
})
services.push({
name: 'Connectivity',
provider: [
{ name: 'BigSwitchBcf' },
{ name: 'NiciraNvp' },
{ name: 'Ovs' },
{ name: 'JuniperContrailVpcRouter' }
]
this.supportedServiceLoading = true
getAPI('listSupportedNetworkServices').then(json => {
const vpcServices = ['Dhcp', 'Dns', 'Lb', 'Gateway', 'StaticNat', 'SourceNat', 'NetworkACL', 'PortForwarding', 'UserData', 'Vpn', 'Connectivity', 'CustomAction']
services = (json?.listsupportednetworkservicesresponse?.networkservice || [])
.filter(service => vpcServices.includes(service.name))
.map(service => {
const providerMap = {}
const providers = [...(service.provider || []), ...(service.name === 'Lb' ? [{ name: 'InternalLbVm' }] : [])]
.map(provider => {
const providerName = provider.name === 'VirtualRouter' ? 'VpcVirtualRouter' : provider.name
const enabled = providerName === 'InternalLbVm'
? service.name === 'Lb'
: !['VirtualRouter', 'Nsx', 'Netris'].includes(providerName)
return {
name: providerName,
description: providerName,
enabled
}
})
.filter(provider => {
if (providerMap[provider.name]) {
return false
}
providerMap[provider.name] = true
return true
})
return {
...service,
description: service.name,
provider: providers
}
})
this.supportedServices = []
if (this.networkmode === 'ROUTED') {
services = services.filter(service => !['SourceNat', 'StaticNat', 'Lb', 'PortForwarding', 'Vpn'].includes(service.name))
}
this.supportedServices = services
}).finally(() => {
this.supportedServiceLoading = false
})
return
}
this.supportedServices = []
if (this.networkmode === 'ROUTED') {
@ -667,64 +590,9 @@ export default {
if (this.provider === 'NSX') {
this.form.nsxsupportlb = true
this.handleNsxLbService(true)
} else if (this.isExternalNetworkProvider) {
this._buildExternalVpcServiceMap()
}
this.fetchSupportedServiceData()
},
_getExtensionServices (extDef) {
if (!extDef || !extDef.details) {
return []
}
const servicesCsv = extDef.details['network.services']
if (servicesCsv && typeof servicesCsv === 'string') {
return servicesCsv.split(',').map(x => x.trim()).filter(x => x.length > 0)
}
return []
},
_buildExternalVpcServiceMap () {
const selectedProvider = this.form?.provider || this.provider
const extProviderObj = {
name: selectedProvider,
description: selectedProvider,
enabled: true
}
const extWithFallbackProviders = [
{ name: selectedProvider },
{ name: 'VpcVirtualRouter' },
{ name: 'ConfigDrive' }
]
const serviceMap = {
Dhcp: extWithFallbackProviders,
Dns: extWithFallbackProviders,
UserData: extWithFallbackProviders
}
const extDef = this.availableExtensionProviders.find(e => e.name === selectedProvider)
const services = this._getExtensionServices(extDef)
const allowedVpcServices = new Set([
'Gateway', 'Lb', 'StaticNat', 'SourceNat', 'NetworkACL', 'PortForwarding', 'Vpn', 'CustomAction'
])
services.forEach(service => {
if (allowedVpcServices.has(service)) {
serviceMap[service] = [{ name: selectedProvider }]
}
})
// Fallback for older extensions that only declare partial details.
if (Object.keys(serviceMap).length <= 3) {
serviceMap.SourceNat = [{ name: selectedProvider }]
serviceMap.StaticNat = [{ name: selectedProvider }]
serviceMap.PortForwarding = [{ name: selectedProvider }]
serviceMap.NetworkACL = [{ name: selectedProvider }]
}
this.externalNetworkProviderObj = extProviderObj
this.externalNetworkSupportedServicesMap = serviceMap
return serviceMap
},
handleNsxLbService (supportLb) {
console.log(supportLb)
if (!supportLb) {