From 72d0546d8b0cdbe51dac1a21eb45f0187a01adf6 Mon Sep 17 00:00:00 2001 From: Vishesh Date: Thu, 5 Sep 2024 14:05:05 +0530 Subject: [PATCH] Shared Network Firewall (Security groups) in Advanced zone without security groups (#9415) --- .../java/com/cloud/network/NetworkModel.java | 4 + .../api/command/user/vm/AddIpToVmNicCmd.java | 2 +- .../command/user/vm/RemoveIpFromVmNicCmd.java | 2 +- .../cloudstack/api/response/ZoneResponse.java | 20 +- .../discoverer/XcpServerDiscoverer.java | 4 +- ...esClusterResourceModifierActionWorker.java | 2 +- .../KubernetesClusterStartWorker.java | 7 +- .../com/cloud/network/NetworkModelImpl.java | 38 +++- .../network/as/AutoScaleManagerImpl.java | 7 +- .../java/com/cloud/vm/UserVmManagerImpl.java | 10 +- .../template/VnfTemplateManagerImpl.java | 2 +- .../cloud/network/MockNetworkModelImpl.java | 10 + .../network/as/AutoScaleManagerImplTest.java | 8 +- .../com/cloud/vpc/MockNetworkModelImpl.java | 10 + .../SecondaryStorageManagerTest.java | 6 + .../component/test_advancedsg_networks.py | 186 +++++++++++++----- .../component/test_security_groups.py | 48 +++-- ui/public/locales/en.json | 1 + ui/src/config/section/network.js | 5 +- ui/src/store/getters.js | 1 + ui/src/store/modules/user.js | 19 ++ ui/src/store/mutation-types.js | 1 + ui/src/views/compute/DeployVM.vue | 14 +- ui/src/views/compute/InstanceTab.vue | 56 +++++- .../compute/wizard/SecurityGroupSelection.vue | 2 +- .../infra/network/ServiceProvidersTab.vue | 36 +++- 26 files changed, 402 insertions(+), 99 deletions(-) diff --git a/api/src/main/java/com/cloud/network/NetworkModel.java b/api/src/main/java/com/cloud/network/NetworkModel.java index 699dcbf6c50..0d81495489c 100644 --- a/api/src/main/java/com/cloud/network/NetworkModel.java +++ b/api/src/main/java/com/cloud/network/NetworkModel.java @@ -356,4 +356,8 @@ public interface NetworkModel { void verifyIp6DnsPair(final String ip6Dns1, final String ip6Dns2); + boolean isSecurityGroupSupportedForZone(Long zoneId); + + boolean checkSecurityGroupSupportForNetwork(DataCenter zone, List networkIds, + List securityGroupsIds); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/AddIpToVmNicCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/AddIpToVmNicCmd.java index 0dc3dcdbdcc..e76a75ae398 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/AddIpToVmNicCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/AddIpToVmNicCmd.java @@ -79,7 +79,7 @@ public class AddIpToVmNicCmd extends BaseAsyncCreateCmd { private boolean isZoneSGEnabled() { Network ntwk = _entityMgr.findById(Network.class, getNetworkId()); DataCenter dc = _entityMgr.findById(DataCenter.class, ntwk.getDataCenterId()); - return dc.isSecurityGroupEnabled(); + return dc.isSecurityGroupEnabled() || _ntwkModel.isSecurityGroupSupportedForZone(dc.getId()); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/RemoveIpFromVmNicCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/RemoveIpFromVmNicCmd.java index a4cd6159dfc..2f53c3d4e4c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/RemoveIpFromVmNicCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/RemoveIpFromVmNicCmd.java @@ -127,7 +127,7 @@ public class RemoveIpFromVmNicCmd extends BaseAsyncCmd { private boolean isZoneSGEnabled() { Network ntwk = _entityMgr.findById(Network.class, getNetworkId()); DataCenter dc = _entityMgr.findById(DataCenter.class, ntwk.getDataCenterId()); - return dc.isSecurityGroupEnabled(); + return dc.isSecurityGroupEnabled() || _ntwkModel.isSecurityGroupSupportedForZone(dc.getId()); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java index 0d76fd2d3f9..ff43fb697b5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java @@ -312,10 +312,6 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso return networkType; } - public boolean isSecurityGroupsEnabled() { - return securityGroupsEnabled; - } - public String getAllocationState() { return allocationState; } @@ -332,10 +328,6 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso return capacities; } - public boolean isLocalStorageEnabled() { - return localStorageEnabled; - } - public Set getTags() { return tags; } @@ -344,6 +336,14 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso return resourceDetails; } + public boolean isSecurityGroupsEnabled() { + return securityGroupsEnabled; + } + + public boolean isLocalStorageEnabled() { + return localStorageEnabled; + } + public Boolean getAllowUserSpecifyVRMtu() { return allowUserSpecifyVRMtu; } @@ -356,6 +356,10 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso return routerPublicInterfaceMaxMtu; } + public boolean isNsxEnabled() { + return nsxEnabled; + } + @Override public void setResourceIconResponse(ResourceIconResponse resourceIconResponse) { this.resourceIconResponse = resourceIconResponse; diff --git a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/discoverer/XcpServerDiscoverer.java b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/discoverer/XcpServerDiscoverer.java index 2e98b68bc44..8a5e59e4373 100644 --- a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/discoverer/XcpServerDiscoverer.java +++ b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/discoverer/XcpServerDiscoverer.java @@ -346,7 +346,7 @@ public class XcpServerDiscoverer extends DiscovererBase implements Discoverer, L } DataCenterVO zone = _dcDao.findById(dcId); - boolean securityGroupEnabled = zone.isSecurityGroupEnabled(); + boolean securityGroupEnabled = zone.isSecurityGroupEnabled() || _networkMgr.isSecurityGroupSupportedForZone(zone.getId()); params.put("securitygroupenabled", Boolean.toString(securityGroupEnabled)); params.put("router.aggregation.command.each.timeout", _configDao.getValue(Config.RouterAggregationCommandEachTimeout.toString())); @@ -695,7 +695,7 @@ public class XcpServerDiscoverer extends DiscovererBase implements Discoverer, L HashMap params = super.buildConfigParams(host); DataCenterVO zone = _dcDao.findById(host.getDataCenterId()); if (zone != null) { - boolean securityGroupEnabled = zone.isSecurityGroupEnabled(); + boolean securityGroupEnabled = zone.isSecurityGroupEnabled() || _networkMgr.isSecurityGroupSupportedForZone(zone.getId()); params.put("securitygroupenabled", Boolean.toString(securityGroupEnabled)); } return params; diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java index 41a2925e402..92a0315efb5 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java @@ -401,7 +401,7 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu if (StringUtils.isNotBlank(kubernetesCluster.getKeyPair())) { keypairs.add(kubernetesCluster.getKeyPair()); } - if (zone.isSecurityGroupEnabled()) { + if (kubernetesCluster.getSecurityGroupId() != null && networkModel.checkSecurityGroupSupportForNetwork(zone, networkIds, List.of(kubernetesCluster.getSecurityGroupId()))) { List securityGroupIds = new ArrayList<>(); securityGroupIds.add(kubernetesCluster.getSecurityGroupId()); nodeVm = userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, clusterTemplate, networkIds, securityGroupIds, owner, diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java index b2fbbd31f6b..9e8a43791e8 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java @@ -215,7 +215,9 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif if (StringUtils.isNotBlank(kubernetesCluster.getKeyPair())) { keypairs.add(kubernetesCluster.getKeyPair()); } - if (zone.isSecurityGroupEnabled()) { + if (kubernetesCluster.getSecurityGroupId() != null && + networkModel.checkSecurityGroupSupportForNetwork(zone, networkIds, + List.of(kubernetesCluster.getSecurityGroupId()))) { List securityGroupIds = new ArrayList<>(); securityGroupIds.add(kubernetesCluster.getSecurityGroupId()); controlVm = userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, clusterTemplate, networkIds, securityGroupIds, owner, @@ -289,7 +291,8 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif if (StringUtils.isNotBlank(kubernetesCluster.getKeyPair())) { keypairs.add(kubernetesCluster.getKeyPair()); } - if (zone.isSecurityGroupEnabled()) { + if (kubernetesCluster.getSecurityGroupId() != null && + networkModel.checkSecurityGroupSupportForNetwork(zone, networkIds, List.of(kubernetesCluster.getSecurityGroupId()))) { List securityGroupIds = new ArrayList<>(); securityGroupIds.add(kubernetesCluster.getSecurityGroupId()); additionalControlVm = userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, clusterTemplate, networkIds, securityGroupIds, owner, diff --git a/server/src/main/java/com/cloud/network/NetworkModelImpl.java b/server/src/main/java/com/cloud/network/NetworkModelImpl.java index 0c6e826a589..fb6433b5f6b 100644 --- a/server/src/main/java/com/cloud/network/NetworkModelImpl.java +++ b/server/src/main/java/com/cloud/network/NetworkModelImpl.java @@ -145,6 +145,8 @@ import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.NicSecondaryIpDao; import com.cloud.vm.dao.VMInstanceDao; +import static com.cloud.network.Network.Service.SecurityGroup; + public class NetworkModelImpl extends ManagerBase implements NetworkModel, Configurable { public static final String UNABLE_TO_USE_NETWORK = "Unable to use network with id= %s, permission denied"; @Inject @@ -1262,7 +1264,7 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi physicalNetworkId = findPhysicalNetworkId(network.getDataCenterId(), null, null); } - return isServiceEnabledInNetwork(physicalNetworkId, network.getId(), Service.SecurityGroup); + return isServiceEnabledInNetwork(physicalNetworkId, network.getId(), SecurityGroup); } @Override @@ -2755,4 +2757,38 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi throw new InvalidParameterValueException("Invalid IPv6 for IPv6 DNS2"); } } + + @Override + public boolean isSecurityGroupSupportedForZone(Long zoneId) { + List networks = getPhysicalNtwksSupportingTrafficType(zoneId, TrafficType.Guest); + for (PhysicalNetwork network : networks ) { + if (_pNSPDao.isServiceProviderEnabled(network.getId(), Provider.SecurityGroupProvider.getName(), Service.SecurityGroup.getName())) { + return true; + } + } + return false; + } + + @Override + public boolean checkSecurityGroupSupportForNetwork(DataCenter zone, List networkIds, + List securityGroupsIds) { + if (zone.isSecurityGroupEnabled()) { + return true; + } + if (CollectionUtils.isNotEmpty(networkIds)) { + for (Long networkId : networkIds) { + Network network = _networksDao.findById(networkId); + if (network == null) { + throw new InvalidParameterValueException("Unable to find network by id " + networkId); + } + if (network.getGuestType() == Network.GuestType.Shared && isSecurityGroupSupportedInNetwork(network)) { + return true; + } + } + } else if (CollectionUtils.isNotEmpty(securityGroupsIds)) { + Network networkWithSecurityGroup = getNetworkWithSGWithFreeIPs(zone.getId()); + return networkWithSecurityGroup != null; + } + return false; + } } diff --git a/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java b/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java index 4776cfc6246..05dba6802cb 100644 --- a/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java +++ b/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java @@ -19,6 +19,7 @@ package com.cloud.network.as; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -37,6 +38,7 @@ import java.util.concurrent.TimeUnit; import javax.inject.Inject; +import com.cloud.network.NetworkModel; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.affinity.AffinityGroupVO; import org.apache.cloudstack.affinity.dao.AffinityGroupDao; @@ -251,6 +253,8 @@ public class AutoScaleManagerImpl extends ManagerBase implements AutoScaleManage @Inject NetworkOrchestrationService networkMgr; @Inject + NetworkModel networkModel; + @Inject private UserVmManager userVmMgr; @Inject private UserDataManager userDataMgr; @@ -1808,7 +1812,8 @@ public class AutoScaleManagerImpl extends ManagerBase implements AutoScaleManage null, null, true, null, affinityGroupIdList, customParameters, null, null, null, null, true, overrideDiskOfferingId); } else { - if (zone.isSecurityGroupEnabled()) { + if (networkModel.checkSecurityGroupSupportForNetwork(zone, networkIds, + Collections.emptyList())) { vm = userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, networkIds, null, owner, vmHostName,vmHostName, diskOfferingId, dataDiskSize, null, hypervisorType, HTTPMethod.GET, userData, userDataId, userDataDetails, sshKeyPairs, diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 0dc0e84cbd4..f6aabb4081a 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -27,6 +27,7 @@ import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -3094,7 +3095,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (zone.getNetworkType() == NetworkType.Basic) { // Get default guest network in Basic zone defaultNetwork = _networkModel.getExclusiveGuestNetwork(zone.getId()); - } else if (zone.isSecurityGroupEnabled()) { + } else if (_networkModel.checkSecurityGroupSupportForNetwork(zone, Collections.emptyList(), securityGroupIdList)) { NicVO defaultNic = _nicDao.findDefaultNicForVM(vm.getId()); if (defaultNic != null) { defaultNetwork = _networkDao.findById(defaultNic.getNetworkId()); @@ -6153,7 +6154,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled, overrideDiskOfferingId); } } else { - if (zone.isSecurityGroupEnabled()) { + if (_networkModel.checkSecurityGroupSupportForNetwork(zone, networkIds, + cmd.getSecurityGroupIdList())) { vm = createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, networkIds, getSecurityGroupIdList(cmd, zone, template, owner), owner, name, displayName, diskOfferingId, size, group, cmd.getHypervisor(), cmd.getHttpMethod(), userData, userDataId, userDataDetails, sshKeyPairNames, cmd.getIpToNetworkMap(), addrs, displayVm, keyboard, cmd.getAffinityGroupIdList(), cmd.getDetails(), cmd.getCustomId(), cmd.getDhcpOptionsMap(), @@ -7573,7 +7575,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir Set applicableNetworks = new LinkedHashSet<>(); Map requestedIPv4ForNics = new HashMap<>(); Map requestedIPv6ForNics = new HashMap<>(); - if (zone.isSecurityGroupEnabled()) { // advanced zone with security groups + if (_networkModel.checkSecurityGroupSupportForNetwork(zone, networkIdList, securityGroupIdList)) { // advanced zone with security groups // cleanup the old security groups _securityGroupMgr.removeInstanceFromGroups(cmd.getVmId()); // if networkIdList is null and the first network of vm is shared network, then keep it if possible @@ -8794,7 +8796,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir private Network getNetworkForOvfNetworkMapping(DataCenter zone, Account owner) throws InsufficientCapacityException, ResourceAllocationException { Network network = null; - if (zone.isSecurityGroupEnabled()) { + if (zone.isSecurityGroupEnabled() || _networkModel.isSecurityGroupSupportedForZone(zone.getId())) { network = _networkModel.getNetworkWithSGWithFreeIPs(zone.getId()); if (network == null) { throw new InvalidParameterValueException("No network with security enabled is found in zone ID: " + zone.getUuid()); diff --git a/server/src/main/java/org/apache/cloudstack/storage/template/VnfTemplateManagerImpl.java b/server/src/main/java/org/apache/cloudstack/storage/template/VnfTemplateManagerImpl.java index a0078d812ae..6a34ca2d0e5 100644 --- a/server/src/main/java/org/apache/cloudstack/storage/template/VnfTemplateManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/storage/template/VnfTemplateManagerImpl.java @@ -291,7 +291,7 @@ public class VnfTemplateManagerImpl extends ManagerBase implements VnfTemplateMa @Override public SecurityGroup createSecurityGroupForVnfAppliance(DataCenter zone, VirtualMachineTemplate template, Account owner, DeployVnfApplianceCmd cmd) { - if (zone == null || !zone.isSecurityGroupEnabled()) { + if (zone == null || !(zone.isSecurityGroupEnabled() || networkModel.isSecurityGroupSupportedForZone(zone.getId()))) { return null; } if (!cmd.getVnfConfigureManagement()) { diff --git a/server/src/test/java/com/cloud/network/MockNetworkModelImpl.java b/server/src/test/java/com/cloud/network/MockNetworkModelImpl.java index 395be635aea..ec594f185c4 100644 --- a/server/src/test/java/com/cloud/network/MockNetworkModelImpl.java +++ b/server/src/test/java/com/cloud/network/MockNetworkModelImpl.java @@ -938,4 +938,14 @@ public class MockNetworkModelImpl extends ManagerBase implements NetworkModel { @Override public void verifyIp6DnsPair(String ip4Dns1, String ip4Dns2) {} + + @Override + public boolean isSecurityGroupSupportedForZone(Long zoneId) { + return false; + } + + @Override + public boolean checkSecurityGroupSupportForNetwork(DataCenter zone, List networkIds, List securityGroupsIds) { + return false; + } } diff --git a/server/src/test/java/com/cloud/network/as/AutoScaleManagerImplTest.java b/server/src/test/java/com/cloud/network/as/AutoScaleManagerImplTest.java index 60277740daa..386cf65aed3 100644 --- a/server/src/test/java/com/cloud/network/as/AutoScaleManagerImplTest.java +++ b/server/src/test/java/com/cloud/network/as/AutoScaleManagerImplTest.java @@ -42,6 +42,7 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.network.Network; +import com.cloud.network.NetworkModel; import com.cloud.network.as.dao.AutoScalePolicyConditionMapDao; import com.cloud.network.as.dao.AutoScalePolicyDao; import com.cloud.network.as.dao.AutoScaleVmGroupDao; @@ -139,6 +140,7 @@ import org.springframework.test.util.ReflectionTestUtils; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; @@ -355,6 +357,8 @@ public class AutoScaleManagerImplTest { @Mock NetworkVO networkMock; @Mock + NetworkModel networkModel; + @Mock NetworkOfferingVO networkOfferingMock; @Mock CounterVO counterMock; @@ -1311,10 +1315,10 @@ public class AutoScaleManagerImplTest { when(userVmMock.getId()).thenReturn(virtualMachineId); when(zoneMock.getNetworkType()).thenReturn(DataCenter.NetworkType.Advanced); - when(zoneMock.isSecurityGroupEnabled()).thenReturn(true); when(userVmService.createAdvancedSecurityGroupVirtualMachine(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), eq(userData), eq(userDataId), eq(userDataDetails.toString()), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), eq(true), any(), any())).thenReturn(userVmMock); + when(networkModel.checkSecurityGroupSupportForNetwork(zoneMock, List.of(networkId), Collections.emptyList())).thenReturn(true); long result = autoScaleManagerImplSpy.createNewVM(asVmGroupMock); @@ -1360,10 +1364,10 @@ public class AutoScaleManagerImplTest { when(userVmMock.getId()).thenReturn(virtualMachineId); when(zoneMock.getNetworkType()).thenReturn(DataCenter.NetworkType.Advanced); - when(zoneMock.isSecurityGroupEnabled()).thenReturn(false); when(userVmService.createAdvancedVirtualMachine(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), eq(userData), eq(userDataId), eq(userDataDetails.toString()), any(), any(), any(), eq(true), any(), any(), any(), any(), any(), any(), any(), eq(true), any(), any())).thenReturn(userVmMock); + when(networkModel.checkSecurityGroupSupportForNetwork(zoneMock, List.of(networkId), Collections.emptyList())).thenReturn(false); long result = autoScaleManagerImplSpy.createNewVM(asVmGroupMock); diff --git a/server/src/test/java/com/cloud/vpc/MockNetworkModelImpl.java b/server/src/test/java/com/cloud/vpc/MockNetworkModelImpl.java index ad332c00fa4..5802b88a6cf 100644 --- a/server/src/test/java/com/cloud/vpc/MockNetworkModelImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockNetworkModelImpl.java @@ -954,4 +954,14 @@ public class MockNetworkModelImpl extends ManagerBase implements NetworkModel { @Override public void verifyIp6DnsPair(String ip4Dns1, String ip4Dns2) {} + + @Override + public boolean isSecurityGroupSupportedForZone(Long zoneId) { + return false; + } + + @Override + public boolean checkSecurityGroupSupportForNetwork(DataCenter zone, List networkIds, List securityGroupsIds) { + return false; + } } diff --git a/services/secondary-storage/controller/src/test/java/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerTest.java b/services/secondary-storage/controller/src/test/java/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerTest.java index feae8719f27..2d48451f43e 100644 --- a/services/secondary-storage/controller/src/test/java/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerTest.java +++ b/services/secondary-storage/controller/src/test/java/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerTest.java @@ -29,6 +29,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import com.cloud.network.NetworkModel; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -56,6 +57,9 @@ public class SecondaryStorageManagerTest { @Mock NetworkDao _networkDao; + @Mock + NetworkModel _networkModel; + @InjectMocks SecondaryStorageManagerImpl _ssMgr = new SecondaryStorageManagerImpl(); @@ -76,6 +80,7 @@ public class SecondaryStorageManagerTest { DataCenterVO dc = mock(DataCenterVO.class); when(dc.getNetworkType()).thenReturn(NetworkType.Advanced); when(dc.isSecurityGroupEnabled()).thenReturn(false); + when(_networkModel.isSecurityGroupSupportedForZone(anyLong())).thenReturn(false); when(_dcDao.findById(Mockito.anyLong())).thenReturn(dc); @@ -102,6 +107,7 @@ public class SecondaryStorageManagerTest { DataCenterVO dc = Mockito.mock(DataCenterVO.class); when(dc.getNetworkType()).thenReturn(NetworkType.Advanced); when(dc.isSecurityGroupEnabled()).thenReturn(true); + when(_networkModel.isSecurityGroupSupportedForZone(anyLong())).thenReturn(true); when(_dcDao.findById(Mockito.anyLong())).thenReturn(dc); diff --git a/test/integration/component/test_advancedsg_networks.py b/test/integration/component/test_advancedsg_networks.py index 60567ae82fd..fb9e146eb07 100644 --- a/test/integration/component/test_advancedsg_networks.py +++ b/test/integration/component/test_advancedsg_networks.py @@ -21,17 +21,19 @@ from marvin.cloudstackTestCase import cloudstackTestCase import unittest from ddt import ddt, data from marvin.lib.base import (Zone, - ServiceOffering, - Account, - NetworkOffering, - Network, - VirtualMachine, - Domain, - VpcOffering, - VPC, - SecurityGroup, - Host, - ) + ServiceOffering, + Account, + NetworkOffering, + Network, + VirtualMachine, + Domain, + VpcOffering, + VPC, + SecurityGroup, + Host, + NetworkServiceProvider, + PhysicalNetwork, + TrafficType) from marvin.lib.common import (get_domain, get_zone, @@ -158,7 +160,9 @@ class TestCreateZoneSG(cloudstackTestCase): return class TestNetworksInAdvancedSG(cloudstackTestCase): - """Test Creation of different types of networks in SG enabled advanced zone""" + """Test Creation of different types of networks in + - SG enabled advanced zone, or + - advanced zone without SG but with SG provider enabled""" @classmethod def setUpClass(cls): @@ -169,6 +173,26 @@ class TestNetworksInAdvancedSG(cloudstackTestCase): # Get Zone, Domain and templates cls.domain = get_domain(cls.api_client) cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests()) + if cls.zone.securitygroupsenabled is False: + physical_networks = PhysicalNetwork.list(cls.api_client, zoneid=cls.zone.id) + selected_physical_network = None + for net in physical_networks: + traffic_types = TrafficType.list(cls.api_client, physicalnetworkid=net.id) + for traffic_type in traffic_types: + if traffic_type.traffictype == 'Guest': + selected_physical_network = net + break + if selected_physical_network is not None: + break + if selected_physical_network is None: + raise Exception("No physical network found with guest traffic type") + + # Enable security group provider for physical network + nsps = NetworkServiceProvider.list(cls.api_client, physicalnetworkid=selected_physical_network.id, name='SecurityGroupProvider') + if len(nsps) == 0: + raise Exception("No security group provider found for physical network") + NetworkServiceProvider.update(cls.api_client, nsps[0].id, state='Enabled') + cls.template = get_template(cls.api_client, cls.zone.id, cls.services["ostype"]) @@ -294,7 +318,7 @@ class TestNetworksInAdvancedSG(cloudstackTestCase): return - @attr(tags = ["advancedsg"]) + @attr(tags = ["advancedsg", "advanced"]) def test_03_createIsolatedNetwork(self): """ Test Isolated Network """ @@ -311,7 +335,7 @@ class TestNetworksInAdvancedSG(cloudstackTestCase): self.isolated_network_offering = NetworkOffering.create(self.api_client, self.services["isolated_network_offering"], conservemode=False) - self.cleanup.append(self.isolated_network_offering) + self.cleanup_nwOfferings.append(self.isolated_network_offering) self.debug("Isolated Network offering created: %s" % self.isolated_network_offering.id) @@ -374,7 +398,7 @@ class TestNetworksInAdvancedSG(cloudstackTestCase): Exception: %s" % e) return - @attr(tags = ["advancedsg"]) + @attr(tags = ["advancedsg", "advanced"]) def test_05_deployVM_SharedwithSG(self): """ Test VM deployment in shared networks with SecurityGroup Provider """ @@ -453,7 +477,7 @@ class TestNetworksInAdvancedSG(cloudstackTestCase): self.debug("Virtual Machine created: %s" % virtual_machine.id) return - @attr(tags = ["advancedsg"]) + @attr(tags = ["advancedsg", "advanced"]) def test_06_SharedNwSGAccountSpecific(self): """ Test Account specific shared network creation with SG""" @@ -535,7 +559,7 @@ class TestNetworksInAdvancedSG(cloudstackTestCase): return - @attr(tags = ["advancedsg"]) + @attr(tags = ["advancedsg", "advanced"]) def test_07_SharedNwSG_DomainWide_SubdomainAcccess(self): """ Test Domain wide shared network with SG, with subdomain access set True""" @@ -556,7 +580,7 @@ class TestNetworksInAdvancedSG(cloudstackTestCase): self.debug("Created domain %s" % self.parent_domain.id) self.debug("Creating child domain under this domain") self.child_domain = Domain.create(self.api_client,services=self.services["domain"], - parentdomainid=self.parent_domain) + parentdomainid=self.parent_domain.id) self.debug("Created child domain: %s" % self.child_domain.id) @@ -753,7 +777,7 @@ class TestNetworksInAdvancedSG(cloudstackTestCase): return - @attr(tags = ["advancedsg"]) + @attr(tags = ["advancedsg", "advanced"]) def test_10_deleteSharedNwSGAccountSpecific_InUse(self): """ Test delete Account specific shared network creation with SG which is in use""" @@ -806,7 +830,7 @@ class TestNetworksInAdvancedSG(cloudstackTestCase): return - @attr(tags = ["advancedsg"]) + @attr(tags = ["advancedsg", "advanced"]) def test_11_deleteSharedNwSG_DomainWide_InUse(self): """ Test delete Domain wide shared network with SG which is in use""" @@ -861,7 +885,7 @@ class TestNetworksInAdvancedSG(cloudstackTestCase): return - @attr(tags = ["advancedsg"]) + @attr(tags = ["advancedsg", "advanced"]) def test_29_deleteSharedNwSG_ZoneWide_InUse(self): """ Test delete Zone wide shared network with SG which is in use""" @@ -908,7 +932,7 @@ class TestNetworksInAdvancedSG(cloudstackTestCase): self.cleanup_vms.append(vm) return - @attr(tags = ["advancedsg"]) + @attr(tags = ["advancedsg", "advanced"]) def test_12_deleteSharedNwSGAccountSpecific_NotInUse(self): """ Test delete Account specific shared network creation with SG which is not in use""" @@ -950,7 +974,7 @@ class TestNetworksInAdvancedSG(cloudstackTestCase): return - @attr(tags = ["advancedsg"]) + @attr(tags = ["advancedsg", "advanced"]) def test_13_deleteSharedNwSG_DomainWide_NotInUse(self): """ Test delete Domain wide shared network with SG which is not in use""" @@ -994,7 +1018,7 @@ class TestNetworksInAdvancedSG(cloudstackTestCase): return - @attr(tags = ["advancedsg"]) + @attr(tags = ["advancedsg", "advanced"]) def test_30_deleteSharedNwSG_ZoneWide_NotInUse(self): """ Test delete zone wide shared network with SG which is not in use""" @@ -1031,7 +1055,7 @@ class TestNetworksInAdvancedSG(cloudstackTestCase): return - @attr(tags = ["advancedsg"]) + @attr(tags = ["advancedsg", "advanced"]) def test__14_createSharedNwWithSG_withoutParams(self): """ Test create shared network with SG without specifying necessary parameters""" @@ -1076,7 +1100,7 @@ class TestNetworksInAdvancedSG(cloudstackTestCase): return - @attr(tags = ["advancedsg"]) + @attr(tags = ["advancedsg", "advanced"]) def test__15_createVPC(self): """ Test create VPC in advanced SG enabled zone""" @@ -1116,6 +1140,26 @@ class TestNetworksInAdvancedSG_VmOperations(cloudstackTestCase): # Get Zone, Domain and templates cls.domain = get_domain(cls.api_client) cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests()) + if cls.zone.securitygroupsenabled is False: + physical_networks = PhysicalNetwork.list(cls.api_client, zoneid=cls.zone.id) + selected_physical_network = None + for net in physical_networks: + traffic_types = TrafficType.list(cls.api_client, physicalnetworkid=net.id) + for traffic_type in traffic_types: + if traffic_type.traffictype == 'Guest': + selected_physical_network = net + break + if selected_physical_network is not None: + break + if selected_physical_network is None: + raise Exception("No physical network found with guest traffic type") + + # Enable security group provider for physical network + nsps = NetworkServiceProvider.list(cls.api_client, physicalnetworkid=selected_physical_network.id, name='SecurityGroupProvider') + if len(nsps) == 0: + raise Exception("No security group provider found for physical network") + NetworkServiceProvider.update(cls.api_client, nsps[0].id, state='Enabled') + cls.template = get_template(cls.api_client,cls.zone.id,cls.services["ostype"]) cls.services["virtual_machine"]["zoneid"] = cls.zone.id @@ -1314,7 +1358,7 @@ class TestNetworksInAdvancedSG_VmOperations(cloudstackTestCase): status = os.system("%s -i %s" %(file2, config_file)) return status - @attr(tags = ["advancedsg"]) + @attr(tags = ["advancedsg", "advanced"]) def test__16_AccountSpecificNwAccess(self): """ Test account specific network access of users""" @@ -1409,7 +1453,7 @@ class TestNetworksInAdvancedSG_VmOperations(cloudstackTestCase): return - @attr(tags = ["advancedsg"]) + @attr(tags = ["advancedsg", "advanced"]) def test__17_DomainSpecificNwAccess(self): """ Test domain specific network access of users""" @@ -1520,7 +1564,7 @@ class TestNetworksInAdvancedSG_VmOperations(cloudstackTestCase): return @data("account", "domain") - @attr(tags = ["advancedsg"]) + @attr(tags = ["advancedsg", "advanced"]) def test_18_DeployVM_NoFreeIPs(self, value): """ Test deploy VM in account/domain specific SG enabled shared network when no free IPs are available""" @@ -1624,7 +1668,7 @@ class TestNetworksInAdvancedSG_VmOperations(cloudstackTestCase): return @data("default", "custom") - @attr(tags = ["advancedsg"]) + @attr(tags = ["advancedsg", "advanced"]) def test_20_DeployVM_SecGrp_sharedNetwork(self, value): """ Test deploy VM in default/custom security group and shared network""" @@ -1680,7 +1724,7 @@ class TestNetworksInAdvancedSG_VmOperations(cloudstackTestCase): return - @attr(tags = ["advancedsg"]) + @attr(tags = ["advancedsg", "advanced"]) def test_24_DeployVM_Multiple_Shared_Networks(self): """ Test deploy VM in multiple zone wide shared networks""" @@ -1728,7 +1772,7 @@ class TestNetworksInAdvancedSG_VmOperations(cloudstackTestCase): return - @attr(tags = ["advancedsg"]) + @attr(tags = ["advancedsg", "advanced"]) def test_25_Deploy_Multiple_VM_Different_Shared_Networks_Same_SG(self): """ Test deploy Multiple VMs in different shared networks but same security group""" @@ -1796,7 +1840,7 @@ class TestNetworksInAdvancedSG_VmOperations(cloudstackTestCase): return - @attr(tags = ["advancedsg"]) + @attr(tags = ["advancedsg", "advanced"]) def test_26_Destroy_Deploy_VM_NoFreeIPs(self): """ Test destroy VM in zone wide shared nw when IPs are full and then try to deploy vm""" @@ -1903,7 +1947,7 @@ class TestNetworksInAdvancedSG_VmOperations(cloudstackTestCase): # Should be able to SSH VM try: self.debug("SSH into VM: %s" % vm.nic[0].ipaddress) - vm.get_ssh_client(ipaddress=vm.nic[0].ipaddress) + vm.get_ssh_client(ipaddress=vm.nic[0].ipaddress, retries=5) self.debug("SSH to VM successful, proceeding for %s operation" % value) @@ -1914,7 +1958,7 @@ class TestNetworksInAdvancedSG_VmOperations(cloudstackTestCase): vm.reboot(self.api_client) self.debug("SSH into VM: %s" % vm.nic[0].ipaddress) - vm.get_ssh_client(ipaddress=vm.nic[0].ipaddress) + vm.get_ssh_client(ipaddress=vm.nic[0].ipaddress, retries=5) except Exception as e: self.fail("SSH Access failed for %s: %s, failed at line %s" % \ @@ -1972,7 +2016,7 @@ class TestNetworksInAdvancedSG_VmOperations(cloudstackTestCase): # Should be able to SSH VM try: self.debug("SSH into VM: %s" % vm.nic[0].ipaddress) - vm.get_ssh_client(ipaddress=vm.nic[0].ipaddress) + vm.get_ssh_client(ipaddress=vm.nic[0].ipaddress, retries=5) self.debug("SSH to VM successful, proceeding for %s operation" % value) vm.delete(self.api_client, expunge=False) if value == "recover": @@ -1989,7 +2033,7 @@ class TestNetworksInAdvancedSG_VmOperations(cloudstackTestCase): retriesCount -= 1 time.sleep(10) self.debug("SSH into VM: %s" % vm.nic[0].ipaddress) - vm.get_ssh_client(ipaddress=vm.nic[0].ipaddress) + vm.get_ssh_client(ipaddress=vm.nic[0].ipaddress, retries=5) self.debug("SSH successful") self.cleanup_vms.append(vm) elif value == "expunge": @@ -2003,7 +2047,7 @@ class TestNetworksInAdvancedSG_VmOperations(cloudstackTestCase): try: self.debug("SSH into VM: %s, this should fail" % vm.nic[0].ipaddress) - vm.get_ssh_client(ipaddress=vm.nic[0].ipaddress) + vm.get_ssh_client(ipaddress=vm.nic[0].ipaddress, retries=5) self.fail("SSH should have failed, instead it succeeded") except Exception as e: self.debug("SSH failed as expected with exception: %s" % e) @@ -2019,7 +2063,7 @@ class TestNetworksInAdvancedSG_VmOperations(cloudstackTestCase): return - @attr(tags = ["advancedsg"]) + @attr(tags = ["advancedsg", "advanced"]) def test_31_Deploy_VM_multiple_shared_networks_sg(self): """ Test deploy VM in multiple SG enabled shared networks""" @@ -2069,7 +2113,7 @@ class TestNetworksInAdvancedSG_VmOperations(cloudstackTestCase): @unittest.skip("Testing pending on multihost setup") @data("account","domain","zone") - @attr(tags = ["advancedsg"]) + @attr(tags = ["advancedsg", "advanced"]) def test_33_VM_Migrate_SharedNwSG(self, value): """ Test migration of VM deployed in Account specific shared network""" @@ -2280,6 +2324,26 @@ class TestSecurityGroups_BasicSanity(cloudstackTestCase): # Get Zone, Domain and templates cls.domain = get_domain(cls.api_client) cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests()) + if cls.zone.securitygroupsenabled is False: + physical_networks = PhysicalNetwork.list(cls.api_client, zoneid=cls.zone.id) + selected_physical_network = None + for net in physical_networks: + traffic_types = TrafficType.list(cls.api_client, physicalnetworkid=net.id) + for traffic_type in traffic_types: + if traffic_type.traffictype == 'Guest': + selected_physical_network = net + break + if selected_physical_network is not None: + break + if selected_physical_network is None: + raise Exception("No physical network found with guest traffic type") + + # Enable security group provider for physical network + nsps = NetworkServiceProvider.list(cls.api_client, physicalnetworkid=selected_physical_network.id, name='SecurityGroupProvider') + if len(nsps) == 0: + raise Exception("No security group provider found for physical network") + NetworkServiceProvider.update(cls.api_client, nsps[0].id, state='Enabled') + cls.template = get_template(cls.api_client,cls.zone.id,cls.services["ostype"]) cls.services["virtual_machine"]["zoneid"] = cls.zone.id @@ -2488,7 +2552,7 @@ class TestSecurityGroups_BasicSanity(cloudstackTestCase): # Should be able to SSH VM try: self.debug("SSH into VM: %s" % vm.nic[0].ipaddress) - ssh = vm.get_ssh_client(ipaddress=vm.nic[0].ipaddress) + ssh = vm.get_ssh_client(ipaddress=vm.nic[0].ipaddress, retries=5) # Ping to outsite world res = ssh.execute("ping -c 1 www.google.com") @@ -2580,7 +2644,7 @@ class TestSecurityGroups_BasicSanity(cloudstackTestCase): # Should be able to SSH VM try: self.debug("SSH into VM: %s" % vm.nic[0].ipaddress) - ssh = vm.get_ssh_client(ipaddress=vm.nic[0].ipaddress) + ssh = vm.get_ssh_client(ipaddress=vm.nic[0].ipaddress, retries=5) # Ping to outsite world res = ssh.execute("ping -c 1 www.google.com") @@ -2673,7 +2737,7 @@ class TestSecurityGroups_BasicSanity(cloudstackTestCase): # Should be able to SSH VM try: self.debug("SSH into VM: %s" % vm.ssh_ip) - ssh = vm.get_ssh_client(ipaddress=vm.nic[0].ipaddress) + ssh = vm.get_ssh_client(ipaddress=vm.nic[0].ipaddress, retries=5) # Ping to outsite world res = ssh.execute("ping -c 1 www.google.com") @@ -2695,7 +2759,7 @@ class TestSecurityGroups_BasicSanity(cloudstackTestCase): ) return - @attr(tags = ["advancedsg"]) + @attr(tags = ["advancedsg", "advanced"]) def test_32_delete_default_security_group(self): """ Test Delete the default security group when No VMs are deployed""" @@ -2741,6 +2805,26 @@ class TestSharedNetworkOperations(cloudstackTestCase): # Get Zone, Domain and templates cls.domain = get_domain(cls.api_client) cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests()) + if cls.zone.securitygroupsenabled is False: + physical_networks = PhysicalNetwork.list(cls.api_client, zoneid=cls.zone.id) + selected_physical_network = None + for net in physical_networks: + traffic_types = TrafficType.list(cls.api_client, physicalnetworkid=net.id) + for traffic_type in traffic_types: + if traffic_type.traffictype == 'Guest': + selected_physical_network = net + break + if selected_physical_network is not None: + break + if selected_physical_network is None: + raise Exception("No physical network found with guest traffic type") + + # Enable security group provider for physical network + nsps = NetworkServiceProvider.list(cls.api_client, physicalnetworkid=selected_physical_network.id, name='SecurityGroupProvider') + if len(nsps) == 0: + raise Exception("No security group provider found for physical network") + NetworkServiceProvider.update(cls.api_client, nsps[0].id, state='Enabled') + cls.template = get_template(cls.api_client,cls.zone.id,cls.services["ostype"]) cls.services["virtual_machine"]["zoneid"] = cls.zone.id @@ -2864,7 +2948,7 @@ class TestSharedNetworkOperations(cloudstackTestCase): return @data("account","domain","zone") - @attr(tags = ["advancedsg"]) + @attr(tags = ["advancedsg", "advanced"]) def test_34_restart_shared_network_sg(self, value): """ Test restart account/domain/zone wide shared network""" @@ -3196,7 +3280,7 @@ class TestAccountBasedIngressRules(cloudstackTestCase): self.api_client.authorizeSecurityGroupIngress(cmd) self.debug("Getting SSH client of virtual machine 1: %s" % self.virtual_machine_1.id) - sshClient = self.virtual_machine_1.get_ssh_client(ipaddress=self.virtual_machine_1.nic[0].ipaddress) + sshClient = self.virtual_machine_1.get_ssh_client(ipaddress=self.virtual_machine_1.nic[0].ipaddress, retries=5) try: if value == "accessByIp": self.debug("SSHing into vm_2 %s from vm_1 %s" % (self.virtual_machine_2.nic[0].ipaddress,self.virtual_machine_1.nic[0].ipaddress)) @@ -3255,7 +3339,7 @@ class TestAccountBasedIngressRules(cloudstackTestCase): self.api_client.authorizeSecurityGroupIngress(cmd) self.debug("Getting SSH client of virtual machine 1: %s" % self.virtual_machine_1.id) - sshClient = self.virtual_machine_1.get_ssh_client(ipaddress=self.virtual_machine_1.nic[0].ipaddress) + sshClient = self.virtual_machine_1.get_ssh_client(ipaddress=self.virtual_machine_1.nic[0].ipaddress, retries=5) try: self.debug("SSHing into vm_2 %s from vm_1 %s" % (self.virtual_machine_2.nic[0].ipaddress,self.virtual_machine_1.nic[0].ipaddress)) command = "ping -c 1 %s" % self.virtual_machine_2.nic[0].ipaddress @@ -3317,7 +3401,7 @@ class TestAccountBasedIngressRules(cloudstackTestCase): self.debug("Deployed vm: %s" % self.virtual_machine_4.id) self.debug("Getting SSH client of virtual machine 1: %s" % self.virtual_machine_3.id) - sshClient = self.virtual_machine_3.get_ssh_client(ipaddress=self.virtual_machine_3.nic[0].ipaddress) + sshClient = self.virtual_machine_3.get_ssh_client(ipaddress=self.virtual_machine_3.nic[0].ipaddress, retries=5) try: if value == "accessByIp": self.debug("SSHing into vm_4 %s from vm_3 %s" % (self.virtual_machine_4.nic[0].ipaddress,self.virtual_machine_3.nic[0].ipaddress)) @@ -3386,7 +3470,7 @@ class TestAccountBasedIngressRules(cloudstackTestCase): self.debug("Deployed vm: %s" % self.virtual_machine_3.id) self.debug("Getting SSH client of virtual machine 1: %s" % self.virtual_machine_3.id) - sshClient = self.virtual_machine_3.get_ssh_client(ipaddress=self.virtual_machine_3.nic[0].ipaddress) + sshClient = self.virtual_machine_3.get_ssh_client(ipaddress=self.virtual_machine_3.nic[0].ipaddress, retries=5) try: if value == "accessByIp": self.debug("SSHing into vm_2 %s from vm_3 %s" % (self.virtual_machine_2.nic[0].ipaddress,self.virtual_machine_3.nic[0].ipaddress)) @@ -3446,7 +3530,7 @@ class TestAccountBasedIngressRules(cloudstackTestCase): self.debug("Getting SSH client of virtual machine 1: %s" % self.virtual_machine_1.id) try: - sshClient = self.virtual_machine_1.get_ssh_client(ipaddress=self.virtual_machine_1.nic[0].ipaddress) + sshClient = self.virtual_machine_1.get_ssh_client(ipaddress=self.virtual_machine_1.nic[0].ipaddress, retries=5) self.debug("SSHing into vm_2 %s from vm_1 %s" % (self.virtual_machine_2.nic[0].ipaddress,self.virtual_machine_1.nic[0].ipaddress)) command = "ssh -t -t root@%s" % self.virtual_machine_2.nic[0].ipaddress self.debug("command: --> %s" % command) @@ -3468,7 +3552,7 @@ class TestAccountBasedIngressRules(cloudstackTestCase): self.debug("Getting SSH client of virtual machine 1: %s" % self.virtual_machine_1.id) try: - sshClient = self.virtual_machine_1.get_ssh_client(ipaddress=self.virtual_machine_1.nic[0].ipaddress) + sshClient = self.virtual_machine_1.get_ssh_client(ipaddress=self.virtual_machine_1.nic[0].ipaddress, retries=5) self.debug("SSHing into vm_2 %s from vm_1 %s" % (self.virtual_machine_2.nic[0].ipaddress,self.virtual_machine_1.nic[0].ipaddress)) command = "ssh -t -t root@%s" % self.virtual_machine_2.nic[0].ipaddress self.debug("command: --> %s" % command) diff --git a/test/integration/component/test_security_groups.py b/test/integration/component/test_security_groups.py index 1a268d692eb..449fe10cb28 100644 --- a/test/integration/component/test_security_groups.py +++ b/test/integration/component/test_security_groups.py @@ -28,7 +28,10 @@ from marvin.lib.base import (Account, SecurityGroup, Router, Host, - Network) + Network, + PhysicalNetwork, + TrafficType, + NetworkServiceProvider) from marvin.lib.common import (get_domain, get_zone, get_template, @@ -73,6 +76,26 @@ class TestDefaultSecurityGroup(cloudstackTestCase): # Get Zone, Domain and templates cls.domain = get_domain(cls.api_client) cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests()) + if cls.zone.securitygroupsenabled is False: + physical_networks = PhysicalNetwork.list(cls.api_client, zoneid=cls.zone.id) + selected_physical_network = None + for net in physical_networks: + traffic_types = TrafficType.list(cls.api_client, physicalnetworkid=net.id) + for traffic_type in traffic_types: + if traffic_type.traffictype == 'Guest': + selected_physical_network = net + break + if selected_physical_network is not None: + break + if selected_physical_network is None: + raise Exception("No physical network found with guest traffic type") + + # Enable security group provider for physical network + nsps = NetworkServiceProvider.list(cls.api_client, physicalnetworkid=selected_physical_network.id, name='SecurityGroupProvider') + if len(nsps) == 0: + raise Exception("No security group provider found for physical network") + NetworkServiceProvider.update(cls.api_client, nsps[0].id, state='Enabled') + cls.testdata['mode'] = cls.zone.networktype template = get_template( @@ -115,7 +138,7 @@ class TestDefaultSecurityGroup(cloudstackTestCase): return - @attr(tags=["sg", "basic", "eip", "advancedsg"]) + @attr(tags=["sg", "basic", "eip", "advancedsg", "advanced"]) def test_01_deployVM_InDefaultSecurityGroup(self): """Test deploy VM in default security group """ @@ -193,7 +216,7 @@ class TestDefaultSecurityGroup(cloudstackTestCase): ) return - @attr(tags=["sg", "basic", "eip", "advancedsg"]) + @attr(tags=["sg", "basic", "eip", "advancedsg", "advanced"]) def test_02_listSecurityGroups(self): """Test list security groups for admin account """ @@ -228,7 +251,7 @@ class TestDefaultSecurityGroup(cloudstackTestCase): ) return - @attr(tags=["sg", "basic", "eip", "advancedsg"]) + @attr(tags=["sg", "basic", "eip", "advancedsg", "advanced"]) def test_03_accessInDefaultSecurityGroup(self): """Test access in default security group """ @@ -314,7 +337,8 @@ class TestDefaultSecurityGroup(cloudstackTestCase): self.virtual_machine.ssh_ip, self.virtual_machine.ssh_port, self.virtual_machine.username, - self.virtual_machine.password + self.virtual_machine.password, + retries=5 ) return @@ -453,7 +477,7 @@ class TestAuthorizeIngressRule(cloudstackTestCase): # Should be able to SSH VM try: self.debug("SSH into VM: %s" % self.virtual_machine.id) - self.virtual_machine.get_ssh_client() + self.virtual_machine.get_ssh_client(retries=5) except Exception as e: self.fail("SSH Access failed for %s: %s" % (self.virtual_machine.ipaddress, e) @@ -604,7 +628,7 @@ class TestRevokeIngressRule(cloudstackTestCase): # Should be able to SSH VM try: self.debug("SSH into VM: %s" % self.virtual_machine.id) - self.virtual_machine.get_ssh_client() + self.virtual_machine.get_ssh_client(retries=5) except Exception as e: self.fail("SSH Access failed for %s: %s" % (self.virtual_machine.ipaddress, e) @@ -937,7 +961,7 @@ class TestdeployVMWithUserData(cloudstackTestCase): % self.virtual_machine.ssh_ip ) - ssh = self.virtual_machine.get_ssh_client() + ssh = self.virtual_machine.get_ssh_client(retries=5) except Exception as e: self.fail("SSH Access failed for %s: %s" % (self.virtual_machine.ipaddress, e) @@ -1348,7 +1372,7 @@ class TestIngressRule(cloudstackTestCase): self.virtual_machine.ssh_ip, self.testdata["ingress_rule"]["endport"] )) - self.virtual_machine.get_ssh_client() + self.virtual_machine.get_ssh_client(retries=5) except Exception as e: self.fail("SSH access failed for ingress rule ID: %s, %s" @@ -1476,7 +1500,7 @@ class TestIngressRule(cloudstackTestCase): self.virtual_machine.ssh_ip, self.testdata["ingress_rule"]["endport"] )) - self.virtual_machine.get_ssh_client() + self.virtual_machine.get_ssh_client(retries=5) except Exception as e: self.fail("SSH access failed for ingress rule ID: %s, %s" @@ -1623,7 +1647,7 @@ class TestIngressRule(cloudstackTestCase): self.debug( "Trying to SSH into VM %s" % self.virtual_machine.ssh_ip) - self.virtual_machine.get_ssh_client() + self.virtual_machine.get_ssh_client(retries=5) except Exception as e: self.fail("SSH access failed for ingress rule ID: %s" % ingress_rule["id"] @@ -1642,7 +1666,7 @@ class TestIngressRule(cloudstackTestCase): self.debug( "Trying to SSH into VM %s" % self.virtual_machine.ssh_ip) - self.virtual_machine.get_ssh_client() + self.virtual_machine.get_ssh_client(retries=5) except Exception as e: self.fail("SSH access failed for ingress rule ID: %s" % ingress_rule["id"] diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 7ebf9e25734..5098f6794f5 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -209,6 +209,7 @@ "label.action.unmanage.volume": "Unmanage Volume", "label.action.unmanage.volumes": "Unmanage Volumes", "label.action.update.host": "Update host", +"label.action.update.security.groups": "Update security groups", "label.action.update.offering.access": "Update offering access", "label.action.update.resource.count": "Update resource count", "label.action.value": "Action/Value", diff --git a/ui/src/config/section/network.js b/ui/src/config/section/network.js index 1ff704a306f..68d02dff313 100644 --- a/ui/src/config/section/network.js +++ b/ui/src/config/section/network.js @@ -307,10 +307,7 @@ export default { return false } const listZoneHaveSGEnabled = store.getters.zones.filter(zone => zone.securitygroupsenabled === true) - if (!listZoneHaveSGEnabled || listZoneHaveSGEnabled.length === 0) { - return false - } - return true + return (listZoneHaveSGEnabled && listZoneHaveSGEnabled.length > 0) || store.getters.showSecurityGroups }, actions: [ { diff --git a/ui/src/store/getters.js b/ui/src/store/getters.js index 405fd11bad1..645d031fc3e 100644 --- a/ui/src/store/getters.js +++ b/ui/src/store/getters.js @@ -36,6 +36,7 @@ const getters = { isLdapEnabled: state => state.user.isLdapEnabled, cloudian: state => state.user.cloudian, zones: state => state.user.zones, + showSecurityGroups: state => state.user.showSecurityGroups, timezoneoffset: state => state.user.timezoneoffset, usebrowsertimezone: state => state.user.usebrowsertimezone, server: state => state.app.server, diff --git a/ui/src/store/modules/user.js b/ui/src/store/modules/user.js index a5b48acf99d..22a2fab959d 100644 --- a/ui/src/store/modules/user.js +++ b/ui/src/store/modules/user.js @@ -34,6 +34,7 @@ import { DEFAULT_THEME, APIS, ZONES, + SHOW_SECURTIY_GROUPS, TIMEZONE_OFFSET, USE_BROWSER_TIMEZONE, HEADER_NOTICES, @@ -124,6 +125,10 @@ const user = { state.zones = zones vueProps.$localStorage.set(ZONES, zones) }, + SET_SHOW_SECURITY_GROUPS: (state, show) => { + state.showSecurityGroups = show + vueProps.$localStorage.set(SHOW_SECURTIY_GROUPS, show) + }, SET_DOMAIN_STORE (state, domainStore) { state.domainStore = domainStore vueProps.$localStorage.set(DOMAIN_STORE, domainStore) @@ -290,6 +295,7 @@ const user = { const cachedUseBrowserTimezone = vueProps.$localStorage.get(USE_BROWSER_TIMEZONE, false) const cachedCustomColumns = vueProps.$localStorage.get(CUSTOM_COLUMNS, {}) const domainStore = vueProps.$localStorage.get(DOMAIN_STORE, {}) + const cachedShowSecurityGroups = vueProps.$localStorage.get(SHOW_SECURTIY_GROUPS, false) const darkMode = vueProps.$localStorage.get(DARK_MODE, false) const latestVersion = vueProps.$localStorage.get(LATEST_CS_VERSION, { version: '', fetchedTs: 0 }) const hasAuth = Object.keys(cachedApis).length > 0 @@ -300,6 +306,7 @@ const user = { if (hasAuth) { console.log('Login detected, using cached APIs') commit('SET_ZONES', cachedZones) + commit('SET_SHOW_SECURITY_GROUPS', cachedShowSecurityGroups) commit('SET_APIS', cachedApis) commit('SET_TIMEZONE_OFFSET', cachedTimezoneOffset) commit('SET_USE_BROWSER_TIMEZONE', cachedUseBrowserTimezone) @@ -388,6 +395,15 @@ const user = { reject(error) }) + api( + 'listNetworkServiceProviders', + { name: 'SecurityGroupProvider', state: 'Enabled' } + ).then(response => { + const showSecurityGroups = response.listnetworkserviceprovidersresponse.count > 0 + commit('SET_SHOW_SECURITY_GROUPS', showSecurityGroups) + }).catch(ignored => { + }) + api('listCapabilities').then(response => { const result = response.listcapabilitiesresponse.capability commit('SET_FEATURES', result) @@ -397,6 +413,9 @@ const user = { if (result && result.customhypervisordisplayname) { commit('SET_CUSTOM_HYPERVISOR_NAME', result.customhypervisordisplayname) } + if (result && result.securitygroupsenabled) { + commit('SET_SHOW_SECURITY_GROUPS', result.securitygroupsenabled) + } }).catch(error => { reject(error) }) diff --git a/ui/src/store/mutation-types.js b/ui/src/store/mutation-types.js index 93dfc9fbc20..94f28beabdf 100644 --- a/ui/src/store/mutation-types.js +++ b/ui/src/store/mutation-types.js @@ -29,6 +29,7 @@ export const DEFAULT_CONTENT_WIDTH_TYPE = 'DEFAULT_CONTENT_WIDTH_TYPE' export const DEFAULT_MULTI_TAB = 'DEFAULT_MULTI_TAB' export const APIS = 'APIS' export const ZONES = 'ZONES' +export const SHOW_SECURTIY_GROUPS = 'SHOW_SECURITY_GROUPS' export const HEADER_NOTICES = 'HEADER_NOTICES' export const TIMEZONE_OFFSET = 'TIMEZONE_OFFSET' export const USE_BROWSER_TIMEZONE = 'USE_BROWSER_TIMEZONE' diff --git a/ui/src/views/compute/DeployVM.vue b/ui/src/views/compute/DeployVM.vue index 5720e545b24..7d661aa9dc3 100644 --- a/ui/src/views/compute/DeployVM.vue +++ b/ui/src/views/compute/DeployVM.vue @@ -1369,7 +1369,19 @@ export default { return tabList }, showSecurityGroupSection () { - return (this.networks.length > 0 && this.zone?.securitygroupsenabled) || (this.zone?.networktype === 'Basic') + if (this.networks.length < 1) { + return false + } + for (const network of this.options.networks) { + if (this.form.networkids && this.form.networkids.includes(network.id)) { + for (const service of network.service) { + if (service.name === 'SecurityGroup') { + return true + } + } + } + } + return false }, isUserAllowedToListSshKeys () { return Boolean('listSSHKeyPairs' in this.$store.getters.apis) diff --git a/ui/src/views/compute/InstanceTab.vue b/ui/src/views/compute/InstanceTab.vue index 5f08145a4ce..dd2e110d384 100644 --- a/ui/src/views/compute/InstanceTab.vue +++ b/ui/src/views/compute/InstanceTab.vue @@ -117,7 +117,14 @@ :routerlinks="(record) => { return { id: '/backup/' + record.id } }" :showSearch="false"/> - + + + {{ $t('label.action.update.security.groups') }} + + + + + 0) { + this.securitygroupids = [] + for (const sg of this.vm.securitygroup) { + this.securitygroupids.push(sg.id) + } + this.dataPreFill = { securitygroupids: this.securitygroupids } + } + this.showUpdateSecurityGroupsModal = true + this.loadingSG = false + }, closeModals () { this.showAddVolumeModal = false this.showAddNetworkModal = false this.showUpdateIpModal = false this.showSecondaryIpModal = false + this.showUpdateSecurityGroupsModal = false this.addNetworkData.network = '' this.addNetworkData.ip = '' this.editIpAddressValue = '' @@ -730,6 +771,17 @@ export default { this.loadingNic = false this.fetchSecondaryIPs(this.selectedNicId) }) + }, + updateSecurityGroupsSelection (securitygroupids) { + this.securitygroupids = securitygroupids || [] + }, + updateSecurityGroups () { + api('updateVirtualMachine', { id: this.vm.id, securitygroupids: this.securitygroupids.join(',') }).catch(error => { + this.$notifyError(error) + }).finally(() => { + this.closeModals() + this.parentFetchData() + }) } } } diff --git a/ui/src/views/compute/wizard/SecurityGroupSelection.vue b/ui/src/views/compute/wizard/SecurityGroupSelection.vue index 51f96a569ad..87bf62ddac2 100644 --- a/ui/src/views/compute/wizard/SecurityGroupSelection.vue +++ b/ui/src/views/compute/wizard/SecurityGroupSelection.vue @@ -94,7 +94,7 @@ export default { } ], items: [], - selectedRowKeys: [], + selectedRowKeys: this?.preFillContent?.securitygroupids || [], page: 1, pageSize: 10, keyword: null, diff --git a/ui/src/views/infra/network/ServiceProvidersTab.vue b/ui/src/views/infra/network/ServiceProvidersTab.vue index b01f543478b..5eb8e3dbcce 100644 --- a/ui/src/views/infra/network/ServiceProvidersTab.vue +++ b/ui/src/views/infra/network/ServiceProvidersTab.vue @@ -937,10 +937,38 @@ export default { } ] }, - // { - // title: 'SecurityGroupProvider', - // details: ['name', 'state', 'id', 'servicelist'], - // }, + { + title: 'SecurityGroupProvider', + details: ['name', 'state', 'id', 'servicelist'], + actions: [ + { + api: 'updateNetworkServiceProvider', + icon: 'stop-outlined', + listView: true, + label: 'label.disable.provider', + confirm: 'message.confirm.disable.provider', + show: (record) => { return record && record.id && record.state === 'Enabled' }, + mapping: { + state: { + value: (record) => { return 'Disabled' } + } + } + }, + { + api: 'updateNetworkServiceProvider', + icon: 'play-circle-outlined', + listView: true, + label: 'label.enable.provider', + confirm: 'message.confirm.enable.provider', + show: (record) => { return record && record.id && record.state === 'Disabled' }, + mapping: { + state: { + value: (record) => { return 'Enabled' } + } + } + } + ] + }, { title: 'VirtualRouter', actions: [