From 4b8af6f0fa29f51df2d5f1c70a29de82c6fd4e18 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Tue, 12 Jul 2022 12:54:53 +0530 Subject: [PATCH 1/7] test: add, refactor ipv6 network, vpc tests (#6338) - Refactor IPv6 related tests - Adds smoke test for IPv4 network to IPv6 upgrade - Adds smoke test for IPv6 VPC Signed-off-by: Abhishek Kumar --- .../cloudstack/api/response/VpcResponse.java | 4 + .../offerings/dao/NetworkOfferingDaoImpl.java | 2 +- .../dao/NetworkOfferingDaoImplTest.java | 93 ++ .../ConfigurationManagerTest.java | 168 +++ .../cloud/network/Ipv6ServiceImplTest.java | 781 +++++++++++++ .../cloud/network/vpc/VpcManagerImplTest.java | 33 +- .../component/test_network_ipv6.py | 935 +++++++++++++++ test/integration/component/test_vpc_ipv6.py | 1026 +++++++++++++++++ test/integration/smoke/test_ipv6_infra.py | 488 ++++++++ test/integration/smoke/test_network_ipv6.py | 713 +++--------- test/integration/smoke/test_vpc_ipv6.py | 901 +++++++++++++++ tools/marvin/marvin/lib/base.py | 13 + tools/marvin/marvin/sshClient.py | 8 +- 13 files changed, 4569 insertions(+), 596 deletions(-) create mode 100644 engine/schema/src/test/java/com/cloud/offerings/dao/NetworkOfferingDaoImplTest.java create mode 100644 server/src/test/java/com/cloud/network/Ipv6ServiceImplTest.java create mode 100644 test/integration/component/test_network_ipv6.py create mode 100644 test/integration/component/test_vpc_ipv6.py create mode 100644 test/integration/smoke/test_ipv6_infra.py create mode 100644 test/integration/smoke/test_vpc_ipv6.py diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VpcResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VpcResponse.java index eb468cbbbd4..3b5661f8a80 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/VpcResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/VpcResponse.java @@ -253,4 +253,8 @@ public class VpcResponse extends BaseResponseWithAnnotations implements Controll public void setIpv6Routes(Set ipv6Routes) { this.ipv6Routes = ipv6Routes; } + + public Set getIpv6Routes() { + return ipv6Routes; + } } diff --git a/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingDaoImpl.java b/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingDaoImpl.java index 65362eba3c7..a35e7914895 100644 --- a/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingDaoImpl.java @@ -279,7 +279,7 @@ public class NetworkOfferingDaoImpl extends GenericDaoBase()); + final List persistedPrefix = new ArrayList<>(); + Mockito.when(dataCenterGuestIpv6PrefixDao.persist(Mockito.any(DataCenterGuestIpv6PrefixVO.class))).thenAnswer((Answer) invocation -> { + DataCenterGuestIpv6PrefixVO prefixVO = (DataCenterGuestIpv6PrefixVO)invocation.getArgument(0); + persistedPrefix.add(prefixVO); + return prefixVO; + }); + configurationMgr.createDataCenterGuestIpv6Prefix(cmd); + Assert.assertEquals(1, persistedPrefix.size()); + DataCenterGuestIpv6PrefixVO prefixVO = persistedPrefix.get(0); + Assert.assertEquals(zoneId, prefixVO.getDataCenterId()); + Assert.assertEquals(prefix, prefixVO.getPrefix()); + } + + @Test + public void testListDataCenterGuestIpv6Prefixes() { + ListGuestNetworkIpv6PrefixesCmd cmd = Mockito.mock(ListGuestNetworkIpv6PrefixesCmd.class); + Mockito.when(cmd.getId()).thenReturn(1L); + Mockito.when(cmd.getZoneId()).thenReturn(1L); + Mockito.when(_zoneDao.findById(Mockito.anyLong())).thenReturn(Mockito.mock(DataCenterVO.class)); + Mockito.when(dataCenterGuestIpv6PrefixDao.findById(Mockito.anyLong())).thenReturn(Mockito.mock(DataCenterGuestIpv6PrefixVO.class)); + Mockito.when(dataCenterGuestIpv6PrefixDao.listByDataCenterId(Mockito.anyLong())) + .thenReturn(List.of(Mockito.mock(DataCenterGuestIpv6PrefixVO.class), Mockito.mock(DataCenterGuestIpv6PrefixVO.class))); + Mockito.when(dataCenterGuestIpv6PrefixDao.listAll()) + .thenReturn(List.of(Mockito.mock(DataCenterGuestIpv6PrefixVO.class), + Mockito.mock(DataCenterGuestIpv6PrefixVO.class), + Mockito.mock(DataCenterGuestIpv6PrefixVO.class))); + List prefixes = configurationMgr.listDataCenterGuestIpv6Prefixes(cmd); + Assert.assertEquals(1, prefixes.size()); + ListGuestNetworkIpv6PrefixesCmd cmd1 = Mockito.mock(ListGuestNetworkIpv6PrefixesCmd.class); + Mockito.when(cmd1.getId()).thenReturn(null); + Mockito.when(cmd1.getZoneId()).thenReturn(1L); + prefixes = configurationMgr.listDataCenterGuestIpv6Prefixes(cmd1); + Assert.assertEquals(2, prefixes.size()); + ListGuestNetworkIpv6PrefixesCmd cmd2 = Mockito.mock(ListGuestNetworkIpv6PrefixesCmd.class); + Mockito.when(cmd2.getId()).thenReturn(null); + Mockito.when(cmd2.getZoneId()).thenReturn(null); + prefixes = configurationMgr.listDataCenterGuestIpv6Prefixes(cmd2); + Assert.assertEquals(3, prefixes.size()); + } + + @Test(expected = InvalidParameterValueException.class) + public void testInvalidDeleteDataCenterGuestIpv6Prefix() { + DeleteGuestNetworkIpv6PrefixCmd cmd = Mockito.mock(DeleteGuestNetworkIpv6PrefixCmd.class); + Mockito.when(cmd.getId()).thenReturn(1L); + Mockito.when(dataCenterGuestIpv6PrefixDao.findById(Mockito.anyLong())).thenReturn(null); + configurationMgr.deleteDataCenterGuestIpv6Prefix(cmd); + } + + @Test(expected = CloudRuntimeException.class) + public void testUsedDeleteDataCenterGuestIpv6Prefix() { + final Long prefixId = 1L; + DeleteGuestNetworkIpv6PrefixCmd cmd = Mockito.mock(DeleteGuestNetworkIpv6PrefixCmd.class); + Mockito.when(cmd.getId()).thenReturn(prefixId); + DataCenterGuestIpv6PrefixVO prefixVO = Mockito.mock(DataCenterGuestIpv6PrefixVO.class); + Mockito.when(prefixVO.getId()).thenReturn(prefixId); + Mockito.when(dataCenterGuestIpv6PrefixDao.findById(Mockito.anyLong())).thenReturn(prefixVO); + Mockito.when(ipv6GuestPrefixSubnetNetworkMapDao.listUsedByPrefix(Mockito.anyLong())) + .thenReturn(List.of(Mockito.mock(Ipv6GuestPrefixSubnetNetworkMapVO.class))); + configurationMgr.deleteDataCenterGuestIpv6Prefix(cmd); + } + + @Test + public void testDeleteDataCenterGuestIpv6Prefix() { + final Long prefixId = 1L; + DeleteGuestNetworkIpv6PrefixCmd cmd = Mockito.mock(DeleteGuestNetworkIpv6PrefixCmd.class); + Mockito.when(cmd.getId()).thenReturn(prefixId); + DataCenterGuestIpv6PrefixVO prefixVO = Mockito.mock(DataCenterGuestIpv6PrefixVO.class); + Mockito.when(prefixVO.getId()).thenReturn(prefixId); + Mockito.when(dataCenterGuestIpv6PrefixDao.findById(Mockito.anyLong())).thenReturn(prefixVO); + Mockito.when(ipv6GuestPrefixSubnetNetworkMapDao.listUsedByPrefix(Mockito.anyLong())).thenReturn(new ArrayList<>()); + final List removedPrefix = new ArrayList<>(); + Mockito.when(dataCenterGuestIpv6PrefixDao.remove(Mockito.anyLong())).thenAnswer((Answer) invocation -> { + removedPrefix.add(invocation.getArgument(0)); + return true; + }); + configurationMgr.deleteDataCenterGuestIpv6Prefix(cmd); + Assert.assertEquals(1, removedPrefix.size()); + Assert.assertEquals(prefixId, removedPrefix.get(0)); + } + + @Test(expected = InvalidParameterValueException.class) + public void testInvalidNetworkTypeCreateIpv6NetworkOffering() { + CreateNetworkOfferingCmd cmd = Mockito.mock(CreateNetworkOfferingCmd.class); + Mockito.when(cmd.getTraffictype()).thenReturn(Networks.TrafficType.Guest.toString()); + Mockito.when(cmd.getGuestIpType()).thenReturn(Network.GuestType.L2.toString()); + Mockito.when(cmd.getInternetProtocol()).thenReturn(NetUtils.InternetProtocol.DualStack.toString()); + configurationMgr.createNetworkOffering(cmd); + } + + @Test(expected = InvalidParameterValueException.class) + public void testDisabledConfigCreateIpv6NetworkOffering() { + CreateNetworkOfferingCmd cmd = Mockito.mock(CreateNetworkOfferingCmd.class); + Mockito.when(cmd.getTraffictype()).thenReturn(Networks.TrafficType.Guest.toString()); + Mockito.when(cmd.getGuestIpType()).thenReturn(Network.GuestType.Isolated.toString()); + Mockito.when(cmd.getInternetProtocol()).thenReturn(NetUtils.InternetProtocol.DualStack.toString()); + configurationMgr.createNetworkOffering(cmd); + } + + @Test(expected = InvalidParameterValueException.class) + public void testWrongIpv6CreateVlanAndPublicIpRange() { + CreateVlanIpRangeCmd cmd = Mockito.mock(CreateVlanIpRangeCmd.class); + Mockito.when(cmd.getIp6Cidr()).thenReturn("fd17:5:8a43:e2a4:c000::/66"); + try { + configurationMgr.createVlanAndPublicIpRange(cmd); + } catch (InsufficientCapacityException | ResourceUnavailableException | ResourceAllocationException e) { + throw new RuntimeException(e); + } + } } diff --git a/server/src/test/java/com/cloud/network/Ipv6ServiceImplTest.java b/server/src/test/java/com/cloud/network/Ipv6ServiceImplTest.java new file mode 100644 index 00000000000..a648b968262 --- /dev/null +++ b/server/src/test/java/com/cloud/network/Ipv6ServiceImplTest.java @@ -0,0 +1,781 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.UUID; + +import javax.management.InstanceAlreadyExistsException; +import javax.management.MBeanRegistrationException; +import javax.management.MalformedObjectNameException; +import javax.management.NotCompliantMBeanException; + +import org.apache.cloudstack.api.command.user.ipv6.CreateIpv6FirewallRuleCmd; +import org.apache.cloudstack.api.command.user.ipv6.UpdateIpv6FirewallRuleCmd; +import org.apache.cloudstack.api.response.Ipv6RouteResponse; +import org.apache.cloudstack.api.response.VpcResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; +import org.apache.commons.collections.CollectionUtils; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import com.cloud.api.ApiDBUtils; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterGuestIpv6PrefixVO; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.Vlan; +import com.cloud.dc.VlanVO; +import com.cloud.dc.dao.DataCenterGuestIpv6PrefixDao; +import com.cloud.dc.dao.VlanDao; +import com.cloud.event.ActionEventUtils; +import com.cloud.event.UsageEventUtils; +import com.cloud.exception.InsufficientAddressCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.network.dao.FirewallRulesDao; +import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.dao.IPAddressVO; +import com.cloud.network.dao.Ipv6GuestPrefixSubnetNetworkMapDao; +import com.cloud.network.dao.NetworkDetailsDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.firewall.FirewallService; +import com.cloud.network.guru.PublicNetworkGuru; +import com.cloud.network.rules.FirewallManager; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.FirewallRuleVO; +import com.cloud.network.vpc.Vpc; +import com.cloud.offerings.dao.NetworkOfferingDao; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.AccountVO; +import com.cloud.user.User; +import com.cloud.user.UserVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.DomainRouterVO; +import com.cloud.vm.Nic; +import com.cloud.vm.NicProfile; +import com.cloud.vm.NicVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.dao.DomainRouterDao; +import com.cloud.vm.dao.NicDao; +import com.googlecode.ipv6.IPv6Network; +import com.googlecode.ipv6.IPv6NetworkMask; + +@PowerMockIgnore("javax.management.*") +@RunWith(PowerMockRunner.class) +@PrepareForTest({ApiDBUtils.class, ActionEventUtils.class, UsageEventUtils.class}) +public class Ipv6ServiceImplTest { + + @Mock + NetworkOfferingDao networkOfferingDao; + @Mock + VlanDao vlanDao; + @Mock + DataCenterGuestIpv6PrefixDao dataCenterGuestIpv6PrefixDao; + @Mock + Ipv6GuestPrefixSubnetNetworkMapDao ipv6GuestPrefixSubnetNetworkMapDao; + @Mock + FirewallRulesDao firewallDao; + @Mock + FirewallService firewallService; + @Mock + NetworkDetailsDao networkDetailsDao; + @Mock + NicDao nicDao; + @Mock + DomainRouterDao domainRouterDao; + @Mock + AccountManager accountManager; + @Mock + NetworkModel networkModel = Mockito.mock(NetworkModelImpl.class); + @Mock + IPAddressDao ipAddressDao; + @Mock + NetworkOrchestrationService networkOrchestrationService; + + FirewallManager firewallManager = Mockito.mock(FirewallManager.class); + + @InjectMocks + private Ipv6ServiceImpl ipv6Service = new Ipv6ServiceImpl(); + + List updatedPrefixSubnetMap; + + List persistedPrefixSubnetMap; + + final String publicReserver = PublicNetworkGuru.class.getSimpleName(); + final String vlan = "vlan"; + final Long networkId = 101L; + final Long nicId = 100L; + final String ipv6Prefix = "fd17:6:8a43:e2a4::/62"; // Will have 4 /64 subnets + final String cidr = "fd17:5:8a43:e2a5::/64"; + final String gateway = "fd17:5:8a43:e2a5::1"; + final String macAddress = "1e:00:4c:00:00:03"; + final String ipv6Address = "fd17:5:8a43:e2a5:1c00:4cff:fe00:3"; // Resulting IPv6 address using SLAAC + public static final long ACCOUNT_ID = 1; + + private AccountVO account; + private UserVO user; + + @Before + public void setup() { + updatedPrefixSubnetMap = new ArrayList<>(); + persistedPrefixSubnetMap = new ArrayList<>(); + MockitoAnnotations.initMocks(this); + ipv6Service.firewallManager = firewallManager; + Mockito.when(ipv6GuestPrefixSubnetNetworkMapDao.update(Mockito.anyLong(), Mockito.any(Ipv6GuestPrefixSubnetNetworkMapVO.class))).thenAnswer((Answer) invocation -> { + Ipv6GuestPrefixSubnetNetworkMapVO map = (Ipv6GuestPrefixSubnetNetworkMapVO)invocation.getArguments()[1]; + updatedPrefixSubnetMap.add(map); + return true; + }); + Mockito.when(ipv6GuestPrefixSubnetNetworkMapDao.persist(Mockito.any(Ipv6GuestPrefixSubnetNetworkMapVO.class))).thenAnswer((Answer) invocation -> { + Ipv6GuestPrefixSubnetNetworkMapVO map = (Ipv6GuestPrefixSubnetNetworkMapVO)invocation.getArguments()[0]; + persistedPrefixSubnetMap.add(map); + return map; + }); + PowerMockito.mockStatic(ApiDBUtils.class); + Mockito.when(ApiDBUtils.findZoneById(Mockito.anyLong())).thenReturn(Mockito.mock(DataCenterVO.class)); + } + + private DataCenterGuestIpv6PrefixVO prepareMocksForIpv6Subnet() { + final long prefixId = 1L; + DataCenterGuestIpv6PrefixVO prefix = Mockito.mock(DataCenterGuestIpv6PrefixVO.class); + Mockito.when(prefix.getId()).thenReturn(prefixId); + Mockito.when(prefix.getPrefix()).thenReturn(ipv6Prefix); + List subnets = new ArrayList<>(); + Ipv6GuestPrefixSubnetNetworkMapVO subnetMap = new Ipv6GuestPrefixSubnetNetworkMapVO(prefixId, "subnet", 1L, Ipv6GuestPrefixSubnetNetworkMap.State.Allocated); + subnets.add(subnetMap); + subnetMap = new Ipv6GuestPrefixSubnetNetworkMapVO(1L, "subnet", 2L, Ipv6GuestPrefixSubnetNetworkMap.State.Allocated); + subnets.add(subnetMap); + Mockito.when(ipv6GuestPrefixSubnetNetworkMapDao.listUsedByPrefix(prefixId)).thenReturn(subnets); + return prefix; + } + + @Test + public void testGetUsedTotalIpv6SubnetForPrefix() { + DataCenterGuestIpv6PrefixVO prefix = prepareMocksForIpv6Subnet(); + Pair results = ipv6Service.getUsedTotalIpv6SubnetForPrefix(prefix); + Assert.assertEquals(2, results.first().intValue()); + Assert.assertEquals(4, results.second().intValue()); + } + + @Test + public void testNoPrefixesGetUsedTotalIpv6SubnetForZone() { + final long zoneId = 1L; + final List prefixes = new ArrayList<>(); + Mockito.when(dataCenterGuestIpv6PrefixDao.listByDataCenterId(zoneId)).thenReturn(prefixes); + Pair results = ipv6Service.getUsedTotalIpv6SubnetForZone(zoneId); + Assert.assertEquals(0, results.first().intValue()); + Assert.assertEquals(0, results.second().intValue()); + } + + @Test + public void testGetUsedTotalIpv6SubnetForZone() { + final long zoneId = 1L; + final List prefixes = new ArrayList<>(); + DataCenterGuestIpv6PrefixVO prefix = prepareMocksForIpv6Subnet(); + prefixes.add(prefix); + prefixes.add(prefix); + Mockito.when(dataCenterGuestIpv6PrefixDao.listByDataCenterId(zoneId)).thenReturn(prefixes); + Pair results = ipv6Service.getUsedTotalIpv6SubnetForZone(zoneId); + Assert.assertEquals(4, results.first().intValue()); + Assert.assertEquals(8, results.second().intValue()); + } + + @Test(expected = ResourceAllocationException.class) + @DB + public void testNoPrefixesPreAllocateIpv6SubnetForNetwork() throws ResourceAllocationException, MalformedObjectNameException, NotCompliantMBeanException, InstanceAlreadyExistsException, MBeanRegistrationException { + final long zoneId = 1L; + final List prefixes = new ArrayList<>(); + Mockito.when(dataCenterGuestIpv6PrefixDao.listByDataCenterId(zoneId)).thenReturn(prefixes); + TransactionLegacy txn = TransactionLegacy.open("testNoPrefixesPreAllocateIpv6SubnetForNetwork"); + try { + ipv6Service.preAllocateIpv6SubnetForNetwork(zoneId); + } finally { + txn.close("testNoPrefixesPreAllocateIpv6SubnetForNetwork"); + } + } + + @Test + @DB + public void testExistingPreAllocateIpv6SubnetForNetwork() { + final long zoneId = 1L; + final List prefixes = new ArrayList<>(); + DataCenterGuestIpv6PrefixVO prefix = prepareMocksForIpv6Subnet(); + prefixes.add(prefix); + Ipv6GuestPrefixSubnetNetworkMapVO ipv6GuestPrefixSubnetNetworkMap = new Ipv6GuestPrefixSubnetNetworkMapVO(1L, "fd17:5:8a43:e2a4::/64", null, Ipv6GuestPrefixSubnetNetworkMap.State.Free); + Mockito.when(dataCenterGuestIpv6PrefixDao.listByDataCenterId(zoneId)).thenReturn(prefixes); + Mockito.when(ipv6GuestPrefixSubnetNetworkMapDao.findFirstAvailable(prefix.getId())).thenReturn(ipv6GuestPrefixSubnetNetworkMap); + updatedPrefixSubnetMap.clear(); + try (TransactionLegacy txn = TransactionLegacy.open("testNoPrefixesPreAllocateIpv6SubnetForNetwork")) { + try { + ipv6Service.preAllocateIpv6SubnetForNetwork(zoneId); + } catch (ResourceAllocationException e) { + Assert.fail("ResourceAllocationException"); + } + } + Assert.assertEquals(1, updatedPrefixSubnetMap.size()); + Ipv6GuestPrefixSubnetNetworkMapVO map = updatedPrefixSubnetMap.get(0); + Assert.assertEquals(Ipv6GuestPrefixSubnetNetworkMap.State.Allocating, map.getState()); + Assert.assertEquals(ipv6GuestPrefixSubnetNetworkMap.getSubnet(), map.getSubnet()); + Assert.assertEquals(ipv6GuestPrefixSubnetNetworkMap.getPrefixId(), map.getPrefixId()); + Assert.assertNull(map.getNetworkId()); + } + + @Test + @DB + public void testNewPreAllocateIpv6SubnetForNetwork() { + final long zoneId = 1L; + final List prefixes = new ArrayList<>(); + DataCenterGuestIpv6PrefixVO prefix = prepareMocksForIpv6Subnet(); + final IPv6Network ip6Prefix = IPv6Network.fromString(prefix.getPrefix()); + Iterator splits = ip6Prefix.split(IPv6NetworkMask.fromPrefixLength(Ipv6Service.IPV6_SLAAC_CIDR_NETMASK)); + List subnets = new ArrayList<>(); + while(splits.hasNext()) { + subnets.add(splits.next().toString()); + } + prefixes.add(prefix); + Mockito.when(dataCenterGuestIpv6PrefixDao.listByDataCenterId(zoneId)).thenReturn(prefixes); + Mockito.when(ipv6GuestPrefixSubnetNetworkMapDao.findFirstAvailable(prefix.getId())).thenReturn(null); + Mockito.when(ipv6GuestPrefixSubnetNetworkMapDao.listUsedByPrefix(prefix.getId())).thenReturn(new ArrayList<>()); + persistedPrefixSubnetMap.clear(); + // No subnet is used from the prefix, should allocate any subnet + try (TransactionLegacy txn = TransactionLegacy.open("testNewPreAllocateIpv6SubnetForNetwork")) { + try { + ipv6Service.preAllocateIpv6SubnetForNetwork(zoneId); + } catch (ResourceAllocationException e) { + Assert.fail("ResourceAllocationException"); + } + } + Assert.assertEquals(1, persistedPrefixSubnetMap.size()); + Ipv6GuestPrefixSubnetNetworkMapVO map = persistedPrefixSubnetMap.get(0); + Assert.assertEquals(Ipv6GuestPrefixSubnetNetworkMap.State.Allocating, map.getState()); + Assert.assertTrue(subnets.contains(map.getSubnet())); + Assert.assertEquals(prefix.getId(), map.getPrefixId()); + Assert.assertNull(map.getNetworkId()); + List usedSubnets = new ArrayList<>(); + for (String subnet : subnets) { + usedSubnets.add(new Ipv6GuestPrefixSubnetNetworkMapVO(prefix.getId(), subnet, 1L, Ipv6GuestPrefixSubnetNetworkMap.State.Allocated)); + } + Mockito.when(ipv6GuestPrefixSubnetNetworkMapDao.listUsedByPrefix(prefix.getId())).thenReturn(usedSubnets); + + // All subnets from the prefix are already in use, should return ResourceAllocationException + try (TransactionLegacy txn = TransactionLegacy.open("testNewPreAllocateIpv6SubnetForNetwork")) { + try { + ipv6Service.preAllocateIpv6SubnetForNetwork(zoneId); + Assert.fail("ResourceAllocationException expected but not returned"); + } catch (ResourceAllocationException ignored) {} + } + persistedPrefixSubnetMap.clear(); + + // 3 out of 4 subnet from the prefix are in use, should return the remaining one + Ipv6GuestPrefixSubnetNetworkMapVO poppedUsedSubnetMap = usedSubnets.remove(2); + try (TransactionLegacy txn = TransactionLegacy.open("testNewPreAllocateIpv6SubnetForNetwork")) { + try { + ipv6Service.preAllocateIpv6SubnetForNetwork(zoneId); + } catch (ResourceAllocationException e) { + Assert.fail("ResourceAllocationException"); + } + } + Assert.assertEquals(1, persistedPrefixSubnetMap.size()); + map = persistedPrefixSubnetMap.get(0); + Assert.assertEquals(Ipv6GuestPrefixSubnetNetworkMap.State.Allocating, map.getState()); + Assert.assertEquals(poppedUsedSubnetMap.getSubnet(), map.getSubnet()); + Assert.assertEquals(prefix.getId(), map.getPrefixId()); + Assert.assertNull(map.getNetworkId()); + } + + @Test + @DB + public void testAssignIpv6SubnetToNetwork() { + final long prefixId = 1L; + final String subnet = "fd17:5:8a43:e2a5::/64"; + final Long networkId = 100L; + Ipv6GuestPrefixSubnetNetworkMapVO allocatingMap = new Ipv6GuestPrefixSubnetNetworkMapVO(prefixId, subnet, null, Ipv6GuestPrefixSubnetNetworkMap.State.Allocating); + Mockito.when(ipv6GuestPrefixSubnetNetworkMapDao.findBySubnet(subnet)).thenReturn(allocatingMap); + Mockito.when(ipv6GuestPrefixSubnetNetworkMapDao.createForUpdate(Mockito.anyLong())).thenReturn(allocatingMap); + updatedPrefixSubnetMap.clear(); + try (TransactionLegacy txn = TransactionLegacy.open("testNewPreAllocateIpv6SubnetForNetwork")) { + ipv6Service.assignIpv6SubnetToNetwork(subnet, networkId); + } + Assert.assertEquals(1, updatedPrefixSubnetMap.size()); + Ipv6GuestPrefixSubnetNetworkMapVO map = updatedPrefixSubnetMap.get(0); + Assert.assertEquals(Ipv6GuestPrefixSubnetNetworkMap.State.Allocated, map.getState()); + Assert.assertEquals(subnet, map.getSubnet()); + Assert.assertEquals(prefixId, map.getPrefixId()); + Assert.assertEquals(networkId, map.getNetworkId()); + } + + @Test + @DB + public void testReleaseIpv6SubnetForNetwork() { + final long prefixId = 1L; + final String subnet = "fd17:5:8a43:e2a5::/64"; + Ipv6GuestPrefixSubnetNetworkMapVO allocatingMap = new Ipv6GuestPrefixSubnetNetworkMapVO(prefixId, subnet, networkId, Ipv6GuestPrefixSubnetNetworkMap.State.Allocated); + Mockito.when(ipv6GuestPrefixSubnetNetworkMapDao.findByNetworkId(networkId)).thenReturn(allocatingMap); + Mockito.when(ipv6GuestPrefixSubnetNetworkMapDao.createForUpdate(Mockito.anyLong())).thenReturn(allocatingMap); + updatedPrefixSubnetMap.clear(); + try (TransactionLegacy txn = TransactionLegacy.open("testNewPreAllocateIpv6SubnetForNetwork")) { + ipv6Service.releaseIpv6SubnetForNetwork(networkId); + } + Assert.assertEquals(1, updatedPrefixSubnetMap.size()); + Ipv6GuestPrefixSubnetNetworkMapVO map = updatedPrefixSubnetMap.get(0); + Assert.assertEquals(Ipv6GuestPrefixSubnetNetworkMap.State.Free, map.getState()); + Assert.assertEquals(subnet, map.getSubnet()); + Assert.assertEquals(prefixId, map.getPrefixId()); + Assert.assertNull(map.getNetworkId()); + } + + @Test + public void testGetAllocatedIpv6FromVlanRange() { + Vlan vlan = Mockito.mock(Vlan.class); + Mockito.when(vlan.getIp6Cidr()).thenReturn(null); + Mockito.when(vlan.getIp6Gateway()).thenReturn(null); + Assert.assertNull(ipv6Service.getAllocatedIpv6FromVlanRange(vlan)); + List addresses = Arrays.asList("fd17:5:8a43:e2a5::1000", "fd17:5:8a43:e2a5::1001"); + Vlan vlan1 = Mockito.mock(Vlan.class); + Mockito.when(vlan1.getIp6Cidr()).thenReturn(cidr); + Mockito.when(vlan1.getIp6Gateway()).thenReturn(gateway); + + List nics = new ArrayList<>(); + for (String address : addresses) { + NicVO nic = new NicVO(publicReserver, 100L, 1L, VirtualMachine.Type.DomainRouter); + nic.setIPv6Address(address); + nics.add(nic); + } + Mockito.when(nicDao.findNicsByIpv6GatewayIpv6CidrAndReserver(gateway, cidr, publicReserver)).thenReturn(nics); + List result = ipv6Service.getAllocatedIpv6FromVlanRange(vlan1); + Assert.assertEquals(addresses.size(), result.size()); + for (String address : addresses) { + Assert.assertTrue(result.contains(address)); + } + } + + @Test + public void testAlreadyExistAssignPublicIpv6ToNetwork() { + Nic nic = Mockito.mock(Nic.class); + Mockito.when(nic.getIPv6Address()).thenReturn(ipv6Address); + Nic assignedNic = ipv6Service.assignPublicIpv6ToNetwork(Mockito.mock(Network.class), nic); + Assert.assertEquals(ipv6Address, assignedNic.getIPv6Address()); + } + + @Test(expected = CloudRuntimeException.class) + public void testNewErrorAssignPublicIpv6ToNetwork() { + Nic nic = Mockito.mock(Nic.class); + Mockito.when(nic.getIPv6Address()).thenReturn(null); + Mockito.when(nic.getBroadcastUri()).thenReturn(URI.create(vlan)); + Mockito.when(vlanDao.listIpv6RangeByPhysicalNetworkIdAndVlanId(1L, "vlan")).thenReturn(new ArrayList<>()); + try (TransactionLegacy txn = TransactionLegacy.open("testNewErrorAssignPublicIpv6ToNetwork")) { + ipv6Service.assignPublicIpv6ToNetwork(Mockito.mock(Network.class), nic); + } + } + + private List mockPlaceholderNics() { + NicVO placeholderNic = Mockito.mock(NicVO.class); + Mockito.when(placeholderNic.getIPv6Address()).thenReturn(ipv6Address); + Mockito.when(placeholderNic.getIPv6Gateway()).thenReturn(gateway); + Mockito.when(placeholderNic.getIPv6Cidr()).thenReturn(cidr); + Mockito.when(placeholderNic.getReserver()).thenReturn(publicReserver); + List placeholderNics = new ArrayList<>(); + placeholderNics.add(placeholderNic); + return placeholderNics; + } + + private void prepareMocksForPublicIpv6(boolean fromPlaceholder) { + VlanVO vlanVO = Mockito.mock(VlanVO.class); + Mockito.when(vlanVO.getIp6Cidr()).thenReturn(cidr); + Mockito.when(vlanVO.getIp6Gateway()).thenReturn(gateway); + Mockito.when(vlanVO.getVlanType()).thenReturn(Vlan.VlanType.VirtualNetwork); + List vlans = new ArrayList<>(); + vlans.add(vlanVO); + Mockito.when(vlanDao.listIpv6RangeByPhysicalNetworkIdAndVlanId(Mockito.anyLong(), Mockito.anyString())).thenReturn(vlans); + List placeholderNics = new ArrayList<>(); + if (fromPlaceholder) { + placeholderNics = mockPlaceholderNics(); + } + Mockito.when(nicDao.listPlaceholderNicsByNetworkIdAndVmType(networkId, VirtualMachine.Type.DomainRouter)).thenReturn(placeholderNics); + Mockito.when(nicDao.createForUpdate(nicId)).thenReturn(new NicVO(publicReserver, 100L, 1L, VirtualMachine.Type.DomainRouter)); + PowerMockito.mockStatic(ActionEventUtils.class); + Mockito.when(ActionEventUtils.onCompletedActionEvent(Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyLong())).thenReturn(1L); + PowerMockito.mockStatic(UsageEventUtils.class); + } + + @Test + @DB + public void testNewAssignPublicIpv6ToNetwork() { + NicVO nic = Mockito.mock(NicVO.class); + Mockito.when(nic.getIPv6Address()).thenReturn(null); + Mockito.when(nic.getBroadcastUri()).thenReturn(URI.create(vlan)); + Mockito.when(nic.getMacAddress()).thenReturn(macAddress); + Mockito.when(nic.getId()).thenReturn(nicId); + Network network = Mockito.mock(Network.class); + Mockito.when(network.getId()).thenReturn(networkId); + prepareMocksForPublicIpv6(false); + Nic assignedNic; + try (TransactionLegacy txn = TransactionLegacy.open("testNewPreAllocateIpv6SubnetForNetwork")) { + assignedNic = ipv6Service.assignPublicIpv6ToNetwork(network, nic); + } + Assert.assertEquals(ipv6Address, assignedNic.getIPv6Address()); + Assert.assertEquals(gateway, assignedNic.getIPv6Gateway()); + Assert.assertEquals(cidr, assignedNic.getIPv6Cidr()); + } + + @Test + public void testFromPlaceholderAssignPublicIpv6ToNetwork() { + NicVO nic = Mockito.mock(NicVO.class); + Mockito.when(nic.getIPv6Address()).thenReturn(null); + Mockito.when(nic.getBroadcastUri()).thenReturn(URI.create(vlan)); + Mockito.when(nic.getId()).thenReturn(nicId); + Network network = Mockito.mock(Network.class); + Mockito.when(network.getId()).thenReturn(networkId); + prepareMocksForPublicIpv6(true); + Nic assignedNic = ipv6Service.assignPublicIpv6ToNetwork(network, nic); + Assert.assertEquals(ipv6Address, assignedNic.getIPv6Address()); + Assert.assertEquals(gateway, assignedNic.getIPv6Gateway()); + Assert.assertEquals(cidr, assignedNic.getIPv6Cidr()); + } + + @Test + public void testIpv4NetworkUpdateNicIpv6() { + Mockito.when(networkOfferingDao.isIpv6Supported(Mockito.anyLong())).thenReturn(false); + NicProfile nicProfile = new NicProfile(); + try { + ipv6Service.updateNicIpv6(nicProfile, Mockito.mock(DataCenter.class), Mockito.mock(Network.class)); + } catch (InsufficientAddressCapacityException e) { + Assert.fail("InsufficientAddressCapacityException"); + } + Assert.assertNull(nicProfile.getIPv6Address()); + Assert.assertNull(nicProfile.getIPv6Gateway()); + Assert.assertNull(nicProfile.getIPv6Cidr()); + } + + @Test + public void testIpv6NetworkUpdateNicIpv6() { + Mockito.when(networkOfferingDao.isIpv6Supported(Mockito.anyLong())).thenReturn(true); + NicProfile nicProfile = new NicProfile(); + nicProfile.setBroadcastUri(URI.create(vlan)); + nicProfile.setMacAddress(macAddress); + prepareMocksForPublicIpv6(false); + try { + ipv6Service.updateNicIpv6(nicProfile, Mockito.mock(DataCenter.class), Mockito.mock(Network.class)); + } catch (InsufficientAddressCapacityException e) { + Assert.fail("InsufficientAddressCapacityException"); + } + Assert.assertEquals(ipv6Address, nicProfile.getIPv6Address()); + Assert.assertEquals(gateway, nicProfile.getIPv6Gateway()); + Assert.assertEquals(cidr, nicProfile.getIPv6Cidr()); + } + + @Test + public void testIpv6NetworkFromPlaceholderUpdateNicIpv6() { + Mockito.when(networkOfferingDao.isIpv6Supported(Mockito.anyLong())).thenReturn(true); + NicProfile nicProfile = new NicProfile(); + nicProfile.setBroadcastUri(URI.create(vlan)); + nicProfile.setMacAddress(macAddress); + prepareMocksForPublicIpv6(true); + try { + ipv6Service.updateNicIpv6(nicProfile, Mockito.mock(DataCenter.class), Mockito.mock(Network.class)); + } catch (InsufficientAddressCapacityException e) { + Assert.fail("InsufficientAddressCapacityException"); + } + Assert.assertEquals(ipv6Address, nicProfile.getIPv6Address()); + Assert.assertEquals(gateway, nicProfile.getIPv6Gateway()); + Assert.assertEquals(cidr, nicProfile.getIPv6Cidr()); + } + + @Test + public void testEmptyGetPublicIpv6AddressesForNetwork(){ + Mockito.when(domainRouterDao.findByNetwork(Mockito.anyLong())).thenReturn(new ArrayList<>()); + List addresses = ipv6Service.getPublicIpv6AddressesForNetwork(Mockito.mock(Network.class)); + Assert.assertTrue(CollectionUtils.isEmpty(addresses)); + List routers = List.of(Mockito.mock(DomainRouterVO.class)); + Mockito.when(domainRouterDao.findByNetwork(Mockito.anyLong())).thenReturn(routers); + Mockito.when(nicDao.listByVmId(Mockito.anyLong())).thenReturn(new ArrayList<>()); + addresses = ipv6Service.getPublicIpv6AddressesForNetwork(Mockito.mock(Network.class)); + Assert.assertTrue(CollectionUtils.isEmpty(addresses)); + NicVO nic = Mockito.mock(NicVO.class); + Mockito.when(nic.getIPv6Address()).thenReturn(null); + List nics = List.of(nic); + Mockito.when(nicDao.listByVmId(Mockito.anyLong())).thenReturn(nics); + addresses = ipv6Service.getPublicIpv6AddressesForNetwork(Mockito.mock(Network.class)); + Assert.assertTrue(CollectionUtils.isEmpty(addresses)); + } + + @Test + public void testGetPublicIpv6AddressesForNetwork(){ + List routers = List.of(Mockito.mock(DomainRouterVO.class), Mockito.mock(DomainRouterVO.class)); + Mockito.when(domainRouterDao.findByNetwork(Mockito.anyLong())).thenReturn(routers); + NicVO nic = Mockito.mock(NicVO.class); + Mockito.when(nic.getIPv6Address()).thenReturn(ipv6Address); + Mockito.when(nic.getReserver()).thenReturn(publicReserver); + List nics = List.of(nic); + Mockito.when(nicDao.listByVmId(Mockito.anyLong())).thenReturn(nics); + List addresses = ipv6Service.getPublicIpv6AddressesForNetwork(Mockito.mock(Network.class)); + Assert.assertEquals(1, addresses.size()); + Assert.assertEquals(ipv6Address, addresses.get(0)); + } + + @Test + public void testEmptyUpdateIpv6RoutesForVpcResponse() { + VpcResponse response = new VpcResponse(); + Vpc vpc = Mockito.mock(Vpc.class); + List networks = new ArrayList<>(); + Mockito.doReturn(networks).when(networkModel).listNetworksByVpc(Mockito.anyLong()); + ipv6Service.updateIpv6RoutesForVpcResponse(vpc, response); + Assert.assertTrue(CollectionUtils.isEmpty(response.getIpv6Routes())); + } + + @Test + public void testUpdateIpv6RoutesForVpcResponse() { + VpcResponse response = new VpcResponse(); + Vpc vpc = Mockito.mock(Vpc.class); + List networks = new ArrayList<>(); + NetworkVO network = Mockito.mock(NetworkVO.class); + Mockito.when(network.getIp6Cidr()).thenReturn(cidr); + networks.add(network); + List routers = List.of(Mockito.mock(DomainRouterVO.class)); + Mockito.when(domainRouterDao.findByNetwork(Mockito.anyLong())).thenReturn(routers); + NicVO nic = Mockito.mock(NicVO.class); + Mockito.when(nic.getIPv6Address()).thenReturn(ipv6Address); + Mockito.when(nic.getReserver()).thenReturn(publicReserver); + Mockito.when(nicDao.listByVmId(Mockito.anyLong())).thenReturn(List.of(nic)); + Mockito.doReturn(networks).when(networkModel).listNetworksByVpc(Mockito.anyLong()); + Mockito.when(networkOfferingDao.isIpv6Supported(Mockito.anyLong())).thenReturn(true); + ipv6Service.updateIpv6RoutesForVpcResponse(vpc, response); + Assert.assertEquals(1, response.getIpv6Routes().size()); + Ipv6RouteResponse routeResponse = new ArrayList<>(response.getIpv6Routes()).get(0); + Assert.assertEquals(ipv6Address, routeResponse.getGateway()); + Assert.assertEquals(cidr, routeResponse.getSubnet()); + } + + @Test + public void testCheckNetworkIpv6UpgradeForNoPrefixes() { + Mockito.when(dataCenterGuestIpv6PrefixDao.listByDataCenterId(Mockito.anyLong())).thenReturn(new ArrayList<>()); + try { + ipv6Service.checkNetworkIpv6Upgrade(Mockito.mock(Network.class)); + Assert.fail("No ResourceAllocationException"); + } catch (InsufficientAddressCapacityException | ResourceAllocationException ignored) {} + } + + @Test + public void testCheckNetworkIpv6UpgradeForNoIpv6Vlan() { + final long physicalNetworkId = 1L; + Mockito.when(dataCenterGuestIpv6PrefixDao.listByDataCenterId(Mockito.anyLong())).thenReturn(List.of(Mockito.mock(DataCenterGuestIpv6PrefixVO.class))); + Network network = Mockito.mock(Network.class); + Mockito.when(network.getPhysicalNetworkId()).thenReturn(physicalNetworkId); + Mockito.when(network.getVpcId()).thenReturn(null); + Mockito.when(ipAddressDao.listByAssociatedNetwork(Mockito.anyLong(), Mockito.anyBoolean())).thenReturn(List.of(Mockito.mock(IPAddressVO.class))); + VlanVO vlanVO = Mockito.mock(VlanVO.class); + Mockito.when(vlanVO.getVlanTag()).thenReturn(vlan); + Mockito.when(vlanDao.findById(Mockito.anyLong())).thenReturn(vlanVO); + Mockito.when(vlanDao.listIpv6RangeByPhysicalNetworkIdAndVlanId(Mockito.anyLong(), Mockito.anyString())).thenReturn(new ArrayList<>()); + try { + ipv6Service.checkNetworkIpv6Upgrade(network); + Assert.fail("No InsufficientAddressCapacityException"); + } catch (InsufficientAddressCapacityException | ResourceAllocationException ignored) {} + } + + @Test + public void testCheckNetworkIpv6UpgradeForNetwork() { + final long physicalNetworkId = 1L; + Mockito.when(dataCenterGuestIpv6PrefixDao.listByDataCenterId(Mockito.anyLong())).thenReturn(List.of(Mockito.mock(DataCenterGuestIpv6PrefixVO.class))); + Network network = Mockito.mock(Network.class); + Mockito.when(network.getPhysicalNetworkId()).thenReturn(physicalNetworkId); + Mockito.when(network.getVpcId()).thenReturn(null); + Mockito.when(ipAddressDao.listByAssociatedNetwork(Mockito.anyLong(), Mockito.anyBoolean())).thenReturn(List.of(Mockito.mock(IPAddressVO.class))); + VlanVO vlanVO = Mockito.mock(VlanVO.class); + Mockito.when(vlanVO.getVlanTag()).thenReturn(vlan); + Mockito.when(vlanDao.findById(Mockito.anyLong())).thenReturn(vlanVO); + Mockito.when(vlanDao.listIpv6RangeByPhysicalNetworkIdAndVlanId(physicalNetworkId, vlan)).thenReturn(List.of(vlanVO)); + try { + ipv6Service.checkNetworkIpv6Upgrade(network); + } catch (InsufficientAddressCapacityException | ResourceAllocationException e) { + throw new RuntimeException(e); + } + } + + @Test + public void testUpdateIpv6FirewallRule() { + final Long firewallRuleId = 1L; + UpdateIpv6FirewallRuleCmd cmd = Mockito.mock(UpdateIpv6FirewallRuleCmd.class); + Mockito.when(cmd.getId()).thenReturn(firewallRuleId); + Mockito.when(firewallDao.findById(firewallRuleId)).thenReturn(null); + try { + ipv6Service.updateIpv6FirewallRule(cmd); + Assert.fail("No InvalidParameterValueException"); + } catch (InvalidParameterValueException ignored) {} + FirewallRuleVO ingressFirewallRule = Mockito.mock(FirewallRuleVO.class); + Mockito.when(ingressFirewallRule.getTrafficType()).thenReturn(FirewallRule.TrafficType.Ingress); + Mockito.when(firewallDao.findById(firewallRuleId)).thenReturn(ingressFirewallRule); + try { + ipv6Service.updateIpv6FirewallRule(cmd); + } catch (InvalidParameterValueException e) { + throw new RuntimeException(e); + } + } + + @Test + public void testDeleteIpv6FirewallRule() { + final Long firewallRuleId = 1L; + Mockito.when(firewallDao.findById(firewallRuleId)).thenReturn(null); + try { + ipv6Service.revokeIpv6FirewallRule(firewallRuleId); + Assert.fail("No InvalidParameterValueException"); + } catch (InvalidParameterValueException ignored) {} + FirewallRuleVO ingressFirewallRule = Mockito.mock(FirewallRuleVO.class); + Mockito.when(ingressFirewallRule.getTrafficType()).thenReturn(FirewallRule.TrafficType.Ingress); + Mockito.when(firewallDao.findById(firewallRuleId)).thenReturn(ingressFirewallRule); + try { + ipv6Service.revokeIpv6FirewallRule(firewallRuleId); + } catch (InvalidParameterValueException e) { + throw new RuntimeException(e); + } + } + + @Test + public void testGetIpv6FirewallRule() { + final Long firewallRuleId = 1L; + final String uuid = UUID.randomUUID().toString(); + Mockito.when(firewallDao.findById(firewallRuleId)).thenReturn(null); + FirewallRule rule = ipv6Service.getIpv6FirewallRule(firewallRuleId); + Assert.assertNull(rule); + FirewallRuleVO ingressFirewallRule = Mockito.mock(FirewallRuleVO.class); + Mockito.when(ingressFirewallRule.getUuid()).thenReturn(uuid); + Mockito.when(firewallDao.findById(firewallRuleId)).thenReturn(ingressFirewallRule); + rule = ipv6Service.getIpv6FirewallRule(firewallRuleId); + Assert.assertEquals(uuid, rule.getUuid()); + } + + private void registerCallContext() { + account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid"); + account.setId(ACCOUNT_ID); + user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", + UUID.randomUUID().toString(), User.Source.UNKNOWN); + CallContext.register(user, account); + } + + @Test(expected = InvalidParameterValueException.class) + public void testInvalidSourceCidrCreateIpv6FirewallRule() { + registerCallContext(); + CreateIpv6FirewallRuleCmd cmd = Mockito.mock(CreateIpv6FirewallRuleCmd.class); + Mockito.when(cmd.getSourceCidrList()).thenReturn(List.of("10.1.1.1")); + try { + ipv6Service.createIpv6FirewallRule(cmd); + } catch (NetworkRuleConflictException e) { + throw new RuntimeException(e); + } + } + + @Test(expected = InvalidParameterValueException.class) + public void testInvalidDestinationCidrCreateIpv6FirewallRule() { + registerCallContext(); + CreateIpv6FirewallRuleCmd cmd = Mockito.mock(CreateIpv6FirewallRuleCmd.class); + Mockito.when(cmd.getDestinationCidrList()).thenReturn(List.of("10.1.1.1")); + try { + ipv6Service.createIpv6FirewallRule(cmd); + } catch (NetworkRuleConflictException e) { + throw new RuntimeException(e); + } + } + + @Test(expected = InvalidParameterValueException.class) + public void testStartPortCidrCreateIpv6FirewallRule() { + registerCallContext(); + CreateIpv6FirewallRuleCmd cmd = Mockito.mock(CreateIpv6FirewallRuleCmd.class); + Mockito.when(cmd.getSourcePortStart()).thenReturn(800000); + try { + ipv6Service.createIpv6FirewallRule(cmd); + } catch (NetworkRuleConflictException e) { + throw new RuntimeException(e); + } + } + + @Test(expected = InvalidParameterValueException.class) + public void testEndPortCidrCreateIpv6FirewallRule() { + registerCallContext(); + CreateIpv6FirewallRuleCmd cmd = Mockito.mock(CreateIpv6FirewallRuleCmd.class); + Mockito.when(cmd.getSourcePortEnd()).thenReturn(800000); + try { + ipv6Service.createIpv6FirewallRule(cmd); + } catch (NetworkRuleConflictException e) { + throw new RuntimeException(e); + } + } + + @Test(expected = InvalidParameterValueException.class) + public void testPortRangeCidrCreateIpv6FirewallRule() { + registerCallContext(); + CreateIpv6FirewallRuleCmd cmd = Mockito.mock(CreateIpv6FirewallRuleCmd.class); + Mockito.when(cmd.getSourcePortStart()).thenReturn(900); + Mockito.when(cmd.getSourcePortEnd()).thenReturn(800); + try { + ipv6Service.createIpv6FirewallRule(cmd); + } catch (NetworkRuleConflictException e) { + throw new RuntimeException(e); + } + } + + @Test + public void testRemovePublicIpv6PlaceholderNics() { + Network network = Mockito.mock(NetworkVO.class); + Mockito.when(network.getId()).thenReturn(networkId); + NicVO nic = Mockito.mock(NicVO.class); + Mockito.when(nic.getId()).thenReturn(nicId); + Mockito.when(nic.getIPv6Address()).thenReturn(ipv6Address); + Mockito.when(nic.getIPv6Cidr()).thenReturn(cidr); + Mockito.when(nic.getIPv6Gateway()).thenReturn(gateway); + Mockito.when(nic.getReserver()).thenReturn(publicReserver); + Mockito.when(nicDao.listPlaceholderNicsByNetworkId(Mockito.anyLong())).thenReturn(List.of(nic)); + final List removedNics = new ArrayList<>(); + Mockito.when(nicDao.remove(Mockito.anyLong())).thenAnswer((Answer) invocation -> { + removedNics.add((Long)invocation.getArguments()[0]); + return true; + }); + PowerMockito.mockStatic(ActionEventUtils.class); + Mockito.when(ActionEventUtils.onCompletedActionEvent(Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyLong())).thenReturn(1L); + PowerMockito.mockStatic(UsageEventUtils.class); + ipv6Service.removePublicIpv6PlaceholderNics(network); + Assert.assertEquals(1, removedNics.size()); + Assert.assertEquals(nicId, removedNics.get(0)); + removedNics.clear(); + NicVO nic1 = Mockito.mock(NicVO.class); + Mockito.when(nic1.getId()).thenReturn(nicId); + Mockito.when(nic1.getIPv6Address()).thenReturn(null); + Mockito.when(nicDao.listPlaceholderNicsByNetworkId(Mockito.anyLong())).thenReturn(List.of(nic1)); + ipv6Service.removePublicIpv6PlaceholderNics(network); + Assert.assertEquals(0, removedNics.size()); + } +} diff --git a/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java b/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java index fb0776f551a..10b59fde8b1 100644 --- a/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java +++ b/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java @@ -20,31 +20,33 @@ package com.cloud.network.vpc; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.powermock.api.mockito.PowerMockito.when; -import com.cloud.exception.InvalidParameterValueException; -import com.cloud.network.NetworkModel; -import com.cloud.network.element.NetworkElement; - -import com.cloud.network.Network; -import com.cloud.network.Network.Capability; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; + +import org.apache.cloudstack.api.command.admin.vpc.CreateVPCOfferingCmd; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; - -import com.cloud.network.Network.Provider; -import com.cloud.network.Network.Service; -import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao; import org.powermock.reflect.Whitebox; -import static org.mockito.Mockito.mock; -import static org.powermock.api.mockito.PowerMockito.when; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.Network; +import com.cloud.network.Network.Capability; +import com.cloud.network.Network.Provider; +import com.cloud.network.Network.Service; +import com.cloud.network.NetworkModel; +import com.cloud.network.element.NetworkElement; +import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao; +import com.cloud.utils.net.NetUtils; public class VpcManagerImplTest { @@ -162,4 +164,11 @@ public class VpcManagerImplTest { return providers; } + + @Test(expected = InvalidParameterValueException.class) + public void testDisabledConfigCreateIpv6VpcOffering() { + CreateVPCOfferingCmd cmd = Mockito.mock(CreateVPCOfferingCmd.class); + Mockito.when(cmd.getInternetProtocol()).thenReturn(NetUtils.InternetProtocol.DualStack.toString()); + manager.createVpcOffering(cmd); + } } diff --git a/test/integration/component/test_network_ipv6.py b/test/integration/component/test_network_ipv6.py new file mode 100644 index 00000000000..29afb8d97be --- /dev/null +++ b/test/integration/component/test_network_ipv6.py @@ -0,0 +1,935 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +""" BVT tests for IPv6 Network""" + +#Import Local Modules +from marvin.codes import FAILED +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.cloudstackAPI import (createGuestNetworkIpv6Prefix, + listGuestNetworkIpv6Prefixes, + deleteGuestNetworkIpv6Prefix, + listIpv6FirewallRules, + createIpv6FirewallRule, + deleteIpv6FirewallRule) +from marvin.lib.utils import (random_gen, + get_process_status, + get_host_credentials) +from marvin.lib.base import (Configurations, + Domain, + NetworkOffering, + Account, + PublicIpRange, + Network, + Router, + ServiceOffering, + VirtualMachine, + NIC, + Host) +from marvin.lib.common import (get_domain, + get_zone, + list_hosts, + get_test_template) +from marvin.sshClient import SshClient +from marvin.cloudstackException import CloudstackAPIException +from marvin.lib.decoratorGenerators import skipTestIf + +from nose.plugins.attrib import attr +from ipaddress import IPv6Network +from random import getrandbits, choice, randint +import time +import logging +import threading + +ipv6_offering_config_name = "ipv6.offering.enabled" +ULA_BASE = IPv6Network("fd00::/8") +PREFIX_OPTIONS = [i for i in range(48, 65, 4)] +FIREWALL_TABLE = "ip6_firewall" +FIREWALL_CHAINS = { + "Ingress": "fw_chain_ingress", + "Egress": "fw_chain_egress" +} +CIDR_IPV6_ANY = "::/0" +ICMPV6_TYPE = { + 1: "destination-unreachable", + 2: "packet-too-big", + 3: "time-exceeded", + 4: "parameter-problem", + 128: "echo-request", + 129: "echo-reply", + 130: "mld-listener-query", + 131: "mld-listener-report", + 132: "mld-listener-done", + 133: "nd-router-solicit", + 134: "nd-router-advert", + 135: "nd-neighbor-solicit", + 136: "nd-neighbor-advert", + 137: "nd-redirect", + 138: "router-renumbering", + 141: "ind-neighbor-solicit", + 142: "ind-neighbor-advert", + 143: "mld2-listener-report" +} +ICMPV6_CODE_TYPE = { + 0: "no-route", + 1: "admin-prohibited", + 3: "addr-unreachable", + 4: "port-unreachable", + 5: "policy-fail", + 6: "reject-route" +} +ICMPV6_TYPE_ANY = "{ destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-request, echo-reply, mld-listener-query, mld-listener-report, mld-listener-done, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, nd-redirect, router-renumbering }" +TCP_UDP_PORT_ANY = "{ 0-65535 }" +SLEEP_BEFORE_VR_CHANGES = 45 +PING_RETRIES = 5 +PING_SLEEP = 20 + +class TestIpv6Network(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + testClient = super(TestIpv6Network, cls).getClsTestClient() + cls.services = testClient.getParsedTestDataConfig() + cls.apiclient = testClient.getApiClient() + cls.dbclient = testClient.getDbConnection() + cls.test_ipv6_guestprefix = None + cls.initial_ipv6_offering_enabled = None + cls._cleanup = [] + cls.routerDetailsMap = {} + + cls.logger = logging.getLogger('TestIpv6Network') + + cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests()) + cls.services['mode'] = cls.zone.networktype + cls.ipv6NotSupported = False + + ipv6_guestprefix = cls.getGuestIpv6Prefix() + if ipv6_guestprefix == None: + cls.ipv6NotSupported = True + if cls.ipv6NotSupported == False: + ipv6_publiciprange = cls.getPublicIpv6Range() + if ipv6_publiciprange == None: + cls.ipv6NotSupported = True + + if cls.ipv6NotSupported == False: + cls.initial_ipv6_offering_enabled = Configurations.list( + cls.apiclient, + name=ipv6_offering_config_name)[0].value + Configurations.update(cls.apiclient, + ipv6_offering_config_name, + "true") + cls.domain = get_domain(cls.apiclient) + cls.account = Account.create( + cls.apiclient, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls._cleanup.append(cls.account) + cls.hypervisor = testClient.getHypervisorInfo() + cls.template = get_test_template( + cls.apiclient, + cls.zone.id, + cls.hypervisor) + else: + cls.debug("IPv6 is not supported, skipping tests!") + return + + @classmethod + def tearDownClass(cls): + if cls.initial_ipv6_offering_enabled != None: + Configurations.update(cls.apiclient, + ipv6_offering_config_name, + cls.initial_ipv6_offering_enabled) + try: + super(TestIpv6Network, cls).tearDownClass() + finally: + if cls.test_ipv6_guestprefix != None: + cmd = deleteGuestNetworkIpv6Prefix.deleteGuestNetworkIpv6PrefixCmd() + cmd.id = cls.test_ipv6_guestprefix.id + cls.apiclient.deleteGuestNetworkIpv6Prefix(cmd) + + @classmethod + def getGuestIpv6Prefix(cls): + cmd = listGuestNetworkIpv6Prefixes.listGuestNetworkIpv6PrefixesCmd() + cmd.zoneid = cls.zone.id + ipv6_prefixes_response = cls.apiclient.listGuestNetworkIpv6Prefixes(cmd) + if isinstance(ipv6_prefixes_response, list) == True and len(ipv6_prefixes_response) > 0: + return ipv6_prefixes_response[0] + ipv6_guestprefix_service = cls.services["guestip6prefix"] + cmd = createGuestNetworkIpv6Prefix.createGuestNetworkIpv6PrefixCmd() + cmd.zoneid = cls.zone.id + cmd.prefix = ipv6_guestprefix_service["prefix"] + ipv6_guestprefix = cls.apiclient.createGuestNetworkIpv6Prefix(cmd) + cls.test_ipv6_guestprefix = ipv6_guestprefix + return ipv6_guestprefix + + @classmethod + def getPublicIpv6Range(cls): + list_public_ip_range_response = PublicIpRange.list( + cls.apiclient, + zoneid=cls.zone.id + ) + ipv4_range_vlan = None + if isinstance(list_public_ip_range_response, list) == True and len(list_public_ip_range_response) > 0: + for ip_range in list_public_ip_range_response: + if ip_range.ip6cidr != None and ip_range.ip6gateway != None: + return ip_range + if ip_range.netmask != None and ip_range.gateway != None: + vlan = ip_range.vlan + if ipv4_range_vlan == None and vlan.startswith("vlan://"): + vlan = vlan.replace("vlan://", "") + if vlan == "untagged": + ipv4_range_vlan = None + else: + ipv4_range_vlan = int(vlan) + ipv6_publiciprange_service = cls.services["publicip6range"] + ipv6_publiciprange_service["zoneid"] = cls.zone.id + ipv6_publiciprange_service["vlan"] = ipv4_range_vlan + ipv6_publiciprange = PublicIpRange.create( + cls.apiclient, + ipv6_publiciprange_service + ) + cls._cleanup.append(ipv6_publiciprange) + return ipv6_publiciprange + + def setUp(self): + self.services = self.testClient.getParsedTestDataConfig() + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.thread = None + self.cleanup = [] + return + + def tearDown(self): + try: + if self.thread and self.thread.is_alive(): + self.thread.join(5*60) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + finally: + super(TestIpv6Network, self).tearDown() + return + + def getRandomIpv6Cidr(self): + prefix_length = choice(PREFIX_OPTIONS) + random_suffix = getrandbits(40) << (128-prefix_length) + base_address = ULA_BASE.network_address + random_suffix + return str(IPv6Network((base_address, prefix_length))) + + def createTinyServiceOffering(self): + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offerings"]["big"], + ) + self.cleanup.append(self.service_offering) + + def createNetworkOfferingInternal(self, is_redundant, is_ipv6, egressdefaultpolicy=True): + off_service = self.services["network_offering"] + if is_redundant: + off_service = self.services["nw_off_isolated_RVR"] + if is_ipv6: + off_service["internetprotocol"] = "dualstack" + if egressdefaultpolicy: + off_service["egress_policy"] = egressdefaultpolicy + network_offering = NetworkOffering.create( + self.apiclient, + off_service + ) + self.cleanup.append(network_offering) + network_offering.update(self.apiclient, state='Enabled') + return network_offering + + def createIpv4NetworkOffering(self, is_redundant=False): + self.network_offering = self.createNetworkOfferingInternal(is_redundant, False, False) + + def createIpv6NetworkOffering(self, is_redundant=False): + self.network_offering = self.createNetworkOfferingInternal(is_redundant, True, False) + + def createIpv6NetworkOfferingForUpdate(self, is_redundant=False): + self.network_offering_update = self.createNetworkOfferingInternal(is_redundant, True) + + + def deployNetwork(self): + self.services["network"]["networkoffering"] = self.network_offering.id + self.network = Network.create( + self.apiclient, + self.services["network"], + self.account.name, + self.account.domainid, + zoneid=self.zone.id + ) + self.cleanup.append(self.network) + + def deployNetworkVm(self): + if self.template == FAILED: + assert False, "get_test_template() failed to return template" + self.services["virtual_machine"]["zoneid"] = self.zone.id + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + networkids=self.network.id, + serviceofferingid=self.service_offering.id + ) + self.cleanup.append(self.virtual_machine) + + def checkIpv6NetworkBasic(self): + self.debug("Listing network: %s" % (self.network.name)) + ipv6_network = Network.list(self.apiclient,listall="true",id=self.network.id) + self.assertTrue( + isinstance(ipv6_network, list), + "Check listNetworks response returns a valid list" + ) + self.assertEqual( + len(ipv6_network), + 1, + "Network not found" + ) + ipv6_network = ipv6_network[0] + self.assertNotEqual(ipv6_network, + None, + "User is not able to retrieve network details %s" % self.network.id) + self.assertNotEqual(ipv6_network.ip6cidr, + None, + "IPv6 CIDR for network is empty") + self.assertNotEqual(ipv6_network.ip6gateway, + None, + "IPv6 gateway for network is empty") + self.assertNotEqual(ipv6_network.ip6routes, + None, + "IPv6 routes for network is empty") + self.network_ipv6_routes = ipv6_network.ip6routes + + def checkIpv6NetworkRoutersBasic(self): + self.debug("Listing routers for network: %s" % self.network.name) + self.routers = Router.list( + self.apiclient, + networkid=self.network.id, + listall=True + ) + self.assertTrue( + isinstance(self.routers, list), + "Check listRouters response returns a valid list" + ) + self.assertTrue( + len(self.routers) > 0, + "Router for the network isn't found" + ) + for router in self.routers: + self.assertFalse( + router.isredundantrouter == True and router.redundantstate == "FAULT", + "Router for the network is in FAULT state" + ) + nics = router.nic + for nic in nics: + if (nic.traffictype == 'Guest' and router.isredundantrouter == False) or nic.traffictype == 'Public': + self.assertNotEqual(nic.ip6address, + None, + "IPv6 address for router %s NIC is empty" % nic.traffictype) + self.assertNotEqual(nic.ip6cidr, + None, + "IPv6 CIDR for router %s NIC is empty" % nic.traffictype) + self.assertNotEqual(nic.ip6gateway, + None, + "IPv6 gateway for router %s NIC is empty" % nic.traffictype) + + + def getRouterProcessStatus(self, router, cmd): + if router.id not in self.routerDetailsMap or self.routerDetailsMap[router.id] is None: + connect_ip = self.apiclient.connection.mgtSvr + connect_user = self.apiclient.connection.user + connect_passwd = self.apiclient.connection.passwd + hypervisor = self.hypervisor + if self.hypervisor.lower() not in ('vmware', 'hyperv'): + hosts = Host.list( + self.apiclient, + zoneid=router.zoneid, + type='Routing', + state='Up', + id=router.hostid + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list host returns a valid list" + ) + host = hosts[0] + connect_ip = host.ipaddress + hypervisor = None + try: + connect_user, connect_passwd= get_host_credentials( + self.config, host.ipaddress) + except KeyError: + self.skipTest( + "Marvin configuration has no host credentials to\ + check router services") + details = {} + details['connect_ip'] = connect_ip + details['connect_user'] = connect_user + details['connect_passwd'] = connect_passwd + details['hypervisor'] = hypervisor + self.routerDetailsMap[router.id] = details + result = get_process_status( + self.routerDetailsMap[router.id]['connect_ip'], + 22, + self.routerDetailsMap[router.id]['connect_user'], + self.routerDetailsMap[router.id]['connect_passwd'], + router.linklocalip, + cmd, + hypervisor=self.routerDetailsMap[router.id]['hypervisor'] + ) + self.assertTrue(type(result) == list and len(result) > 0, + "%s on router %s returned invalid result" % (cmd, router.id)) + result = '\n'.join(result) + return result + + def getNetworkRouter(self, network, red_state="PRIMARY"): + routers = Router.list( + self.apiclient, + networkid=network.id, + listall=True + ) + self.assertTrue( + isinstance(routers, list) and len(routers) > 0, + "No routers found for network %s" % network.id + ) + if len(routers) == 1: + return routers[0] + for router in routers: + if router.redundantstate == red_state: + return router + + def getNetworkGateway(self, network): + ipv6_network = Network.list(self.apiclient,listall="true",id=network.id) + self.assertTrue( + isinstance(ipv6_network, list), + "Check listNetworks response returns a valid list" + ) + self.assertEqual( + len(ipv6_network), + 1, + "Network not found" + ) + ipv6_network = ipv6_network[0] + self.assertNotEqual(ipv6_network.ip6gateway, + None, + "IPv6 gateway for network is empty") + return ipv6_network.ip6gateway + + def getNetworkRoutes(self, network): + ipv6_network = Network.list(self.apiclient,listall="true",id=network.id) + self.assertTrue( + isinstance(ipv6_network, list), + "Check listNetworks response returns a valid list" + ) + self.assertEqual( + len(ipv6_network), + 1, + "Network not found" + ) + ipv6_network = ipv6_network[0] + self.assertNotEqual(ipv6_network.ip6routes, + None, + "IPv6 routes for network is empty") + return ipv6_network.ip6routes + + def isNetworkEgressDefaultPolicyAllow(self, network): + ipv6_network = Network.list(self.apiclient,listall="true",id=network.id) + if len(ipv6_network) == 1: + ipv6_network = ipv6_network[0] + return ipv6_network.egressdefaultpolicy + return False + + def checkRouterNicState(self, router, dev, state): + st = "state %s" % state + cmd = "ip link show %s | grep '%s'" % (dev, st) + res = self.getRouterProcessStatus(router, cmd) + self.assertTrue(type(res) == str and len(res) > 0 and st in res, + "%s failed on router %s" % (cmd, router.id)) + + def checkIpv6NetworkPrimaryRouter(self, router, network_ip6gateway): + self.checkRouterNicState(router, "eth0", "UP") + guest_gateway_check_cmd = "ip -6 address show %s | grep 'inet6 %s'" % ("eth0", network_ip6gateway) + res = self.getRouterProcessStatus(router, guest_gateway_check_cmd) + self.assertTrue(type(res) == str and len(res) > 0 and network_ip6gateway in res, + "%s failed on router %s" % (guest_gateway_check_cmd, router.id)) + self.assertFalse("dadfailed" in res, + "dadfailed for IPv6 guest gateway on router %s" % router.id) + self.checkRouterNicState(router, "eth2", "UP") + public_ipv6 = None + public_ipv6_gateway = None + nics = router.nic + for nic in nics: + if nic.traffictype == 'Public': + public_ipv6 = nic.ip6address + public_ipv6_gateway = nic.ip6gateway + break + self.assertNotEqual(public_ipv6, + None, + "IPv6 address for router Public NIC is empty") + public_ip_check_cmd = "ip -6 address show %s | grep 'inet6 %s'" % ("eth2", public_ipv6) + res = self.getRouterProcessStatus(router, public_ip_check_cmd) + self.assertTrue(type(res) == str and len(res) > 0 and public_ipv6 in res, + "%s failed on router %s" % (public_ip_check_cmd, router.id)) + self.assertFalse("dadfailed" in res, + "dadfailed for public IPv6 on router %s" % router.id) + self.assertNotEqual(public_ipv6_gateway, + None, + "IPv6 gateway for router Public NIC is empty") + default_route_check_cmd = "ip -6 route | grep 'default via %s'" % (public_ipv6_gateway) + res = self.getRouterProcessStatus(router, default_route_check_cmd) + self.assertTrue(type(res) == str and len(res) > 0 and public_ipv6_gateway in res, + "%s failed on router %s" % (default_route_check_cmd, router.id)) + + def checkIpv6NetworkBackupRouter(self, router, network_ip6gateway): + self.checkRouterNicState(router, "eth0", "UP") + guest_gateway_check_cmd = "ip -6 address show %s | grep 'inet6 %s'" % ("eth0", network_ip6gateway) + res = self.getRouterProcessStatus(router, guest_gateway_check_cmd) + self.assertFalse(type(res) == str and len(res) > 0 and network_ip6gateway in res, + "%s failed on router %s" % (guest_gateway_check_cmd, router.id)) + self.checkRouterNicState(router, "eth2", "DOWN") + + def checkIpv6NetworkRoutersInternal(self): + network_ip6gateway = self.getNetworkGateway(self.network) + for router in self.routers: + if router.state != "Running": + continue + if router.isredundantrouter == True and router.redundantstate == 'BACKUP': + self.checkIpv6NetworkBackupRouter(router, network_ip6gateway) + continue + self.checkIpv6NetworkPrimaryRouter(router, network_ip6gateway) + + + def checkIpv6NetworkVm(self): + self.debug("Listing NICS for VM %s in network: %s" % (self.virtual_machine.name, self.network.name)) + nics = NIC.list( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + networkid=self.network.id + ) + self.assertEqual( + len(nics), + 1, + "VM NIC for the network isn't found" + ) + nic = nics[0] + self.assertNotEqual(nic.ip6address, + None, + "IPv6 address for VM %s NIC is empty" % nic.traffictype) + self.virtual_machine_ipv6_address = nic.ip6address + self.assertNotEqual(nic.ip6cidr, + None, + "IPv6 CIDR for VM %s NIC is empty" % nic.traffictype) + self.assertNotEqual(nic.ip6gateway, + None, + "IPv6 gateway for VM %s NIC is empty" % nic.traffictype) + + def restartNetworkWithCleanup(self): + self.network.restart(self.apiclient, cleanup=True) + time.sleep(SLEEP_BEFORE_VR_CHANGES) + + def updateNetworkWithOffering(self): + self.network.update(self.apiclient, networkofferingid=self.network_offering_update.id) + time.sleep(SLEEP_BEFORE_VR_CHANGES) + + def createIpv6FirewallRuleInNetwork(self, network_id, traffic_type, source_cidr, dest_cidr, protocol, + start_port, end_port, icmp_type, icmp_code): + cmd = createIpv6FirewallRule.createIpv6FirewallRuleCmd() + cmd.networkid = network_id + cmd.traffictype = traffic_type + if source_cidr: + cmd.cidrlist = source_cidr + if dest_cidr: + cmd.destcidrlist = dest_cidr + if protocol: + cmd.protocol = protocol + if start_port: + cmd.startport = start_port + if end_port: + cmd.endport = end_port + if icmp_type is not None: + cmd.icmptype = icmp_type + if icmp_code is not None: + cmd.icmpcode = icmp_code + fw_rule = self.apiclient.createIpv6FirewallRule(cmd) + return fw_rule + + def deployRoutingTestResources(self): + self.routing_test_network_offering = self.createNetworkOfferingInternal(False, True) + self.services["network"]["networkoffering"] = self.routing_test_network_offering.id + self.routing_test_network = Network.create( + self.apiclient, + self.services["network"], + self.account.name, + self.account.domainid, + zoneid=self.zone.id + ) + self.cleanup.append(self.routing_test_network) + self.services["virtual_machine"]["zoneid"] = self.zone.id + self.routing_test_vm = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + networkids=[self.routing_test_network.id], + serviceofferingid=self.service_offering.id, + mode="advanced" + ) + self.cleanup.append(self.routing_test_vm) + + def prepareRoutingTestResourcesInBackground(self): + self.thread = threading.Thread(target=self.deployRoutingTestResources, args=()) + self.thread.daemon = True + self.thread.start() + + def checkIpv6NetworkRouting(self): + if not self.thread: + self.deployRoutingTestResources() + else: + self.thread.join(5*60) + self.assertFalse(not self.routing_test_network or not self.routing_test_vm, + "Routing resources failure") + + fw1 = self.createIpv6FirewallRuleInNetwork(self.routing_test_network.id, "Ingress", None, None, "icmp", + None, None, None, None) + fw2 = self.createIpv6FirewallRuleInNetwork(self.network.id, "Ingress", None, None, "icmp", + None, None, None, None) + + test_network_router = self.getNetworkRouter(self.routing_test_network) + routes = self.getNetworkRoutes(self.network) + self.logger.debug("Adding network routes in routing_test_network %s" % routes) + for route in routes: + add_route_cmd = "ip -6 route add %s via %s" % (route.subnet, route.gateway) + self.getRouterProcessStatus(test_network_router, add_route_cmd) + + network_router = self.getNetworkRouter(self.network) + routes = self.getNetworkRoutes(self.routing_test_network) + self.logger.debug("Adding routing_test_network routes in network %s" % routes) + for route in routes: + add_route_cmd = "ip -6 route add %s via %s" % (route.subnet, route.gateway) + self.getRouterProcessStatus(network_router, add_route_cmd) + + ping_cmd = "ping6 -c 4 %s" % self.virtual_machine_ipv6_address + count = 0 + while count < PING_RETRIES: + count = count + 1 + res = self.getRouterProcessStatus(test_network_router, ping_cmd) + if " 0% packet loss" in res: + break + time.sleep(PING_SLEEP) + self.assertTrue(" 0% packet loss" in res, + "Ping from router %s of network %s to VM %s of network %s is unsuccessful" % (test_network_router.id, self.routing_test_network.id, self.virtual_machine.id, self.network.id)) + + ssh = self.routing_test_vm.get_ssh_client(retries=5) + count = 0 + while count < PING_RETRIES: + count = count + 1 + res = ssh.execute(ping_cmd) + if type(res) == list and len(res) > 0 and " 0% packet loss" in '\n'.join(res): + break + time.sleep(PING_SLEEP) + self.assertTrue(type(res) == list and len(res) > 0, + "%s on VM %s returned invalid result" % (ping_cmd, self.routing_test_vm.id)) + self.logger.debug(res) + res = '\n'.join(res) + + self.assertTrue(" 0% packet loss" in res, + "Ping from VM %s of network %s to VM %s of network %s is unsuccessful" % (self.routing_test_vm.id, self.routing_test_network.id, self.virtual_machine.id, self.network.id)) + + cmd = deleteIpv6FirewallRule.deleteIpv6FirewallRuleCmd() + cmd.id = fw2.id + self.apiclient.deleteIpv6FirewallRule(cmd) + + def createAndVerifyIpv6FirewallRule(self, traffic_type, source_cidr, dest_cidr, protocol, + start_port, end_port, icmp_type, icmp_code, parsed_rule, delete=False): + self.logger.debug("createAndVerifyIpv6FirewallRule - %s" % parsed_rule) + fw_rule = self.createIpv6FirewallRuleInNetwork(self.network.id, traffic_type, source_cidr, dest_cidr, protocol, + start_port, end_port, icmp_type, icmp_code) + cmd = listIpv6FirewallRules.listIpv6FirewallRulesCmd() + cmd.id = fw_rule.id + rules = self.apiclient.listIpv6FirewallRules(cmd) + self.assertTrue( + isinstance(rules, list), + "Check listIpv6FirewallRules response returns a valid list" + ) + rule = rules[0] + self.assertEqual(rule.networkid, self.network.id, + "IPv6 firewall rule network ID mismatch %s, %s" % (rule.networkid, self.network.id)) + self.assertEqual(rule.traffictype, traffic_type, + "IPv6 firewall rule traffic type mismatch %s, %s" % (rule.traffictype, traffic_type)) + if source_cidr: + self.assertEqual(rule.cidrlist, source_cidr, + "IPv6 firewall rule source CIDR mismatch %s, %s" % (rule.cidrlist, source_cidr)) + if dest_cidr: + self.assertEqual(rule.destcidrlist, dest_cidr, + "IPv6 firewall rule destination CIDR mismatch %s, %s" % (rule.destcidrlist, dest_cidr)) + if protocol: + self.assertEqual(rule.protocol, protocol, + "IPv6 firewall rule protocol mismatch %s, %s" % (rule.protocol, protocol)) + if start_port: + self.assertEqual(rule.startport, start_port, + "IPv6 firewall rule start port mismatch %d, %d" % (rule.startport, start_port)) + if end_port: + self.assertEqual(rule.endport, end_port, + "IPv6 firewall rule end port mismatch %d, %d" % (rule.endport, end_port)) + if icmp_type is not None: + self.assertEqual(rule.icmptype, icmp_type, + "IPv6 firewall rule ICMP type mismatch %d, %d" % (rule.icmptype, icmp_type)) + if icmp_code is not None: + self.assertEqual(rule.icmpcode, icmp_code, + "IPv6 firewall rule ICMP code mismatch %d, %d" % (rule.icmpcode, icmp_code)) + routerCmd = "nft list chain ip6 %s %s" % (FIREWALL_TABLE, FIREWALL_CHAINS[traffic_type]) + res = self.getRouterProcessStatus(self.getNetworkRouter(self.network), routerCmd) + self.assertTrue(parsed_rule in res, + "Listing firewall rule with nft list chain failure for rule: %s" % parsed_rule) + if delete == True: + cmd = deleteIpv6FirewallRule.deleteIpv6FirewallRuleCmd() + cmd.id = fw_rule.id + self.apiclient.deleteIpv6FirewallRule(cmd) + res = self.getRouterProcessStatus(self.getNetworkRouter(self.network), routerCmd) + self.assertFalse(parsed_rule in res, + "Firewall rule present in nft list chain failure despite delete for rule: %s" % parsed_rule) + + def checkIpv6FirewallRule(self): + traffic_type = "Ingress" + + # Ingress - ip6 saddr SOURCE_CIDR ip6 daddr DEST_CIDR tcp dport { START_PORT-END_PORT } accept + source_cidr = self.getRandomIpv6Cidr() + dest_cidr = self.getRandomIpv6Cidr() + protocol = "tcp" + start_port = randint(3000, 5000) + end_port = start_port + randint(1, 8) + rule = "ip6 saddr %s ip6 daddr %s %s dport { %d-%d } accept" % (source_cidr, dest_cidr, protocol, start_port, end_port) + self.createAndVerifyIpv6FirewallRule(traffic_type, source_cidr, dest_cidr, protocol, + start_port, end_port, None, None, rule, True) + + # Ingress - ip6 daddr DEST_CIDR icmpv6 type TYPE code CODE accept + source_cidr = self.getRandomIpv6Cidr() + protocol = "icmp" + icmp_type = choice(list(ICMPV6_TYPE.keys())) + icmp_code = choice(list(ICMPV6_CODE_TYPE.keys())) + rule = "ip6 saddr %s ip6 daddr %s %sv6 type %s %sv6 code %s accept" % (source_cidr, CIDR_IPV6_ANY, protocol, ICMPV6_TYPE[icmp_type], protocol, ICMPV6_CODE_TYPE[icmp_code]) + self.createAndVerifyIpv6FirewallRule(traffic_type, source_cidr, None, protocol, + None, None, icmp_type, icmp_code, rule) + + action = "accept" + if self.isNetworkEgressDefaultPolicyAllow(self.network): + action = "drop" + traffic_type = "Egress" + + # Egress - ip6 saddr ::/0 ip6 daddr ::/0 udp dport { 0-65355 } ACTION + protocol = "udp" + rule = "ip6 saddr %s ip6 daddr %s %s dport %s %s" % (CIDR_IPV6_ANY, CIDR_IPV6_ANY, protocol, TCP_UDP_PORT_ANY, action) + self.createAndVerifyIpv6FirewallRule(traffic_type, None, None, protocol, + None, None, None, None, rule) + + # Egress - ip6 saddr ::/0 ip6 daddr ::/0 icmpv6 type ANY_TYPE ACTION + protocol = "icmp" + rule = "ip6 saddr %s ip6 daddr %s %sv6 type %s %s" % (CIDR_IPV6_ANY, CIDR_IPV6_ANY, protocol, ICMPV6_TYPE_ANY, action) + self.createAndVerifyIpv6FirewallRule(traffic_type, None, None, protocol, + None, None, None, None, rule) + + # Egress - ip6 saddr ::/0 ip6 daddr DEST_CIDR ACTION + protocol = "all" + dest_cidr = self.getRandomIpv6Cidr() + rule = "ip6 saddr %s ip6 daddr %s %s" % (CIDR_IPV6_ANY, CIDR_IPV6_ANY, action) + self.createAndVerifyIpv6FirewallRule(traffic_type, None, None, protocol, + None, None, None, None, rule) + + def checkNetworkVRRedundancy(self): + network_ip6gateway = self.getNetworkGateway(self.network) + primary_router = self.getNetworkRouter(self.network) + Router.stop( + self.apiclient, + id=primary_router.id + ) + time.sleep(SLEEP_BEFORE_VR_CHANGES) + new_primary_router = self.getNetworkRouter(self.network) + self.assertNotEqual(new_primary_router.id, primary_router.id, + "Original primary router ID: %s of network is still the primary router after stopping" % (primary_router.id)) + self.checkIpv6NetworkPrimaryRouter(new_primary_router, network_ip6gateway) + + def checkIpv6Network(self): + self.checkIpv6NetworkBasic() + self.checkIpv6NetworkRoutersBasic() + self.checkIpv6NetworkRoutersInternal() + + + @attr( + tags=[ + "advanced", + "basic", + "eip", + "sg", + "advancedns", + "smoke"], + required_hardware="false") + @skipTestIf("ipv6NotSupported") + def test_01_verify_ipv6_network(self): + """Test to verify IPv6 network + + # Validate the following: + # 1. Create IPv6 network, deploy VM + # 2. Verify network has required IPv6 details + # 3. List router for the network and verify it has required IPv6 details for Guest and Public NIC of the VR + # 4. SSH into VR(s) and verify correct details are present for its NICs + # 5. Verify VM in network has required IPv6 details + # 6. Restart network with cleanup and re-verify network details + # 7. Update network with a new offering and re-verify network details + # 8. Deploy another IPv6 network and check routing between two networks and their VM + # 9. Create IPv6 firewall rules and verify in VR if they get implemented + """ + + self.createIpv6NetworkOffering() + self.createIpv6NetworkOfferingForUpdate() + self.createTinyServiceOffering() + self.deployNetwork() + self.deployNetworkVm() + self.checkIpv6Network() + self.checkIpv6NetworkVm() + self.prepareRoutingTestResourcesInBackground() + self.restartNetworkWithCleanup() + self.checkIpv6Network() + self.updateNetworkWithOffering() + self.checkIpv6Network() + self.checkIpv6NetworkRouting() + self.checkIpv6FirewallRule() + + @attr( + tags=[ + "advanced", + "basic", + "eip", + "sg", + "advancedns", + "smoke"], + required_hardware="false") + @skipTestIf("ipv6NotSupported") + def test_02_verify_ipv6_network_redundant(self): + """Test to verify redundant IPv6 network + + # Validate the following: + # 1. Create IPv6 network, deploy VM + # 2. Verify network has required IPv6 details + # 3. List VRs for the network and verify it has required IPv6 details for Guest and Public NIC of the VR + # 4. SSH into VR(s) and verify correct details are present for its NICs + # 5. Verify VM in network has required IPv6 details + # 6. Restart network with cleanup and re-verify network details + # 7. Update network with a new offering and re-verify network details + # 8. Deploy another IPv6 network and check routing between two networks and their VM + # 9. Create IPv6 firewall rules and verify in VR if they get implemented + # 10. Stop primary router and verify internals in backup VR + """ + + self.createIpv6NetworkOffering(True) + self.createIpv6NetworkOfferingForUpdate(True) + self.createTinyServiceOffering() + self.deployNetwork() + self.deployNetworkVm() + self.checkIpv6Network() + self.checkIpv6NetworkVm() + self.prepareRoutingTestResourcesInBackground() + self.restartNetworkWithCleanup() + self.checkIpv6Network() + self.updateNetworkWithOffering() + self.checkIpv6Network() + self.checkIpv6NetworkRouting() + self.checkIpv6FirewallRule() + self.checkNetworkVRRedundancy() + + @attr( + tags=[ + "advanced", + "basic", + "eip", + "sg", + "advancedns", + "smoke"], + required_hardware="false") + @skipTestIf("ipv6NotSupported") + def test_03_verify_upgraded_ipv6_network(self): + """Test to verify IPv4 network upgraded to IPv6 network + + # Validate the following: + # 1. Create IPv4 network, deploy VM + # 2. Update network to a IPv6 offering + # 3. Verify network has required IPv6 details + # 4. List VRs for the network and verify it has required IPv6 details for Guest and Public NIC of the VR + # 5. SSH into VR(s) and verify correct details are present for its NICs + # 6. Verify VM in network has required IPv6 details + # 7. Restart network with cleanup and re-verify network details + # 8. Deploy another IPv6 network and check routing between two networks and their VM + # 9. Create IPv6 firewall rules and verify in VR if they get implemented + """ + + self.createIpv4NetworkOffering(False) + self.createIpv6NetworkOfferingForUpdate(False) + self.createTinyServiceOffering() + self.prepareRoutingTestResourcesInBackground() + self.deployNetwork() + self.deployNetworkVm() + self.updateNetworkWithOffering() + self.checkIpv6Network() + self.checkIpv6NetworkVm() + self.restartNetworkWithCleanup() + self.checkIpv6Network() + self.checkIpv6NetworkRouting() + self.checkIpv6FirewallRule() + + @attr( + tags=[ + "advanced", + "basic", + "eip", + "sg", + "advancedns", + "smoke"], + required_hardware="false") + @skipTestIf("ipv6NotSupported") + def test_04_verify_upgraded_ipv6_network_redundant(self): + """Test to verify redundant IPv4 network upgraded to redundant IPv6 network + + # Validate the following: + # 1. Create IPv4 network, deploy VM + # 2. Update network to a IPv6 offering + # 3. Verify network has required IPv6 details + # 4. List VRs for the network and verify it has required IPv6 details for Guest and Public NIC of the VR + # 5. SSH into VR(s) and verify correct details are present for its NICs + # 6. Verify VM in network has required IPv6 details + # 7. Restart network with cleanup and re-verify network details + # 8. Deploy another IPv6 network and check routing between two networks and their VM + # 9. Create IPv6 firewall rules and verify in VR if they get implemented + # 10. Stop primary router and verify internals in backup VR + """ + + self.createIpv4NetworkOffering(True) + self.createIpv6NetworkOfferingForUpdate(True) + self.createTinyServiceOffering() + self.prepareRoutingTestResourcesInBackground() + self.deployNetwork() + self.deployNetworkVm() + self.updateNetworkWithOffering() + self.checkIpv6Network() + self.checkIpv6NetworkVm() + self.restartNetworkWithCleanup() + self.checkIpv6Network() + self.checkIpv6NetworkRouting() + self.checkIpv6FirewallRule() + self.checkNetworkVRRedundancy() diff --git a/test/integration/component/test_vpc_ipv6.py b/test/integration/component/test_vpc_ipv6.py new file mode 100644 index 00000000000..fda46ea2378 --- /dev/null +++ b/test/integration/component/test_vpc_ipv6.py @@ -0,0 +1,1026 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +""" BVT tests for IPv6 VPC""" + +#Import Local Modules +from marvin.codes import FAILED +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.cloudstackAPI import (createGuestNetworkIpv6Prefix, + listGuestNetworkIpv6Prefixes, + deleteGuestNetworkIpv6Prefix) +from marvin.lib.utils import (isAlmostEqual, + random_gen, + get_process_status, + get_host_credentials) +from marvin.lib.base import (Configurations, + Domain, + NetworkOffering, + VpcOffering, + Account, + PublicIpRange, + Network, + VPC, + Router, + ServiceOffering, + VirtualMachine, + NIC, + Host, + NetworkACLList, + NetworkACL) +from marvin.lib.common import (get_domain, + get_zone, + get_test_template, + get_template) +from marvin.sshClient import SshClient +from marvin.cloudstackException import CloudstackAPIException +from marvin.lib.decoratorGenerators import skipTestIf + +from nose.plugins.attrib import attr +from ipaddress import IPv6Network +from random import getrandbits, choice, randint +import time +import logging +import threading + +ipv6_offering_config_name = "ipv6.offering.enabled" +ULA_BASE = IPv6Network("fd00::/8") +PREFIX_OPTIONS = [i for i in range(48, 65, 4)] +ACL_TABLE = "ip6_acl" +ACL_CHAINS_SUFFIX = { + "Ingress": "_ingress_policy", + "Egress": "_egress_policy" +} +CIDR_IPV6_ANY = "::/0" +ICMPV6_TYPE = { + 1: "destination-unreachable", + 2: "packet-too-big", + 3: "time-exceeded", + 4: "parameter-problem", + 128: "echo-request", + 129: "echo-reply", + 130: "mld-listener-query", + 131: "mld-listener-report", + 132: "mld-listener-done", + 133: "nd-router-solicit", + 134: "nd-router-advert", + 135: "nd-neighbor-solicit", + 136: "nd-neighbor-advert", + 137: "nd-redirect", + 138: "router-renumbering", + 141: "ind-neighbor-solicit", + 142: "ind-neighbor-advert", + 143: "mld2-listener-report" +} +ICMPV6_CODE_TYPE = { + 0: "no-route", + 1: "admin-prohibited", + 3: "addr-unreachable", + 4: "port-unreachable", + 5: "policy-fail", + 6: "reject-route" +} +ICMPV6_TYPE_ANY = "{ destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-request, echo-reply, mld-listener-query, mld-listener-report, mld-listener-done, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, nd-redirect, router-renumbering }" +TCP_UDP_PORT_ANY = "{ 0-65535 }" +VPC_ROUTER_PUBLIC_NIC = "eth1" +VPC_ROUTER_GUEST_NIC = "eth2" +VPC_DATA = { + "cidr": "10.1.0.0/22", + "tier1_gateway": "10.1.1.1", + "tier2_gateway": "10.1.2.1", + "tier_netmask": "255.255.255.0" +} +ROUTE_TEST_VPC_DATA = { + "cidr": "10.2.0.0/22", + "tier1_gateway": "10.2.1.1", + "tier_netmask": "255.255.255.0" +} +SLEEP_BEFORE_VR_CHANGES = 90 +PING_RETRIES = 5 +PING_SLEEP = 20 + + +class TestIpv6Vpc(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + testClient = super(TestIpv6Vpc, cls).getClsTestClient() + cls.services = testClient.getParsedTestDataConfig() + cls.apiclient = testClient.getApiClient() + cls.dbclient = testClient.getDbConnection() + cls.test_ipv6_guestprefix = None + cls.initial_ipv6_offering_enabled = None + cls._cleanup = [] + cls.routerDetailsMap = {} + cls.vpcAllowAllAclDetailsMap = {} + + cls.logger = logging.getLogger('TestIpv6Vpc') + + cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests()) + cls.services['mode'] = cls.zone.networktype + cls.ipv6NotSupported = False + + ipv6_guestprefix = cls.getGuestIpv6Prefix() + if ipv6_guestprefix == None: + cls.ipv6NotSupported = True + if cls.ipv6NotSupported == False: + ipv6_publiciprange = cls.getPublicIpv6Range() + if ipv6_publiciprange == None: + cls.ipv6NotSupported = True + + if cls.ipv6NotSupported == False: + cls.initial_ipv6_offering_enabled = Configurations.list( + cls.apiclient, + name=ipv6_offering_config_name)[0].value + Configurations.update(cls.apiclient, + ipv6_offering_config_name, + "true") + cls.domain = get_domain(cls.apiclient) + cls.account = Account.create( + cls.apiclient, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls._cleanup.append(cls.account) + cls.hypervisor = testClient.getHypervisorInfo() + cls.template = get_test_template( + cls.apiclient, + cls.zone.id, + cls.hypervisor) + else: + cls.debug("IPv6 is not supported, skipping tests!") + return + + @classmethod + def tearDownClass(cls): + if cls.initial_ipv6_offering_enabled != None: + Configurations.update(cls.apiclient, + ipv6_offering_config_name, + cls.initial_ipv6_offering_enabled) + try: + super(TestIpv6Vpc, cls).tearDownClass() + finally: + if cls.test_ipv6_guestprefix != None: + cmd = deleteGuestNetworkIpv6Prefix.deleteGuestNetworkIpv6PrefixCmd() + cmd.id = cls.test_ipv6_guestprefix.id + cls.apiclient.deleteGuestNetworkIpv6Prefix(cmd) + + @classmethod + def getGuestIpv6Prefix(cls): + cmd = listGuestNetworkIpv6Prefixes.listGuestNetworkIpv6PrefixesCmd() + cmd.zoneid = cls.zone.id + ipv6_prefixes_response = cls.apiclient.listGuestNetworkIpv6Prefixes(cmd) + if isinstance(ipv6_prefixes_response, list) == True and len(ipv6_prefixes_response) > 0: + return ipv6_prefixes_response[0] + ipv6_guestprefix_service = cls.services["guestip6prefix"] + cmd = createGuestNetworkIpv6Prefix.createGuestNetworkIpv6PrefixCmd() + cmd.zoneid = cls.zone.id + cmd.prefix = ipv6_guestprefix_service["prefix"] + ipv6_guestprefix = cls.apiclient.createGuestNetworkIpv6Prefix(cmd) + cls.test_ipv6_guestprefix = ipv6_guestprefix + return ipv6_guestprefix + + @classmethod + def getPublicIpv6Range(cls): + list_public_ip_range_response = PublicIpRange.list( + cls.apiclient, + zoneid=cls.zone.id + ) + ipv4_range_vlan = None + if isinstance(list_public_ip_range_response, list) == True and len(list_public_ip_range_response) > 0: + for ip_range in list_public_ip_range_response: + if ip_range.ip6cidr != None and ip_range.ip6gateway != None: + return ip_range + if ip_range.netmask != None and ip_range.gateway != None: + vlan = ip_range.vlan + if ipv4_range_vlan == None and vlan.startswith("vlan://"): + vlan = vlan.replace("vlan://", "") + if vlan == "untagged": + ipv4_range_vlan = None + else: + ipv4_range_vlan = int(vlan) + ipv6_publiciprange_service = cls.services["publicip6range"] + ipv6_publiciprange_service["zoneid"] = cls.zone.id + ipv6_publiciprange_service["vlan"] = ipv4_range_vlan + ipv6_publiciprange = PublicIpRange.create( + cls.apiclient, + ipv6_publiciprange_service + ) + cls._cleanup.append(ipv6_publiciprange) + return ipv6_publiciprange + + def setUp(self): + self.services = self.testClient.getParsedTestDataConfig() + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.thread = None + self.cleanup = [] + return + + def tearDown(self): + try: + if self.thread and self.thread.is_alive(): + self.thread.join(5*60) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + finally: + super(TestIpv6Vpc, self).tearDown() + return + + def getRandomIpv6Cidr(self): + prefix_length = choice(PREFIX_OPTIONS) + random_suffix = getrandbits(40) << (128-prefix_length) + base_address = ULA_BASE.network_address + random_suffix + return str(IPv6Network((base_address, prefix_length))) + + def createTinyServiceOffering(self): + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offerings"]["big"], + ) + self.cleanup.append(self.service_offering) + + def createVpcOfferingInternal(self, is_redundant, is_ipv6): + off_service = self.services["vpc_offering"] + if is_redundant: + off_service["serviceCapabilityList"] = { + "SourceNat": { + "RedundantRouter": 'true' + }, + } + if is_ipv6: + off_service["internetprotocol"] = "dualstack" + vpc_offering = VpcOffering.create( + self.apiclient, + off_service + ) + self.cleanup.append(vpc_offering) + vpc_offering.update(self.apiclient, state='Enabled') + return vpc_offering + + def createIpv4VpcOffering(self, is_redundant=False): + self.vpc_offering = self.createVpcOfferingInternal(is_redundant, False) + + def createIpv6VpcOffering(self, is_redundant=False): + self.vpc_offering = self.createVpcOfferingInternal(is_redundant, True) + + def createIpv6VpcOfferingForUpdate(self, is_redundant=False): + self.vpc_offering_update = self.createVpcOfferingInternal(is_redundant, True) + + def createNetworkTierOfferingInternal(self, is_ipv6, remove_lb=True): + off_service = self.services["nw_offering_isolated_vpc"] + if not remove_lb: # Remove Lb service + if "serviceProviderList" in off_service and "Lb" in off_service["serviceProviderList"].keys(): + providers = off_service["serviceProviderList"] + providers.pop("Lb") + off_service["serviceProviderList"] = providers + if "supportedservices" in off_service and "Lb" in off_service["supportedservices"]: + supportedServices = off_service["supportedservices"].split(",") + supportedServices.remove("Lb") + off_service["supportedservices"] = ",".join(supportedServices) + if is_ipv6: + off_service["internetprotocol"] = "dualstack" + network_offering = NetworkOffering.create( + self.apiclient, + off_service, + conservemode=False + ) + self.cleanup.append(network_offering) + network_offering.update(self.apiclient, state='Enabled') + return network_offering + + def createIpv4NetworkTierOffering(self): + self.network_offering = self.createNetworkTierOfferingInternal(False) + + def createIpv6NetworkTierOffering(self, remove_lb=True): + self.network_offering = self.createNetworkTierOfferingInternal(True) + + def createIpv6NetworkTierOfferingForUpdate(self): + self.network_offering_update = self.createNetworkTierOfferingInternal(True) + + def deployAllowAllVpcInternal(self, cidr): + service = self.services["vpc"] + service["cidr"] = cidr + vpc = VPC.create( + self.apiclient, + self.services["vpc"], + vpcofferingid=self.vpc_offering.id, + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid + ) + self.cleanup.append(vpc) + acl = NetworkACLList.create( + self.apiclient, + services={}, + name="allowall", + description="allowall", + vpcid=vpc.id + ) + rule ={ + "protocol": "all", + "traffictype": "ingress", + } + NetworkACL.create(self.apiclient, + services=rule, + aclid=acl.id + ) + rule["traffictype"] = "egress" + NetworkACL.create(self.apiclient, + services=rule, + aclid=acl.id + ) + self.vpcAllowAllAclDetailsMap[vpc.id] = acl.id + return vpc + + def deployVpc(self): + self.vpc = self.deployAllowAllVpcInternal(VPC_DATA["cidr"]) + + def deployNetworkTierInternal(self, network_offering_id, vpc_id, tier_gateway, tier_netmask, acl_id=None, tier_name=None): + if not acl_id and vpc_id in self.vpcAllowAllAclDetailsMap: + acl_id = self.vpcAllowAllAclDetailsMap[vpc_id] + service = self.services["ntwk"] + if tier_name: + service["name"] = tier_name + service["displaytext"] = "vpc-%s" % tier_name + network = Network.create( + self.apiclient, + service, + self.account.name, + self.account.domainid, + networkofferingid=network_offering_id, + vpcid=vpc_id, + zoneid=self.zone.id, + gateway=tier_gateway, + netmask=tier_netmask, + aclid=acl_id + ) + self.cleanup.append(network) + return network + + def deployNetworkTier(self): + self.network = self.deployNetworkTierInternal( + self.network_offering.id, + self.vpc.id, + VPC_DATA["tier1_gateway"], + VPC_DATA["tier_netmask"] + ) + + def deployNetworkTierVmInternal(self, network): + if self.template == FAILED: + assert False, "get_test_template() failed to return template" + self.services["virtual_machine"]["zoneid"] = self.zone.id + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + networkids=network, + serviceofferingid=self.service_offering.id + ) + self.cleanup.append(virtual_machine) + return virtual_machine + + def deployNetworkTierVm(self): + self.virtual_machine = self.deployNetworkTierVmInternal(self.network.id) + + def checkIpv6Vpc(self): + self.debug("Listing VPC: %s" % (self.vpc.name)) + ipv6_vpc = VPC.list(self.apiclient,listall="true",id=self.vpc.id) + self.assertTrue( + isinstance(ipv6_vpc, list), + "Check listVpcs response returns a valid list" + ) + self.assertEqual( + len(ipv6_vpc), + 1, + "Network not found" + ) + ipv6_vpc = ipv6_vpc[0] + self.assertNotEqual(ipv6_vpc.ip6routes, + None, + "IPv6 routes for network is empty") + + def checkIpv6NetworkTierBasic(self): + self.debug("Listing network: %s" % (self.network.name)) + ipv6_network = Network.list(self.apiclient,listall="true",id=self.network.id) + self.assertTrue( + isinstance(ipv6_network, list), + "Check listNetworks response returns a valid list" + ) + self.assertEqual( + len(ipv6_network), + 1, + "Network not found" + ) + ipv6_network = ipv6_network[0] + self.assertNotEqual(ipv6_network, + None, + "User is not able to retrieve network details %s" % self.network.id) + self.assertNotEqual(ipv6_network.ip6cidr, + None, + "IPv6 CIDR for network is empty") + self.assertNotEqual(ipv6_network.ip6gateway, + None, + "IPv6 gateway for network is empty") + self.assertNotEqual(ipv6_network.ip6routes, + None, + "IPv6 routes for network is empty") + + def checkIpv6VpcRoutersBasic(self): + self.debug("Listing routers for VPC: %s" % self.vpc.name) + self.routers = Router.list( + self.apiclient, + vpcid=self.vpc.id, + listall=True + ) + self.assertTrue( + isinstance(self.routers, list), + "Check listRouters response returns a valid list" + ) + self.assertTrue( + len(self.routers) > 0, + "Router for the network isn't found" + ) + for router in self.routers: + self.assertFalse( + router.isredundantrouter == True and router.redundantstate == "FAULT", + "Router for the network is in FAULT state" + ) + nics = router.nic + for nic in nics: + if (nic.traffictype == 'Guest' and router.isredundantrouter == False) or nic.traffictype == 'Public': + self.assertNotEqual(nic.ip6address, + None, + "IPv6 address for router %s NIC is empty" % nic.traffictype) + self.assertNotEqual(nic.ip6cidr, + None, + "IPv6 CIDR for router %s NIC is empty" % nic.traffictype) + self.assertNotEqual(nic.ip6gateway, + None, + "IPv6 gateway for router %s NIC is empty" % nic.traffictype) + + + def getRouterProcessStatus(self, router, cmd): + if router.id not in self.routerDetailsMap or self.routerDetailsMap[router.id] is None: + connect_ip = self.apiclient.connection.mgtSvr + connect_user = self.apiclient.connection.user + connect_passwd = self.apiclient.connection.passwd + hypervisor = self.hypervisor + if self.hypervisor.lower() not in ('vmware', 'hyperv'): + hosts = Host.list( + self.apiclient, + zoneid=router.zoneid, + type='Routing', + state='Up', + id=router.hostid + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list host returns a valid list" + ) + host = hosts[0] + connect_ip = host.ipaddress + hypervisor = None + try: + connect_user, connect_passwd= get_host_credentials( + self.config, host.ipaddress) + except KeyError: + self.skipTest( + "Marvin configuration has no host credentials to\ + check router services") + details = {} + details['connect_ip'] = connect_ip + details['connect_user'] = connect_user + details['connect_passwd'] = connect_passwd + details['hypervisor'] = hypervisor + self.routerDetailsMap[router.id] = details + result = get_process_status( + self.routerDetailsMap[router.id]['connect_ip'], + 22, + self.routerDetailsMap[router.id]['connect_user'], + self.routerDetailsMap[router.id]['connect_passwd'], + router.linklocalip, + cmd, + hypervisor=self.routerDetailsMap[router.id]['hypervisor'] + ) + self.assertTrue(type(result) == list and len(result) > 0, + "%s on router %s returned invalid result" % (cmd, router.id)) + result = '\n'.join(result) + return result + + def getVpcRouter(self, vpc, red_state="PRIMARY"): + routers = Router.list( + self.apiclient, + vpcid=vpc.id, + listall=True + ) + self.assertTrue( + isinstance(routers, list) and len(routers) > 0, + "No routers found for VPC %s" % vpc.id + ) + if len(routers) == 1: + return routers[0] + for router in routers: + if router.redundantstate == red_state: + return router + + def getNetworkGateway(self, network): + ipv6_network = Network.list(self.apiclient,listall="true",id=network.id) + self.assertTrue( + isinstance(ipv6_network, list), + "Check listNetworks response returns a valid list" + ) + self.assertEqual( + len(ipv6_network), + 1, + "Network not found" + ) + ipv6_network = ipv6_network[0] + self.assertNotEqual(ipv6_network.ip6gateway, + None, + "IPv6 gateway for network is empty") + return ipv6_network.ip6gateway + + def getNetworkRoutes(self, network): + ipv6_network = Network.list(self.apiclient,listall="true",id=network.id) + self.assertTrue( + isinstance(ipv6_network, list), + "Check listNetworks response returns a valid list" + ) + self.assertEqual( + len(ipv6_network), + 1, + "Network not found" + ) + ipv6_network = ipv6_network[0] + self.assertNotEqual(ipv6_network.ip6routes, + None, + "IPv6 routes for network is empty") + return ipv6_network.ip6routes + + def isNetworkEgressDefaultPolicyAllow(self, network): + ipv6_network = Network.list(self.apiclient,listall="true",id=network.id) + if len(ipv6_network) == 1: + ipv6_network = ipv6_network[0] + return ipv6_network.egressdefaultpolicy + return False + + def checkRouterNicState(self, router, dev, state): + st = "state %s" % state + cmd = "ip link show %s | grep '%s'" % (dev, st) + res = self.getRouterProcessStatus(router, cmd) + self.assertTrue(type(res) == str and len(res) > 0 and st in res, + "%s failed on router %s" % (cmd, router.id)) + + def checkIpv6VpcPrimaryRouter(self, router, network_ip6gateway): + self.checkRouterNicState(router, VPC_ROUTER_GUEST_NIC, "UP") + guest_gateway_check_cmd = "ip -6 address show %s | grep 'inet6 %s'" % (VPC_ROUTER_GUEST_NIC, network_ip6gateway) + res = self.getRouterProcessStatus(router, guest_gateway_check_cmd) + self.assertTrue(type(res) == str and len(res) > 0 and network_ip6gateway in res, + "%s failed on router %s" % (guest_gateway_check_cmd, router.id)) + self.assertFalse("dadfailed" in res, + "dadfailed for IPv6 guest gateway on router %s" % router.id) + self.checkRouterNicState(router, VPC_ROUTER_PUBLIC_NIC, "UP") + public_ipv6 = None + public_ipv6_gateway = None + nics = router.nic + for nic in nics: + if nic.traffictype == 'Public': + public_ipv6 = nic.ip6address + public_ipv6_gateway = nic.ip6gateway + break + self.assertNotEqual(public_ipv6, + None, + "IPv6 address for router Public NIC is empty") + public_ip_check_cmd = "ip -6 address show %s | grep 'inet6 %s'" % (VPC_ROUTER_PUBLIC_NIC, public_ipv6) + res = self.getRouterProcessStatus(router, public_ip_check_cmd) + self.assertTrue(type(res) == str and len(res) > 0 and public_ipv6 in res, + "%s failed on router %s" % (public_ip_check_cmd, router.id)) + self.assertFalse("dadfailed" in res, + "dadfailed for public IPv6 on router %s" % router.id) + self.assertNotEqual(public_ipv6_gateway, + None, + "IPv6 gateway for router Public NIC is empty") + default_route_check_cmd = "ip -6 route | grep 'default via %s'" % (public_ipv6_gateway) + res = self.getRouterProcessStatus(router, default_route_check_cmd) + self.assertTrue(type(res) == str and len(res) > 0 and public_ipv6_gateway in res, + "%s failed on router %s" % (default_route_check_cmd, router.id)) + + def checkIpv6VpcBackupRouter(self, router, network_ip6gateway): + self.checkRouterNicState(router, VPC_ROUTER_GUEST_NIC, "UP") + guest_gateway_check_cmd = "ip -6 address show %s | grep 'inet6 %s'" % ("eth0", network_ip6gateway) + res = self.getRouterProcessStatus(router, guest_gateway_check_cmd) + self.assertFalse(type(res) == str and len(res) > 0 and network_ip6gateway in res, + "%s failed on router %s" % (guest_gateway_check_cmd, router.id)) + self.checkRouterNicState(router, VPC_ROUTER_PUBLIC_NIC, "DOWN") + + def checkIpv6VpcRoutersInternal(self): + network_ip6gateway = self.getNetworkGateway(self.network) + for router in self.routers: + if router.state != "Running": + continue + if router.isredundantrouter == True and router.redundantstate == 'BACKUP': + self.checkIpv6VpcBackupRouter(router, network_ip6gateway) + continue + self.checkIpv6VpcPrimaryRouter(router, network_ip6gateway) + + + def checkIpv6NetworkTierVm(self): + self.debug("Listing NICS for VM %s in network tier: %s" % (self.virtual_machine.name, self.network.name)) + nics = NIC.list( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + networkid=self.network.id + ) + self.assertEqual( + len(nics), + 1, + "VM NIC for the network tier isn't found" + ) + nic = nics[0] + self.assertNotEqual(nic.ip6address, + None, + "IPv6 address for VM %s NIC is empty" % nic.traffictype) + self.virtual_machine_ipv6_address = nic.ip6address + self.assertNotEqual(nic.ip6cidr, + None, + "IPv6 CIDR for VM %s NIC is empty" % nic.traffictype) + self.assertNotEqual(nic.ip6gateway, + None, + "IPv6 gateway for VM %s NIC is empty" % nic.traffictype) + + def restartVpcWithCleanup(self): + self.vpc.restart(self.apiclient, cleanup=True) + time.sleep(SLEEP_BEFORE_VR_CHANGES) + + def updateNetworkTierWithOffering(self): + self.network.update(self.apiclient, networkofferingid=self.network_offering_update.id) + time.sleep(SLEEP_BEFORE_VR_CHANGES) + + def deployRoutingTestResources(self): + self.routing_test_vpc = self.deployAllowAllVpcInternal(ROUTE_TEST_VPC_DATA["cidr"]) + self.routing_test_network_offering = self.createNetworkTierOfferingInternal(True) + self.routing_test_network = self.deployNetworkTierInternal( + self.routing_test_network_offering.id, + self.routing_test_vpc.id, + ROUTE_TEST_VPC_DATA["tier1_gateway"], + ROUTE_TEST_VPC_DATA["tier_netmask"] + ) + self.services["virtual_machine"]["zoneid"] = self.zone.id + self.routing_test_vm = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + networkids=[self.routing_test_network.id], + serviceofferingid=self.service_offering.id, + mode="advanced", + vpcid=self.routing_test_vpc.id + ) + self.cleanup.append(self.routing_test_vm) + + def prepareRoutingTestResourcesInBackground(self): + self.thread = threading.Thread(target=self.deployRoutingTestResources, args=()) + self.thread.daemon = True + self.thread.start() + + def checkVpcRouting(self): + if not self.thread: + self.deployRoutingTestResources() + else: + self.thread.join(5*60) + self.assertFalse(not self.routing_test_vpc or not self.routing_test_network or not self.routing_test_vm, + "Routing resources failure") + + test_vpc_router = self.getVpcRouter(self.routing_test_vpc) + routes = self.getNetworkRoutes(self.network) + self.logger.debug("Adding vpc routes in routing_test_vpc %s" % routes) + for route in routes: + add_route_cmd = "ip -6 route add %s via %s" % (route.subnet, route.gateway) + self.getRouterProcessStatus(test_vpc_router, add_route_cmd) + + vpc_router = self.getVpcRouter(self.vpc) + routes = self.getNetworkRoutes(self.routing_test_network) + self.logger.debug("Adding routing_test_vpc routes in vpc %s" % routes) + for route in routes: + add_route_cmd = "ip -6 route add %s via %s" % (route.subnet, route.gateway) + self.getRouterProcessStatus(vpc_router, add_route_cmd) + + ping_cmd = "ping6 -c 4 %s" % self.virtual_machine_ipv6_address + count = 0 + while count < PING_RETRIES: + count = count + 1 + res = self.getRouterProcessStatus(test_vpc_router, ping_cmd) + if " 0% packet loss" in res: + break + time.sleep(PING_SLEEP) + self.assertTrue(" 0% packet loss" in res, + "Ping from router %s of VPC %s to VM %s of VPC %s is unsuccessful" % (test_vpc_router.id, self.routing_test_vpc.id, self.virtual_machine.id, self.vpc.id)) + + ssh = self.routing_test_vm.get_ssh_client(retries=5) + count = 0 + while count < PING_RETRIES: + count = count + 1 + res = ssh.execute(ping_cmd) + if type(res) == list and len(res) > 0 and " 0% packet loss" in '\n'.join(res): + break + time.sleep(PING_SLEEP) + self.assertTrue(type(res) == list and len(res) > 0, + "%s on VM %s returned invalid result" % (ping_cmd, self.routing_test_vm.id)) + self.logger.debug(res) + res = '\n'.join(res) + self.assertTrue(" 0% packet loss" in res, + "Ping from VM %s of VPC %s to VM %s of VPC %s is unsuccessful" % (self.routing_test_vm.id, self.routing_test_vpc.id, self.virtual_machine.id, self.vpc.id)) + + def createNetworkAclRule(self, rule, aclid): + return NetworkACL.create(self.apiclient, + services=rule, + aclid=aclid + ) + + def verifyAclRulesInRouter(self, nic, rules, router): + for rule in rules: + acl_chain = nic + ACL_CHAINS_SUFFIX[rule["traffictype"]] + routerCmd = "nft list chain ip6 %s %s" % (ACL_TABLE, acl_chain) + res = self.getRouterProcessStatus(router, routerCmd) + self.assertTrue(rule["parsedrule"] in res, + "Listing firewall rule with nft list chain failure for rule: %s" % rule["parsedrule"]) + + def checkIpv6AclRule(self): + router = self.getVpcRouter(self.vpc) + + tier1_acl = NetworkACLList.create( + self.apiclient, + services={}, + name="tier1_acl", + description="tier1_acl", + vpcid=self.vpc.id + ) + rules = [] + # Ingress - ip6 saddr SOURCE_CIDR tcp dport { START_PORT-END_PORT } accept + rule = {} + rule["traffictype"] = "Ingress" + rule["cidrlist"] = self.getRandomIpv6Cidr() + rule["protocol"] = "tcp" + rule["startport"] = randint(3000, 5000) + rule["endport"] = rule["startport"] + randint(1, 8) + parsedrule = "ip6 saddr %s %s dport { %d-%d } accept" % (rule["cidrlist"], rule["protocol"], rule["startport"], rule["endport"]) + rules.append({"traffictype": rule["traffictype"], "parsedrule": parsedrule}) + self.createNetworkAclRule(rule, tier1_acl.id) + # Egress - ip6 daddr DEST_CIDR icmpv6 type TYPE code CODE accept + rule = {} + rule["traffictype"] = "Egress" + rule["cidrlist"] = self.getRandomIpv6Cidr() + rule["protocol"] = "icmp" + rule["icmptype"] = choice(list(ICMPV6_TYPE.keys())) + rule["icmpcode"] = choice(list(ICMPV6_CODE_TYPE.keys())) + parsedrule = "ip6 daddr %s %sv6 type %s %sv6 code %s accept" % (rule["cidrlist"], rule["protocol"], ICMPV6_TYPE[rule["icmptype"]], rule["protocol"], ICMPV6_CODE_TYPE[rule["icmpcode"]]) + rules.append({"traffictype": rule["traffictype"], "parsedrule": parsedrule}) + self.createNetworkAclRule(rule, tier1_acl.id) + + self.network.replaceACLList(self.apiclient, tier1_acl.id) + + self.verifyAclRulesInRouter("eth2", rules, router) + + + tier2_acl = NetworkACLList.create( + self.apiclient, + services={}, + name="tier2_acl", + description="tier2_acl", + vpcid=self.vpc.id + ) + rules = [] + # Ingress - ip6 saddr ::/0 udp dport { 0-65355 } ACTION + rule = {} + rule["traffictype"] = "Ingress" + rule["cidrlist"] = CIDR_IPV6_ANY + rule["protocol"] = "udp" + parsedrule = "ip6 saddr %s %s dport %s accept" % (rule["cidrlist"], rule["protocol"], TCP_UDP_PORT_ANY) + rules.append({"traffictype": rule["traffictype"], "parsedrule": parsedrule}) + self.createNetworkAclRule(rule, tier2_acl.id) + # Egress - ip6 daddr DEST_CIDR icmpv6 type TYPE code CODE accept + rule = {} + rule["traffictype"] = "Egress" + rule["protocol"] = "all" + parsedrule = "ip6 daddr %s accept" % (CIDR_IPV6_ANY) + rules.append({"traffictype": rule["traffictype"], "parsedrule": parsedrule}) + self.createNetworkAclRule(rule, tier2_acl.id) + + self.network_offering_tier2 = self.createNetworkTierOfferingInternal(True, False) + self.tier2_network = self.deployNetworkTierInternal( + self.network_offering_tier2.id, + self.vpc.id, + VPC_DATA["tier2_gateway"], + VPC_DATA["tier_netmask"], + tier2_acl.id, + "tier2" + ) + self.tier2_vm = self.deployNetworkTierVmInternal(self.tier2_network.id) + + self.verifyAclRulesInRouter("eth3", rules, router) + + def checkVpcVRRedundancy(self): + network_ip6gateway = self.getNetworkGateway(self.network) + primary_router = self.getVpcRouter(self.vpc) + Router.stop( + self.apiclient, + id=primary_router.id + ) + time.sleep(self.services["sleep"]/2) + new_primary_router = self.getVpcRouter(self.vpc) + self.assertNotEqual(new_primary_router.id, primary_router.id, + "Original primary router ID: %s of VPC is still the primary router after stopping" % (primary_router.id)) + self.checkIpv6VpcPrimaryRouter(new_primary_router, network_ip6gateway) + + def checkIpv6VpcNetworking(self, check_vm=False): + self.checkIpv6Vpc() + self.checkIpv6NetworkTierBasic() + self.checkIpv6VpcRoutersBasic() + self.checkIpv6VpcRoutersInternal() + if check_vm: + self.checkIpv6NetworkTierVm() + + @attr( + tags=[ + "advanced", + "basic", + "eip", + "sg", + "advancedns", + "smoke"], + required_hardware="false") + @skipTestIf("ipv6NotSupported") + def test_01_verify_ipv6_vpc(self): + """Test to verify IPv6 VPC + + # Validate the following: + # 1. Create IPv6 VPC, add tiers, deploy VM + # 2. Verify VPC, tier has required IPv6 details + # 3. List router for the VPC and verify it has required IPv6 details for Guest and Public NIC of the VR + # 4. SSH into VR(s) and verify correct details are present for its NICs + # 5. Verify VM in network tier has required IPv6 details + # 6. Restart VPC with cleanup and re-verify VPC networking + # 7. Update network tier with a new offering and re-verify VPC networking + # 8. Deploy another IPv6 VPC with tier and check routing between two VPC and their VM + # 9. Create IPv6 ACL rules in two different VPC tiers and verify in VR if they get implemented correctly + """ + + self.createIpv6VpcOffering() + self.deployVpc() + self.createIpv6NetworkTierOffering() + self.createIpv6NetworkTierOfferingForUpdate() + self.createTinyServiceOffering() + self.deployNetworkTier() + self.deployNetworkTierVm() + self.checkIpv6VpcNetworking(True) + self.prepareRoutingTestResourcesInBackground() + self.restartVpcWithCleanup() + self.checkIpv6VpcNetworking() + self.updateNetworkTierWithOffering() + self.checkIpv6VpcNetworking() + self.checkVpcRouting() + self.checkIpv6AclRule() + + @attr( + tags=[ + "advanced", + "basic", + "eip", + "sg", + "advancedns", + "smoke"], + required_hardware="false") + @skipTestIf("ipv6NotSupported") + def test_02_verify_ipv6_vpc_redundant(self): + """Test to verify redundant IPv6 VPC + + # Validate the following: + # 1. Create redundant IPv6 VPC, add tiers, deploy VM + # 2. Verify VPC, tier has required IPv6 details + # 3. List router for the VPC and verify it has required IPv6 details for Guest and Public NIC of the VR + # 4. SSH into VR(s) and verify correct details are present for its NICs + # 5. Verify VM in network tier has required IPv6 details + # 6. Restart VPC with cleanup and re-verify VPC networking + # 7. Update network tier with a new offering and re-verify VPC networking + # 8. Deploy another IPv6 VPC with tier and check routing between two VPC and their VM + # 9. Create IPv6 ACL rules in two different VPC tiers and verify in VR if they get implemented correctly + # 10. Stop primary router and verify internals in backup VR + """ + + self.createIpv6VpcOffering(True) + self.deployVpc() + self.createIpv6NetworkTierOffering() + self.createIpv6NetworkTierOfferingForUpdate() + self.createTinyServiceOffering() + self.deployNetworkTier() + self.deployNetworkTierVm() + self.checkIpv6VpcNetworking(True) + self.prepareRoutingTestResourcesInBackground() + self.restartVpcWithCleanup() + self.checkIpv6VpcNetworking() + self.updateNetworkTierWithOffering() + self.checkIpv6VpcNetworking() + self.checkVpcRouting() + self.checkIpv6AclRule() + self.checkVpcVRRedundancy() + + @attr( + tags=[ + "advanced", + "basic", + "eip", + "sg", + "advancedns", + "smoke"], + required_hardware="false") + @skipTestIf("ipv6NotSupported") + def test_03_verify_upgraded_ipv6_vpc(self): + """Test to verify IPv4 VPC tier upgraded to IPv6 VPC tier + + # Validate the following: + # 1. Create IPv4 VPC, add tiers, deploy VM + # 2. Update VPC tier to IPv6 offering + # 3. Verify VPC, tier has required IPv6 details + # 4. List router for the VPC and verify it has required IPv6 details for Guest and Public NIC of the VR + # 5. SSH into VR(s) and verify correct details are present for its NICs + # 6. Verify VM in network tier has required IPv6 details + # 7. Restart VPC with cleanup and re-verify VPC networking + # 8. Deploy another IPv6 VPC with tier and check routing between two VPC and their VM + # 9. Create IPv6 ACL rules in two different VPC tiers and verify in VR if they get implemented correctly + """ + + self.createIpv6VpcOffering() + self.deployVpc() + self.prepareRoutingTestResourcesInBackground() + self.createIpv4NetworkTierOffering() + self.createIpv6NetworkTierOfferingForUpdate() + self.createTinyServiceOffering() + self.deployNetworkTier() + self.deployNetworkTierVm() + self.updateNetworkTierWithOffering() + self.checkIpv6VpcNetworking(True) + self.restartVpcWithCleanup() + self.checkIpv6VpcNetworking() + self.checkVpcRouting() + self.checkIpv6AclRule() + + @attr( + tags=[ + "advanced", + "basic", + "eip", + "sg", + "advancedns", + "smoke"], + required_hardware="false") + @skipTestIf("ipv6NotSupported") + def test_04_verify_upgraded_ipv6_vpc_redundant(self): + """Test to verify redunadnt IPv4 VPC tier upgraded to IPv6 VPC tier + + # Validate the following: + # 1. Create redundant IPv4 VPC, add tiers, deploy VM + # 2. Update VPC tier to IPv6 offering + # 3. Verify VPC, tier has required IPv6 details + # 4. List router for the VPC and verify it has required IPv6 details for Guest and Public NIC of the VR + # 5. SSH into VR(s) and verify correct details are present for its NICs + # 6. Verify VM in network tier has required IPv6 details + # 7. Restart VPC with cleanup and re-verify VPC networking + # 8. Deploy another IPv6 VPC with tier and check routing between two VPC and their VM + # 9. Create IPv6 ACL rules in two different VPC tiers and verify in VR if they get implemented correctly + # 10. Stop primary router and verify internals in backup VR + """ + + self.createIpv6VpcOffering(True) + self.deployVpc() + self.prepareRoutingTestResourcesInBackground() + self.createIpv4NetworkTierOffering() + self.createIpv6NetworkTierOfferingForUpdate() + self.createTinyServiceOffering() + self.deployNetworkTier() + self.deployNetworkTierVm() + self.updateNetworkTierWithOffering() + self.checkIpv6VpcNetworking(True) + self.restartVpcWithCleanup() + self.checkIpv6VpcNetworking() + self.checkVpcRouting() + self.checkIpv6AclRule() + self.checkVpcVRRedundancy() diff --git a/test/integration/smoke/test_ipv6_infra.py b/test/integration/smoke/test_ipv6_infra.py new file mode 100644 index 00000000000..30f57ee6526 --- /dev/null +++ b/test/integration/smoke/test_ipv6_infra.py @@ -0,0 +1,488 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +""" BVT tests for IPv6 infra operations""" + +#Import Local Modules +from marvin.codes import FAILED +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.cloudstackAPI import (createGuestNetworkIpv6Prefix, + listGuestNetworkIpv6Prefixes, + deleteGuestNetworkIpv6Prefix) +from marvin.lib.utils import (cleanup_resources) +from marvin.lib.base import (Configurations, + NetworkOffering, + VpcOffering, + PublicIpRange) +from marvin.lib.common import (get_zone) +from marvin.cloudstackException import CloudstackAPIException + +from nose.plugins.attrib import attr +import logging + +ipv6_offering_config_name = "ipv6.offering.enabled" + +class TestCreateIpv6NetworkVpcOffering(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + testClient = super(TestCreateIpv6NetworkVpcOffering, cls).getClsTestClient() + cls.apiclient = testClient.getApiClient() + cls.services = testClient.getParsedTestDataConfig() + cls.initial_ipv6_offering_enabled = Configurations.list( + cls.apiclient, + name=ipv6_offering_config_name)[0].value + cls._cleanup = [] + return + + @classmethod + def tearDownClass(cls): + if cls.initial_ipv6_offering_enabled != None: + Configurations.update(cls.apiclient, + ipv6_offering_config_name, + cls.initial_ipv6_offering_enabled) + super(TestCreateIpv6NetworkVpcOffering, cls).tearDownClass() + + def setUp(self): + self.services = self.testClient.getParsedTestDataConfig() + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @attr( + tags=[ + "advanced", + "basic", + "eip", + "sg", + "advancedns", + "smoke"], + required_hardware="false") + def test_01_create_ipv6_network_offering(self): + """Test to create network offering + + # Validate the following: + # 1. createNetworkOffering should return valid info for new offering + # 2. The Cloud Database contains the valid information + """ + Configurations.update(self.apiclient, + ipv6_offering_config_name, + "true") + ipv6_service = self.services["network_offering"] + ipv6_service["internetprotocol"] = "dualstack" + network_offering = NetworkOffering.create( + self.apiclient, + ipv6_service + ) + self.cleanup.append(network_offering) + + self.debug("Created Network offering with ID: %s" % network_offering.id) + + list_network_off_response = NetworkOffering.list(self.apiclient, + id=network_offering.id) + self.assertEqual( + isinstance(list_network_off_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_network_off_response), + 0, + "Check Network offering is created" + ) + network_off_response = list_network_off_response[0] + + self.assertEqual( + network_off_response.id, + network_offering.id, + "Check server id in listNetworkOfferings" + ) + self.assertEqual( + network_off_response.internetprotocol.lower(), + ipv6_service["internetprotocol"].lower(), + "Check internetprotocol in listNetworkOfferings" + ) + return + + @attr( + tags=[ + "advanced", + "basic", + "eip", + "sg", + "advancedns", + "smoke"], + required_hardware="false") + def test_02_create_ipv6_network_offering_fail(self): + """Test to create network offering + + # Validate the following: + # 1. createNetworkOffering should fail + """ + Configurations.update(self.apiclient, + ipv6_offering_config_name, + "false") + ipv6_service = self.services["network_offering"] + ipv6_service["internetprotocol"] = "dualstack" + try: + network_offering = NetworkOffering.create( + self.apiclient, + ipv6_service + ) + self.cleanup.append(network_offering) + self.fail("Network offering created despite global setting - %s set to false" % ipv6_offering_config_name) + except CloudstackAPIException as e: + self.debug("Network offering creation failed as expected %s " % e) + return + + @attr( + tags=[ + "advanced", + "basic", + "eip", + "sg", + "advancedns", + "smoke"], + required_hardware="false") + def test_03_create_ipv6_vpc_offering(self): + """Test to create network offering + + # Validate the following: + # 1. createVpcOffering should return valid info for new offering + # 2. The Cloud Database contains the valid information + """ + Configurations.update(self.apiclient, + ipv6_offering_config_name, + "true") + ipv6_service = self.services["vpc_offering"] + ipv6_service["internetprotocol"] = "dualstack" + vpc_offering = VpcOffering.create( + self.apiclient, + ipv6_service + ) + self.cleanup.append(vpc_offering) + + self.debug("Created VPC offering with ID: %s" % vpc_offering.id) + + list_vpc_off_response = VpcOffering.list(self.apiclient, + id=vpc_offering.id) + self.assertEqual( + isinstance(list_vpc_off_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_vpc_off_response), + 0, + "Check VPC offering is created" + ) + vpc_off_response = list_vpc_off_response[0] + self.assertEqual( + vpc_off_response.id, + vpc_offering.id, + "Check server id in listVpcOfferings" + ) + self.assertEqual( + vpc_off_response.internetprotocol.lower(), + ipv6_service["internetprotocol"].lower(), + "Check internetprotocol in listVpcOfferings" + ) + return + + @attr( + tags=[ + "advanced", + "basic", + "eip", + "sg", + "advancedns", + "smoke"], + required_hardware="false") + def test_04_create_ipv6_vpc_offering_fail(self): + """Test to create VPC offering failure + + # Validate the following: + # 1. createVpcOffering should fail + """ + Configurations.update(self.apiclient, + ipv6_offering_config_name, + "false") + ipv6_service = self.services["vpc_offering"] + ipv6_service["internetprotocol"] = "dualstack" + try: + vpc_offering = VpcOffering.create( + self.apiclient, + ipv6_service + ) + self.cleanup.append(vpc_offering) + self.fail("VPC offering created despite global setting - %s set to false" % ipv6_offering_config_name) + except CloudstackAPIException as e: + self.debug("VPC offering creation failed as expected %s " % e) + return + +class TestIpv6PublicIpRange(cloudstackTestCase): + + def setUp(self): + self.services = self.testClient.getParsedTestDataConfig() + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + testClient = super(TestIpv6PublicIpRange, cls).getClsTestClient() + cls.apiclient = testClient.getApiClient() + cls.services = testClient.getParsedTestDataConfig() + cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests()) + cls._cleanup = [] + return + + @classmethod + def tearDownClass(cls): + super(TestIpv6PublicIpRange, cls).tearDownClass() + + @attr( + tags=[ + "advanced", + "basic", + "eip", + "sg", + "advancedns", + "smoke"], + required_hardware="false") + def test_01_create_ipv6_public_ip_range(self): + """Test to add IPv6 public IP range + + # Validate the following: + # 1. createVlanIpRange should return valid info for new public range + # 2. The Cloud Database contains the valid information + """ + ipv6_publiciprange_service = self.services["publicip6range"] + ipv6_publiciprange_service["zoneid"] = self.zone.id + ipv6_publiciprange = PublicIpRange.create( + self.apiclient, + ipv6_publiciprange_service + ) + self.cleanup.append(ipv6_publiciprange) + + self.debug("Created IPv6 public IP range with ID: %s" % ipv6_publiciprange.vlan.id) + ipv6_publiciprange = ipv6_publiciprange.vlan + + public_ip_ranges = PublicIpRange.list( + self.apiclient, + id=ipv6_publiciprange.id + ) + self.assertEqual( + isinstance(public_ip_ranges, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(public_ip_ranges), + 0, + "Check public IP range is created" + ) + public_ip_range = public_ip_ranges[0] + + self.assertEqual( + public_ip_range.id, + ipv6_publiciprange.id, + "Check server id" + ) + self.assertEqual( + public_ip_range.ip6cidr, + ipv6_publiciprange_service["ip6cidr"], + "Check ip6cidr for IPv6 public IP range" + ) + return + + @attr( + tags=[ + "advanced", + "basic", + "eip", + "sg", + "advancedns", + "smoke"], + required_hardware="false") + def test_02_create_ipv6_public_ip_range_fail(self): + """Test to add IPv6 public IP range failure + + # Validate the following: + # 1. createVlanIpRange should return valid info for new public range + # 2. The Cloud Database contains the valid information + """ + ipv6_publiciprange_service = self.services["publicip6range"] + cidr = ipv6_publiciprange_service["ip6cidr"] + x = cidr.split("/") + x[1] = "72" + cidr = "/".join(x) + ipv6_publiciprange_service["ip6cidr"] = cidr + ipv6_publiciprange_service["zoneid"] = self.zone.id + try: + ipv6_publiciprange = PublicIpRange.create( + self.apiclient, + ipv6_publiciprange_service + ) + except Exception as e: + self.debug("IPv6 public range creation failed as expected %s " % e) + ipv6_publiciprange = None + if ipv6_publiciprange != None: + self.debug("Created IPv6 public range with ID: %s. Deleting it before failure" % ipv6_publiciprange.id) + self.cleanup.append(ipv6_publiciprange) + self.fail("IPv6 guest prefix created despite CIDR size greater than 64") + return + +class TestIpv6GuestPrefix(cloudstackTestCase): + + def setUp(self): + self.services = self.testClient.getParsedTestDataConfig() + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + testClient = super(TestIpv6GuestPrefix, cls).getClsTestClient() + cls.apiclient = testClient.getApiClient() + cls.services = testClient.getParsedTestDataConfig() + cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests()) + cls._cleanup = [] + return + + @classmethod + def tearDownClass(cls): + super(TestIpv6GuestPrefix, cls).tearDownClass() + + @attr( + tags=[ + "advanced", + "basic", + "eip", + "sg", + "advancedns", + "smoke"], + required_hardware="false") + def test_01_create_ipv6_guest_prefix(self): + """Test to add IPv6 guest prefix + + # Validate the following: + # 1. createGuestNetworkIpv6Prefix should return valid info for new IPv6 prefix + # 2. The Cloud Database contains the valid information + """ + ipv6_guestprefix_service = self.services["guestip6prefix"] + cmd = createGuestNetworkIpv6Prefix.createGuestNetworkIpv6PrefixCmd() + cmd.zoneid = self.zone.id + cmd.prefix = ipv6_guestprefix_service["prefix"] + ipv6_guestprefix = self.apiclient.createGuestNetworkIpv6Prefix(cmd) + + self.debug("Created IPv6 guest prefix with ID: %s" % ipv6_guestprefix.id) + + cmd = listGuestNetworkIpv6Prefixes.listGuestNetworkIpv6PrefixesCmd() + cmd.id = ipv6_guestprefix.id + ipv6_guestprefixes = self.apiclient.listGuestNetworkIpv6Prefixes(cmd) + self.assertEqual( + isinstance(ipv6_guestprefixes, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(ipv6_guestprefixes), + 0, + "Check guest IPv6 prefix is created" + ) + ipv6_guestprefix_response = ipv6_guestprefixes[0] + + self.assertEqual( + ipv6_guestprefix.id, + ipv6_guestprefix_response.id, + "Check server id" + ) + self.assertEqual( + ipv6_guestprefix_response.prefix, + ipv6_guestprefix_service["prefix"], + "Check prefix for IPv6" + ) + + cmd = deleteGuestNetworkIpv6Prefix.deleteGuestNetworkIpv6PrefixCmd() + cmd.id = ipv6_guestprefix.id + self.apiclient.deleteGuestNetworkIpv6Prefix(cmd) + return + + @attr( + tags=[ + "advanced", + "basic", + "eip", + "sg", + "advancedns", + "smoke"], + required_hardware="false") + def test_02_create_ipv6_guest_prefix_fail(self): + """Test to add IPv6 guest prefix failure + + # Validate the following: + # 1. createGuestNetworkIpv6Prefix should fail + """ + ipv6_guestprefix_service = self.services["guestip6prefix"] + cmd = createGuestNetworkIpv6Prefix.createGuestNetworkIpv6PrefixCmd() + cmd.zoneid = self.zone.id + prefix = ipv6_guestprefix_service["prefix"] + x = prefix.split("/") + x[1] = "72" + prefix = "/".join(x) + cmd.prefix = prefix + try: + ipv6_guestprefix = self.apiclient.createGuestNetworkIpv6Prefix(cmd) + except Exception as e: + self.debug("IPv6 guest prefix creation failed as expected %s " % e) + ipv6_guestprefix = None + if ipv6_guestprefix != None: + self.debug("Created IPv6 guest prefix with ID: %s. Deleting it before failure" % ipv6_guestprefix.id) + cmd = deleteGuestNetworkIpv6Prefix.deleteGuestNetworkIpv6PrefixCmd() + cmd.id = ipv6_guestprefix.id + self.apiclient.deleteGuestNetworkIpv6Prefix(cmd) + self.fail("IPv6 guest prefix created despite CIDR size greater than 64") diff --git a/test/integration/smoke/test_network_ipv6.py b/test/integration/smoke/test_network_ipv6.py index 51ce21a6a43..385ff636b16 100644 --- a/test/integration/smoke/test_network_ipv6.py +++ b/test/integration/smoke/test_network_ipv6.py @@ -14,7 +14,7 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -""" BVT tests for Network offerings""" +""" BVT test for IPv6 Network""" #Import Local Modules from marvin.codes import FAILED @@ -25,15 +25,12 @@ from marvin.cloudstackAPI import (createGuestNetworkIpv6Prefix, listIpv6FirewallRules, createIpv6FirewallRule, deleteIpv6FirewallRule) -from marvin.lib.utils import (isAlmostEqual, - cleanup_resources, - random_gen, +from marvin.lib.utils import (random_gen, get_process_status, get_host_credentials) from marvin.lib.base import (Configurations, Domain, NetworkOffering, - VpcOffering, Account, PublicIpRange, Network, @@ -45,8 +42,7 @@ from marvin.lib.base import (Configurations, from marvin.lib.common import (get_domain, get_zone, list_hosts, - get_test_template, - get_template) + get_test_template) from marvin.sshClient import SshClient from marvin.cloudstackException import CloudstackAPIException from marvin.lib.decoratorGenerators import skipTestIf @@ -56,6 +52,7 @@ from ipaddress import IPv6Network from random import getrandbits, choice, randint import time import logging +import threading ipv6_offering_config_name = "ipv6.offering.enabled" ULA_BASE = IPv6Network("fd00::/8") @@ -96,459 +93,9 @@ ICMPV6_CODE_TYPE = { } ICMPV6_TYPE_ANY = "{ destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-request, echo-reply, mld-listener-query, mld-listener-report, mld-listener-done, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, nd-redirect, router-renumbering }" TCP_UDP_PORT_ANY = "{ 0-65535 }" - -class TestCreateIpv6NetworkVpcOffering(cloudstackTestCase): - - @classmethod - def setUpClass(cls): - testClient = super(TestCreateIpv6NetworkVpcOffering, cls).getClsTestClient() - cls.apiclient = testClient.getApiClient() - cls.services = testClient.getParsedTestDataConfig() - cls.initial_ipv6_offering_enabled = Configurations.list( - cls.apiclient, - name=ipv6_offering_config_name)[0].value - cls._cleanup = [] - return - - @classmethod - def tearDownClass(cls): - if cls.initial_ipv6_offering_enabled != None: - Configurations.update(cls.apiclient, - ipv6_offering_config_name, - cls.initial_ipv6_offering_enabled) - super(TestCreateIpv6NetworkVpcOffering, cls).tearDownClass() - - def setUp(self): - self.services = self.testClient.getParsedTestDataConfig() - self.apiclient = self.testClient.getApiClient() - self.dbclient = self.testClient.getDbConnection() - self.cleanup = [] - return - - def tearDown(self): - try: - #Clean up, terminate the created templates - cleanup_resources(self.apiclient, self.cleanup) - - except Exception as e: - raise Exception("Warning: Exception during cleanup : %s" % e) - return - - @attr( - tags=[ - "advanced", - "basic", - "eip", - "sg", - "advancedns", - "smoke"], - required_hardware="false") - def test_01_create_ipv6_network_offering(self): - """Test to create network offering - - # Validate the following: - # 1. createNetworkOffering should return valid info for new offering - # 2. The Cloud Database contains the valid information - """ - Configurations.update(self.apiclient, - ipv6_offering_config_name, - "true") - ipv6_service = self.services["network_offering"] - ipv6_service["internetprotocol"] = "dualstack" - network_offering = NetworkOffering.create( - self.apiclient, - ipv6_service - ) - self.cleanup.append(network_offering) - - self.debug("Created Network offering with ID: %s" % network_offering.id) - - list_network_off_response = NetworkOffering.list(self.apiclient, - id=network_offering.id) - self.assertEqual( - isinstance(list_network_off_response, list), - True, - "Check list response returns a valid list" - ) - self.assertNotEqual( - len(list_network_off_response), - 0, - "Check Network offering is created" - ) - network_off_response = list_network_off_response[0] - - self.assertEqual( - network_off_response.id, - network_offering.id, - "Check server id in listNetworkOfferings" - ) - self.assertEqual( - network_off_response.internetprotocol.lower(), - ipv6_service["internetprotocol"].lower(), - "Check internetprotocol in listNetworkOfferings" - ) - return - - @attr( - tags=[ - "advanced", - "basic", - "eip", - "sg", - "advancedns", - "smoke"], - required_hardware="false") - def test_02_create_ipv6_network_offering_fail(self): - """Test to create network offering - - # Validate the following: - # 1. createNetworkOffering should fail - """ - Configurations.update(self.apiclient, - ipv6_offering_config_name, - "false") - ipv6_service = self.services["network_offering"] - ipv6_service["internetprotocol"] = "dualstack" - try: - network_offering = NetworkOffering.create( - self.apiclient, - ipv6_service - ) - self.cleanup.append(network_offering) - self.fail("Network offering created despite global setting - %s set to false" % ipv6_offering_config_name) - except CloudstackAPIException as e: - self.debug("Network offering creation failed as expected %s " % e) - return - - @attr( - tags=[ - "advanced", - "basic", - "eip", - "sg", - "advancedns", - "smoke"], - required_hardware="false") - def test_03_create_ipv6_vpc_offering(self): - """Test to create network offering - - # Validate the following: - # 1. createVpcOffering should return valid info for new offering - # 2. The Cloud Database contains the valid information - """ - Configurations.update(self.apiclient, - ipv6_offering_config_name, - "true") - ipv6_service = self.services["vpc_offering"] - ipv6_service["internetprotocol"] = "dualstack" - vpc_offering = VpcOffering.create( - self.apiclient, - ipv6_service - ) - self.cleanup.append(vpc_offering) - - self.debug("Created VPC offering with ID: %s" % vpc_offering.id) - - list_vpc_off_response = VpcOffering.list(self.apiclient, - id=vpc_offering.id) - self.assertEqual( - isinstance(list_vpc_off_response, list), - True, - "Check list response returns a valid list" - ) - self.assertNotEqual( - len(list_vpc_off_response), - 0, - "Check VPC offering is created" - ) - vpc_off_response = list_vpc_off_response[0] - self.assertEqual( - vpc_off_response.id, - vpc_offering.id, - "Check server id in listVpcOfferings" - ) - self.assertEqual( - vpc_off_response.internetprotocol.lower(), - ipv6_service["internetprotocol"].lower(), - "Check internetprotocol in listVpcOfferings" - ) - return - - @attr( - tags=[ - "advanced", - "basic", - "eip", - "sg", - "advancedns", - "smoke"], - required_hardware="false") - def test_04_create_ipv6_vpc_offering_fail(self): - """Test to create VPC offering failure - - # Validate the following: - # 1. createVpcOffering should fail - """ - Configurations.update(self.apiclient, - ipv6_offering_config_name, - "false") - ipv6_service = self.services["vpc_offering"] - ipv6_service["internetprotocol"] = "dualstack" - try: - vpc_offering = VpcOffering.create( - self.apiclient, - ipv6_service - ) - self.cleanup.append(vpc_offering) - self.fail("VPC offering created despite global setting - %s set to false" % ipv6_offering_config_name) - except CloudstackAPIException as e: - self.debug("VPC offering creation failed as expected %s " % e) - return - -class TestIpv6PublicIpRange(cloudstackTestCase): - - def setUp(self): - self.services = self.testClient.getParsedTestDataConfig() - self.apiclient = self.testClient.getApiClient() - self.dbclient = self.testClient.getDbConnection() - self.cleanup = [] - return - - def tearDown(self): - try: - #Clean up, terminate the created templates - cleanup_resources(self.apiclient, self.cleanup) - - except Exception as e: - raise Exception("Warning: Exception during cleanup : %s" % e) - return - - @classmethod - def setUpClass(cls): - testClient = super(TestIpv6PublicIpRange, cls).getClsTestClient() - cls.apiclient = testClient.getApiClient() - cls.services = testClient.getParsedTestDataConfig() - cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests()) - cls._cleanup = [] - return - - @classmethod - def tearDownClass(cls): - super(TestIpv6PublicIpRange, cls).tearDownClass() - - @attr( - tags=[ - "advanced", - "basic", - "eip", - "sg", - "advancedns", - "smoke"], - required_hardware="false") - def test_01_create_ipv6_public_ip_range(self): - """Test to add IPv6 public IP range - - # Validate the following: - # 1. createVlanIpRange should return valid info for new public range - # 2. The Cloud Database contains the valid information - """ - ipv6_publiciprange_service = self.services["publicip6range"] - ipv6_publiciprange_service["zoneid"] = self.zone.id - ipv6_publiciprange = PublicIpRange.create( - self.apiclient, - ipv6_publiciprange_service - ) - self.cleanup.append(ipv6_publiciprange) - - self.debug("Created IPv6 public IP range with ID: %s" % ipv6_publiciprange.vlan.id) - ipv6_publiciprange = ipv6_publiciprange.vlan - - public_ip_ranges = PublicIpRange.list( - self.apiclient, - id=ipv6_publiciprange.id - ) - self.assertEqual( - isinstance(public_ip_ranges, list), - True, - "Check list response returns a valid list" - ) - self.assertNotEqual( - len(public_ip_ranges), - 0, - "Check public IP range is created" - ) - public_ip_range = public_ip_ranges[0] - - self.assertEqual( - public_ip_range.id, - ipv6_publiciprange.id, - "Check server id" - ) - self.assertEqual( - public_ip_range.ip6cidr, - ipv6_publiciprange_service["ip6cidr"], - "Check ip6cidr for IPv6 public IP range" - ) - return - - @attr( - tags=[ - "advanced", - "basic", - "eip", - "sg", - "advancedns", - "smoke"], - required_hardware="false") - def test_02_create_ipv6_public_ip_range_fail(self): - """Test to add IPv6 public IP range failure - - # Validate the following: - # 1. createVlanIpRange should return valid info for new public range - # 2. The Cloud Database contains the valid information - """ - ipv6_publiciprange_service = self.services["publicip6range"] - cidr = ipv6_publiciprange_service["ip6cidr"] - x = cidr.split("/") - x[1] = "72" - cidr = "/".join(x) - ipv6_publiciprange_service["ip6cidr"] = cidr - ipv6_publiciprange_service["zoneid"] = self.zone.id - try: - ipv6_publiciprange = PublicIpRange.create( - self.apiclient, - ipv6_publiciprange_service - ) - except Exception as e: - self.debug("IPv6 public range creation failed as expected %s " % e) - ipv6_publiciprange = None - if ipv6_publiciprange != None: - self.debug("Created IPv6 public range with ID: %s. Deleting it before failure" % ipv6_publiciprange.id) - self.cleanup.append(ipv6_publiciprange) - self.fail("IPv6 guest prefix created despite CIDR size greater than 64") - return - -class TestIpv6GuestPrefix(cloudstackTestCase): - - def setUp(self): - self.services = self.testClient.getParsedTestDataConfig() - self.apiclient = self.testClient.getApiClient() - self.dbclient = self.testClient.getDbConnection() - self.cleanup = [] - return - - def tearDown(self): - try: - #Clean up, terminate the created templates - cleanup_resources(self.apiclient, self.cleanup) - - except Exception as e: - raise Exception("Warning: Exception during cleanup : %s" % e) - return - - @classmethod - def setUpClass(cls): - testClient = super(TestIpv6GuestPrefix, cls).getClsTestClient() - cls.apiclient = testClient.getApiClient() - cls.services = testClient.getParsedTestDataConfig() - cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests()) - cls._cleanup = [] - return - - @classmethod - def tearDownClass(cls): - super(TestIpv6GuestPrefix, cls).tearDownClass() - - @attr( - tags=[ - "advanced", - "basic", - "eip", - "sg", - "advancedns", - "smoke"], - required_hardware="false") - def test_01_create_ipv6_guest_prefix(self): - """Test to add IPv6 guest prefix - - # Validate the following: - # 1. createGuestNetworkIpv6Prefix should return valid info for new IPv6 prefix - # 2. The Cloud Database contains the valid information - """ - ipv6_guestprefix_service = self.services["guestip6prefix"] - cmd = createGuestNetworkIpv6Prefix.createGuestNetworkIpv6PrefixCmd() - cmd.zoneid = self.zone.id - cmd.prefix = ipv6_guestprefix_service["prefix"] - ipv6_guestprefix = self.apiclient.createGuestNetworkIpv6Prefix(cmd) - - self.debug("Created IPv6 guest prefix with ID: %s" % ipv6_guestprefix.id) - - cmd = listGuestNetworkIpv6Prefixes.listGuestNetworkIpv6PrefixesCmd() - cmd.id = ipv6_guestprefix.id - ipv6_guestprefixes = self.apiclient.listGuestNetworkIpv6Prefixes(cmd) - self.assertEqual( - isinstance(ipv6_guestprefixes, list), - True, - "Check list response returns a valid list" - ) - self.assertNotEqual( - len(ipv6_guestprefixes), - 0, - "Check guest IPv6 prefix is created" - ) - ipv6_guestprefix_response = ipv6_guestprefixes[0] - - self.assertEqual( - ipv6_guestprefix.id, - ipv6_guestprefix_response.id, - "Check server id" - ) - self.assertEqual( - ipv6_guestprefix_response.prefix, - ipv6_guestprefix_service["prefix"], - "Check prefix for IPv6" - ) - - cmd = deleteGuestNetworkIpv6Prefix.deleteGuestNetworkIpv6PrefixCmd() - cmd.id = ipv6_guestprefix.id - self.apiclient.deleteGuestNetworkIpv6Prefix(cmd) - return - - @attr( - tags=[ - "advanced", - "basic", - "eip", - "sg", - "advancedns", - "smoke"], - required_hardware="false") - def test_02_create_ipv6_guest_prefix_fail(self): - """Test to add IPv6 guest prefix failure - - # Validate the following: - # 1. createGuestNetworkIpv6Prefix should fail - """ - ipv6_guestprefix_service = self.services["guestip6prefix"] - cmd = createGuestNetworkIpv6Prefix.createGuestNetworkIpv6PrefixCmd() - cmd.zoneid = self.zone.id - prefix = ipv6_guestprefix_service["prefix"] - x = prefix.split("/") - x[1] = "72" - prefix = "/".join(x) - cmd.prefix = prefix - try: - ipv6_guestprefix = self.apiclient.createGuestNetworkIpv6Prefix(cmd) - except Exception as e: - self.debug("IPv6 guest prefix creation failed as expected %s " % e) - ipv6_guestprefix = None - if ipv6_guestprefix != None: - self.debug("Created IPv6 guest prefix with ID: %s. Deleting it before failure" % ipv6_guestprefix.id) - cmd = deleteGuestNetworkIpv6Prefix.deleteGuestNetworkIpv6PrefixCmd() - cmd.id = ipv6_guestprefix.id - self.apiclient.deleteGuestNetworkIpv6Prefix(cmd) - self.fail("IPv6 guest prefix created despite CIDR size greater than 64") - return +SLEEP_BEFORE_VR_CHANGES = 45 +PING_RETRIES = 5 +PING_SLEEP = 20 class TestIpv6Network(cloudstackTestCase): @@ -593,17 +140,10 @@ class TestIpv6Network(cloudstackTestCase): ) cls._cleanup.append(cls.account) cls.hypervisor = testClient.getHypervisorInfo() - cls.template = get_template( - cls.apiclient, - cls.zone.id, - cls.services["ostype"] - ) - if cls.hypervisor.lower() in ('xenserver'): - # Default Xenserver template has IPv6 disabled - cls.template = get_test_template( - cls.apiclient, - cls.zone.id, - cls.hypervisor) + cls.template = get_test_template( + cls.apiclient, + cls.zone.id, + cls.hypervisor) else: cls.debug("IPv6 is not supported, skipping tests!") return @@ -614,11 +154,13 @@ class TestIpv6Network(cloudstackTestCase): Configurations.update(cls.apiclient, ipv6_offering_config_name, cls.initial_ipv6_offering_enabled) - super(TestIpv6Network, cls).tearDownClass() - if cls.test_ipv6_guestprefix != None: - cmd = deleteGuestNetworkIpv6Prefix.deleteGuestNetworkIpv6PrefixCmd() - cmd.id = cls.test_ipv6_guestprefix.id - cls.apiclient.deleteGuestNetworkIpv6Prefix(cmd) + try: + super(TestIpv6Network, cls).tearDownClass() + finally: + if cls.test_ipv6_guestprefix != None: + cmd = deleteGuestNetworkIpv6Prefix.deleteGuestNetworkIpv6PrefixCmd() + cmd.id = cls.test_ipv6_guestprefix.id + cls.apiclient.deleteGuestNetworkIpv6Prefix(cmd) @classmethod def getGuestIpv6Prefix(cls): @@ -650,7 +192,10 @@ class TestIpv6Network(cloudstackTestCase): vlan = ip_range.vlan if ipv4_range_vlan == None and vlan.startswith("vlan://"): vlan = vlan.replace("vlan://", "") - ipv4_range_vlan = int(vlan) + if vlan == "untagged": + ipv4_range_vlan = None + else: + ipv4_range_vlan = int(vlan) ipv6_publiciprange_service = cls.services["publicip6range"] ipv6_publiciprange_service["zoneid"] = cls.zone.id ipv6_publiciprange_service["vlan"] = ipv4_range_vlan @@ -665,16 +210,18 @@ class TestIpv6Network(cloudstackTestCase): self.services = self.testClient.getParsedTestDataConfig() self.apiclient = self.testClient.getApiClient() self.dbclient = self.testClient.getDbConnection() + self.thread = None self.cleanup = [] return def tearDown(self): try: - #Clean up, terminate the created templates - cleanup_resources(self.apiclient, reversed(self.cleanup)) - + if self.thread and self.thread.is_alive(): + self.thread.join(5*60) except Exception as e: raise Exception("Warning: Exception during cleanup : %s" % e) + finally: + super(TestIpv6Network, self).tearDown() return def getRandomIpv6Cidr(self): @@ -690,30 +237,33 @@ class TestIpv6Network(cloudstackTestCase): ) self.cleanup.append(self.service_offering) - def createIpv6NetworkOfferingInternal(self, is_redundant, egressdefaultpolicy=True): - ipv6_service = self.services["network_offering"] + def createNetworkOfferingInternal(self, is_redundant, is_ipv6, egressdefaultpolicy=True): + off_service = self.services["network_offering"] if is_redundant: - ipv6_service = self.services["nw_off_isolated_RVR"] - ipv6_service["internetprotocol"] = "dualstack" + off_service = self.services["nw_off_isolated_RVR"] + if is_ipv6: + off_service["internetprotocol"] = "dualstack" if egressdefaultpolicy: - ipv6_service["egress_policy"] = egressdefaultpolicy + off_service["egress_policy"] = egressdefaultpolicy network_offering = NetworkOffering.create( self.apiclient, - ipv6_service + off_service ) + self.cleanup.append(network_offering) network_offering.update(self.apiclient, state='Enabled') return network_offering - def createIpv6NetworkOffering(self, is_redundant): - self.network_offering = self.createIpv6NetworkOfferingInternal(is_redundant, False) - self.cleanup.append(self.network_offering) + def createIpv4NetworkOffering(self, is_redundant=False): + self.network_offering = self.createNetworkOfferingInternal(is_redundant, False, False) - def createIpv6NetworkOfferingForUpdate(self, is_redundant): - self.network_offering_update = self.createIpv6NetworkOfferingInternal(is_redundant) - self.cleanup.append(self.network_offering_update) + def createIpv6NetworkOffering(self, is_redundant=False): + self.network_offering = self.createNetworkOfferingInternal(is_redundant, True, False) + + def createIpv6NetworkOfferingForUpdate(self, is_redundant=False): + self.network_offering_update = self.createNetworkOfferingInternal(is_redundant, True) - def deployIpv6Network(self): + def deployNetwork(self): self.services["network"]["networkoffering"] = self.network_offering.id self.network = Network.create( self.apiclient, @@ -724,7 +274,7 @@ class TestIpv6Network(cloudstackTestCase): ) self.cleanup.append(self.network) - def deployIpv6NetworkVm(self): + def deployNetworkVm(self): if self.template == FAILED: assert False, "get_test_template() failed to return template" self.services["virtual_machine"]["zoneid"] = self.zone.id @@ -844,7 +394,7 @@ class TestIpv6Network(cloudstackTestCase): cmd, hypervisor=self.routerDetailsMap[router.id]['hypervisor'] ) - self.assertTrue(type(result) == list, + self.assertTrue(type(result) == list and len(result) > 0, "%s on router %s returned invalid result" % (cmd, router.id)) result = '\n'.join(result) return result @@ -865,6 +415,23 @@ class TestIpv6Network(cloudstackTestCase): if router.redundantstate == red_state: return router + def getNetworkGateway(self, network): + ipv6_network = Network.list(self.apiclient,listall="true",id=network.id) + self.assertTrue( + isinstance(ipv6_network, list), + "Check listNetworks response returns a valid list" + ) + self.assertEqual( + len(ipv6_network), + 1, + "Network not found" + ) + ipv6_network = ipv6_network[0] + self.assertNotEqual(ipv6_network.ip6gateway, + None, + "IPv6 gateway for network is empty") + return ipv6_network.ip6gateway + def getNetworkRoutes(self, network): ipv6_network = Network.list(self.apiclient,listall="true",id=network.id) self.assertTrue( @@ -896,11 +463,11 @@ class TestIpv6Network(cloudstackTestCase): self.assertTrue(type(res) == str and len(res) > 0 and st in res, "%s failed on router %s" % (cmd, router.id)) - def checkIpv6NetworkPrimaryRouter(self, router): + def checkIpv6NetworkPrimaryRouter(self, router, network_ip6gateway): self.checkRouterNicState(router, "eth0", "UP") - guest_gateway_check_cmd = "ip -6 address show %s | grep 'inet6 %s'" % ("eth0", self.network.ip6gateway) + guest_gateway_check_cmd = "ip -6 address show %s | grep 'inet6 %s'" % ("eth0", network_ip6gateway) res = self.getRouterProcessStatus(router, guest_gateway_check_cmd) - self.assertTrue(type(res) == str and len(res) > 0 and self.network.ip6gateway in res, + self.assertTrue(type(res) == str and len(res) > 0 and network_ip6gateway in res, "%s failed on router %s" % (guest_gateway_check_cmd, router.id)) self.assertFalse("dadfailed" in res, "dadfailed for IPv6 guest gateway on router %s" % router.id) @@ -930,22 +497,23 @@ class TestIpv6Network(cloudstackTestCase): self.assertTrue(type(res) == str and len(res) > 0 and public_ipv6_gateway in res, "%s failed on router %s" % (default_route_check_cmd, router.id)) - def checkIpv6NetworkBackupRouter(self, router): + def checkIpv6NetworkBackupRouter(self, router, network_ip6gateway): self.checkRouterNicState(router, "eth0", "UP") - guest_gateway_check_cmd = "ip -6 address show %s | grep 'inet6 %s'" % ("eth0", self.network.ip6gateway) + guest_gateway_check_cmd = "ip -6 address show %s | grep 'inet6 %s'" % ("eth0", network_ip6gateway) res = self.getRouterProcessStatus(router, guest_gateway_check_cmd) - self.assertFalse(type(res) == str and len(res) > 0 and self.network.ip6gateway in res, + self.assertFalse(type(res) == str and len(res) > 0 and network_ip6gateway in res, "%s failed on router %s" % (guest_gateway_check_cmd, router.id)) self.checkRouterNicState(router, "eth2", "DOWN") def checkIpv6NetworkRoutersInternal(self): + network_ip6gateway = self.getNetworkGateway(self.network) for router in self.routers: if router.state != "Running": continue if router.isredundantrouter == True and router.redundantstate == 'BACKUP': - self.checkIpv6NetworkBackupRouter(router) + self.checkIpv6NetworkBackupRouter(router, network_ip6gateway) continue - self.checkIpv6NetworkPrimaryRouter(router) + self.checkIpv6NetworkPrimaryRouter(router, network_ip6gateway) def checkIpv6NetworkVm(self): @@ -958,7 +526,7 @@ class TestIpv6Network(cloudstackTestCase): self.assertEqual( len(nics), 1, - "Router for the network isn't found" + "VM NIC for the network isn't found" ) nic = nics[0] self.assertNotEqual(nic.ip6address, @@ -974,9 +542,11 @@ class TestIpv6Network(cloudstackTestCase): def restartNetworkWithCleanup(self): self.network.restart(self.apiclient, cleanup=True) + time.sleep(SLEEP_BEFORE_VR_CHANGES) def updateNetworkWithOffering(self): self.network.update(self.apiclient, networkofferingid=self.network_offering_update.id) + time.sleep(SLEEP_BEFORE_VR_CHANGES) def createIpv6FirewallRuleInNetwork(self, network_id, traffic_type, source_cidr, dest_cidr, protocol, start_port, end_port, icmp_type, icmp_code): @@ -1000,9 +570,8 @@ class TestIpv6Network(cloudstackTestCase): fw_rule = self.apiclient.createIpv6FirewallRule(cmd) return fw_rule - def checkNetworkRouting(self): - self.routing_test_network_offering = self.createIpv6NetworkOfferingInternal(False, True) - self.cleanup.append(self.routing_test_network_offering) + def deployRoutingTestResources(self): + self.routing_test_network_offering = self.createNetworkOfferingInternal(False, True) self.services["network"]["networkoffering"] = self.routing_test_network_offering.id self.routing_test_network = Network.create( self.apiclient, @@ -1025,37 +594,62 @@ class TestIpv6Network(cloudstackTestCase): ) self.cleanup.append(self.routing_test_vm) + def prepareRoutingTestResourcesInBackground(self): + self.thread = threading.Thread(target=self.deployRoutingTestResources, args=()) + self.thread.daemon = True + self.thread.start() + + def checkIpv6NetworkRouting(self): + if not self.thread: + self.deployRoutingTestResources() + else: + self.thread.join(5*60) + self.assertFalse(not self.routing_test_network or not self.routing_test_vm, + "Routing resources failure") + fw1 = self.createIpv6FirewallRuleInNetwork(self.routing_test_network.id, "Ingress", None, None, "icmp", None, None, None, None) fw2 = self.createIpv6FirewallRuleInNetwork(self.network.id, "Ingress", None, None, "icmp", None, None, None, None) - router = self.getNetworkRouter(self.routing_test_network) - self.logger.debug("Adding network routes in routing_test_network %s" % self.network_ipv6_routes) - for route in self.network_ipv6_routes: + test_network_router = self.getNetworkRouter(self.routing_test_network) + routes = self.getNetworkRoutes(self.network) + self.logger.debug("Adding network routes in routing_test_network %s" % routes) + for route in routes: add_route_cmd = "ip -6 route add %s via %s" % (route.subnet, route.gateway) - self.getRouterProcessStatus(router, add_route_cmd) + self.getRouterProcessStatus(test_network_router, add_route_cmd) - router = self.getNetworkRouter(self.network) + network_router = self.getNetworkRouter(self.network) routes = self.getNetworkRoutes(self.routing_test_network) self.logger.debug("Adding routing_test_network routes in network %s" % routes) for route in routes: add_route_cmd = "ip -6 route add %s via %s" % (route.subnet, route.gateway) - self.getRouterProcessStatus(router, add_route_cmd) - - time.sleep(self.services["sleep"]) + self.getRouterProcessStatus(network_router, add_route_cmd) ping_cmd = "ping6 -c 4 %s" % self.virtual_machine_ipv6_address - res = self.getRouterProcessStatus(router, ping_cmd) + count = 0 + while count < PING_RETRIES: + count = count + 1 + res = self.getRouterProcessStatus(test_network_router, ping_cmd) + if " 0% packet loss" in res: + break + time.sleep(PING_SLEEP) self.assertTrue(" 0% packet loss" in res, - "Ping from router %s of network %s to VM %s of network %s is unsuccessful" % (router.id, self.routing_test_network.id, self.virtual_machine.id, self.network.id)) + "Ping from router %s of network %s to VM %s of network %s is unsuccessful" % (test_network_router.id, self.routing_test_network.id, self.virtual_machine.id, self.network.id)) ssh = self.routing_test_vm.get_ssh_client(retries=5) - res = ssh.execute(ping_cmd) + count = 0 + while count < PING_RETRIES: + count = count + 1 + res = ssh.execute(ping_cmd) + if type(res) == list and len(res) > 0 and " 0% packet loss" in '\n'.join(res): + break + time.sleep(PING_SLEEP) self.assertTrue(type(res) == list and len(res) > 0, "%s on VM %s returned invalid result" % (ping_cmd, self.routing_test_vm.id)) self.logger.debug(res) res = '\n'.join(res) + self.assertTrue(" 0% packet loss" in res, "Ping from VM %s of network %s to VM %s of network %s is unsuccessful" % (self.routing_test_vm.id, self.routing_test_network.id, self.virtual_machine.id, self.network.id)) @@ -1160,17 +754,22 @@ class TestIpv6Network(cloudstackTestCase): None, None, None, None, rule) def checkNetworkVRRedundancy(self): + network_ip6gateway = self.getNetworkGateway(self.network) primary_router = self.getNetworkRouter(self.network) Router.stop( self.apiclient, id=primary_router.id ) - time.sleep(self.services["sleep"]/2) + time.sleep(SLEEP_BEFORE_VR_CHANGES) new_primary_router = self.getNetworkRouter(self.network) self.assertNotEqual(new_primary_router.id, primary_router.id, "Original primary router ID: %s of network is still the primary router after stopping" % (primary_router.id)) - print(new_primary_router) - self.checkIpv6NetworkPrimaryRouter(new_primary_router) + self.checkIpv6NetworkPrimaryRouter(new_primary_router, network_ip6gateway) + + def checkIpv6Network(self): + self.checkIpv6NetworkBasic() + self.checkIpv6NetworkRoutersBasic() + self.checkIpv6NetworkRoutersInternal() @attr( @@ -1192,69 +791,23 @@ class TestIpv6Network(cloudstackTestCase): # 3. List router for the network and verify it has required IPv6 details for Guest and Public NIC of the VR # 4. SSH into VR(s) and verify correct details are present for its NICs # 5. Verify VM in network has required IPv6 details - # 6. Restart network with cleanup - # 7. Update network with a new offering - # 8. Again verify network and VR details - # 9. Deploy another IPv6 network and check routing between two networks and their VM - # 10. Create IPv6 firewall rules and verify in VR if they get implemented + # 6. Restart network with cleanup and re-verify network details + # 7. Update network with a new offering and re-verify network details + # 8. Deploy another IPv6 network and check routing between two networks and their VM + # 9. Create IPv6 firewall rules and verify in VR if they get implemented """ - self.createIpv6NetworkOffering(False) - self.createIpv6NetworkOfferingForUpdate(False) + self.createIpv6NetworkOffering() + self.createIpv6NetworkOfferingForUpdate() self.createTinyServiceOffering() - self.deployIpv6Network() - self.deployIpv6NetworkVm() - self.checkIpv6NetworkBasic() - self.checkIpv6NetworkRoutersBasic() - self.checkIpv6NetworkRoutersInternal() + self.deployNetwork() + self.deployNetworkVm() + self.checkIpv6Network() self.checkIpv6NetworkVm() + self.prepareRoutingTestResourcesInBackground() self.restartNetworkWithCleanup() + self.checkIpv6Network() self.updateNetworkWithOffering() - self.checkIpv6NetworkBasic() - self.checkIpv6NetworkRoutersBasic() - self.checkNetworkRouting() + self.checkIpv6Network() + self.checkIpv6NetworkRouting() self.checkIpv6FirewallRule() - - @attr( - tags=[ - "advanced", - "basic", - "eip", - "sg", - "advancedns", - "smoke"], - required_hardware="false") - @skipTestIf("ipv6NotSupported") - def test_02_verify_ipv6_network_redundant(self): - """Test to verify redundant IPv6 network - - # Validate the following: - # 1. Create IPv6 network, deploy VM - # 2. Verify network has required IPv6 details - # 3. List VRs for the network and verify it has required IPv6 details for Guest and Public NIC of the VR - # 4. SSH into VR(s) and verify correct details are present for its NICs - # 5. Verify VM in network has required IPv6 details - # 6. Restart network with cleanup - # 7. Update network with a new offering - # 8. Again verify network and VR details - # 9. Deploy another IPv6 network and check routing between two networks and their VM - # 10. Create IPv6 firewall rules and verify in VR if they get implemented - # 11. Stop primary router and verify internals in backup VR - """ - - self.createIpv6NetworkOffering(True) - self.createIpv6NetworkOfferingForUpdate(True) - self.createTinyServiceOffering() - self.deployIpv6Network() - self.deployIpv6NetworkVm() - self.checkIpv6NetworkBasic() - self.checkIpv6NetworkRoutersBasic() - self.checkIpv6NetworkRoutersInternal() - self.checkIpv6NetworkVm() - self.restartNetworkWithCleanup() - self.updateNetworkWithOffering() - self.checkIpv6NetworkBasic() - self.checkIpv6NetworkRoutersBasic() - self.checkNetworkRouting() - self.checkIpv6FirewallRule() - self.checkNetworkVRRedundancy() diff --git a/test/integration/smoke/test_vpc_ipv6.py b/test/integration/smoke/test_vpc_ipv6.py new file mode 100644 index 00000000000..e00c578809c --- /dev/null +++ b/test/integration/smoke/test_vpc_ipv6.py @@ -0,0 +1,901 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +""" BVT test for IPv6 VPC""" + +#Import Local Modules +from marvin.codes import FAILED +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.cloudstackAPI import (createGuestNetworkIpv6Prefix, + listGuestNetworkIpv6Prefixes, + deleteGuestNetworkIpv6Prefix) +from marvin.lib.utils import (isAlmostEqual, + random_gen, + get_process_status, + get_host_credentials) +from marvin.lib.base import (Configurations, + Domain, + NetworkOffering, + VpcOffering, + Account, + PublicIpRange, + Network, + VPC, + Router, + ServiceOffering, + VirtualMachine, + NIC, + Host, + NetworkACLList, + NetworkACL) +from marvin.lib.common import (get_domain, + get_zone, + get_test_template, + get_template) +from marvin.sshClient import SshClient +from marvin.cloudstackException import CloudstackAPIException +from marvin.lib.decoratorGenerators import skipTestIf + +from nose.plugins.attrib import attr +from ipaddress import IPv6Network +from random import getrandbits, choice, randint +import time +import logging +import threading + +ipv6_offering_config_name = "ipv6.offering.enabled" +ULA_BASE = IPv6Network("fd00::/8") +PREFIX_OPTIONS = [i for i in range(48, 65, 4)] +ACL_TABLE = "ip6_acl" +ACL_CHAINS_SUFFIX = { + "Ingress": "_ingress_policy", + "Egress": "_egress_policy" +} +CIDR_IPV6_ANY = "::/0" +ICMPV6_TYPE = { + 1: "destination-unreachable", + 2: "packet-too-big", + 3: "time-exceeded", + 4: "parameter-problem", + 128: "echo-request", + 129: "echo-reply", + 130: "mld-listener-query", + 131: "mld-listener-report", + 132: "mld-listener-done", + 133: "nd-router-solicit", + 134: "nd-router-advert", + 135: "nd-neighbor-solicit", + 136: "nd-neighbor-advert", + 137: "nd-redirect", + 138: "router-renumbering", + 141: "ind-neighbor-solicit", + 142: "ind-neighbor-advert", + 143: "mld2-listener-report" +} +ICMPV6_CODE_TYPE = { + 0: "no-route", + 1: "admin-prohibited", + 3: "addr-unreachable", + 4: "port-unreachable", + 5: "policy-fail", + 6: "reject-route" +} +ICMPV6_TYPE_ANY = "{ destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-request, echo-reply, mld-listener-query, mld-listener-report, mld-listener-done, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, nd-redirect, router-renumbering }" +TCP_UDP_PORT_ANY = "{ 0-65535 }" +VPC_ROUTER_PUBLIC_NIC = "eth1" +VPC_ROUTER_GUEST_NIC = "eth2" +VPC_DATA = { + "cidr": "10.1.0.0/22", + "tier1_gateway": "10.1.1.1", + "tier2_gateway": "10.1.2.1", + "tier_netmask": "255.255.255.0" +} +ROUTE_TEST_VPC_DATA = { + "cidr": "10.2.0.0/22", + "tier1_gateway": "10.2.1.1", + "tier_netmask": "255.255.255.0" +} +SLEEP_BEFORE_VR_CHANGES = 90 +PING_RETRIES = 5 +PING_SLEEP = 20 + + +class TestIpv6Vpc(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + testClient = super(TestIpv6Vpc, cls).getClsTestClient() + cls.services = testClient.getParsedTestDataConfig() + cls.apiclient = testClient.getApiClient() + cls.dbclient = testClient.getDbConnection() + cls.test_ipv6_guestprefix = None + cls.initial_ipv6_offering_enabled = None + cls._cleanup = [] + cls.routerDetailsMap = {} + cls.vpcAllowAllAclDetailsMap = {} + + cls.logger = logging.getLogger('TestIpv6Vpc') + + cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests()) + cls.services['mode'] = cls.zone.networktype + cls.ipv6NotSupported = False + + ipv6_guestprefix = cls.getGuestIpv6Prefix() + if ipv6_guestprefix == None: + cls.ipv6NotSupported = True + if cls.ipv6NotSupported == False: + ipv6_publiciprange = cls.getPublicIpv6Range() + if ipv6_publiciprange == None: + cls.ipv6NotSupported = True + + if cls.ipv6NotSupported == False: + cls.initial_ipv6_offering_enabled = Configurations.list( + cls.apiclient, + name=ipv6_offering_config_name)[0].value + Configurations.update(cls.apiclient, + ipv6_offering_config_name, + "true") + cls.domain = get_domain(cls.apiclient) + cls.account = Account.create( + cls.apiclient, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls._cleanup.append(cls.account) + cls.hypervisor = testClient.getHypervisorInfo() + cls.template = get_test_template( + cls.apiclient, + cls.zone.id, + cls.hypervisor) + else: + cls.debug("IPv6 is not supported, skipping tests!") + return + + @classmethod + def tearDownClass(cls): + if cls.initial_ipv6_offering_enabled != None: + Configurations.update(cls.apiclient, + ipv6_offering_config_name, + cls.initial_ipv6_offering_enabled) + try: + super(TestIpv6Vpc, cls).tearDownClass() + finally: + if cls.test_ipv6_guestprefix != None: + cmd = deleteGuestNetworkIpv6Prefix.deleteGuestNetworkIpv6PrefixCmd() + cmd.id = cls.test_ipv6_guestprefix.id + cls.apiclient.deleteGuestNetworkIpv6Prefix(cmd) + + @classmethod + def getGuestIpv6Prefix(cls): + cmd = listGuestNetworkIpv6Prefixes.listGuestNetworkIpv6PrefixesCmd() + cmd.zoneid = cls.zone.id + ipv6_prefixes_response = cls.apiclient.listGuestNetworkIpv6Prefixes(cmd) + if isinstance(ipv6_prefixes_response, list) == True and len(ipv6_prefixes_response) > 0: + return ipv6_prefixes_response[0] + ipv6_guestprefix_service = cls.services["guestip6prefix"] + cmd = createGuestNetworkIpv6Prefix.createGuestNetworkIpv6PrefixCmd() + cmd.zoneid = cls.zone.id + cmd.prefix = ipv6_guestprefix_service["prefix"] + ipv6_guestprefix = cls.apiclient.createGuestNetworkIpv6Prefix(cmd) + cls.test_ipv6_guestprefix = ipv6_guestprefix + return ipv6_guestprefix + + @classmethod + def getPublicIpv6Range(cls): + list_public_ip_range_response = PublicIpRange.list( + cls.apiclient, + zoneid=cls.zone.id + ) + ipv4_range_vlan = None + if isinstance(list_public_ip_range_response, list) == True and len(list_public_ip_range_response) > 0: + for ip_range in list_public_ip_range_response: + if ip_range.ip6cidr != None and ip_range.ip6gateway != None: + return ip_range + if ip_range.netmask != None and ip_range.gateway != None: + vlan = ip_range.vlan + if ipv4_range_vlan == None and vlan.startswith("vlan://"): + vlan = vlan.replace("vlan://", "") + if vlan == "untagged": + ipv4_range_vlan = None + else: + ipv4_range_vlan = int(vlan) + ipv6_publiciprange_service = cls.services["publicip6range"] + ipv6_publiciprange_service["zoneid"] = cls.zone.id + ipv6_publiciprange_service["vlan"] = ipv4_range_vlan + ipv6_publiciprange = PublicIpRange.create( + cls.apiclient, + ipv6_publiciprange_service + ) + cls._cleanup.append(ipv6_publiciprange) + return ipv6_publiciprange + + def setUp(self): + self.services = self.testClient.getParsedTestDataConfig() + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.thread = None + self.cleanup = [] + return + + def tearDown(self): + try: + if self.thread and self.thread.is_alive(): + self.thread.join(5*60) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + finally: + super(TestIpv6Vpc, self).tearDown() + return + + def getRandomIpv6Cidr(self): + prefix_length = choice(PREFIX_OPTIONS) + random_suffix = getrandbits(40) << (128-prefix_length) + base_address = ULA_BASE.network_address + random_suffix + return str(IPv6Network((base_address, prefix_length))) + + def createTinyServiceOffering(self): + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offerings"]["big"], + ) + self.cleanup.append(self.service_offering) + + def createVpcOfferingInternal(self, is_redundant, is_ipv6): + off_service = self.services["vpc_offering"] + if is_redundant: + off_service["serviceCapabilityList"] = { + "SourceNat": { + "RedundantRouter": 'true' + }, + } + if is_ipv6: + off_service["internetprotocol"] = "dualstack" + vpc_offering = VpcOffering.create( + self.apiclient, + off_service + ) + self.cleanup.append(vpc_offering) + vpc_offering.update(self.apiclient, state='Enabled') + return vpc_offering + + def createIpv4VpcOffering(self, is_redundant=False): + self.vpc_offering = self.createVpcOfferingInternal(is_redundant, False) + + def createIpv6VpcOffering(self, is_redundant=False): + self.vpc_offering = self.createVpcOfferingInternal(is_redundant, True) + + def createIpv6VpcOfferingForUpdate(self, is_redundant=False): + self.vpc_offering_update = self.createVpcOfferingInternal(is_redundant, True) + + def createNetworkTierOfferingInternal(self, is_ipv6, remove_lb=True): + off_service = self.services["nw_offering_isolated_vpc"] + if not remove_lb: # Remove Lb service + if "serviceProviderList" in off_service and "Lb" in off_service["serviceProviderList"].keys(): + providers = off_service["serviceProviderList"] + providers.pop("Lb") + off_service["serviceProviderList"] = providers + if "supportedservices" in off_service and "Lb" in off_service["supportedservices"]: + supportedServices = off_service["supportedservices"].split(",") + supportedServices.remove("Lb") + off_service["supportedservices"] = ",".join(supportedServices) + if is_ipv6: + off_service["internetprotocol"] = "dualstack" + network_offering = NetworkOffering.create( + self.apiclient, + off_service, + conservemode=False + ) + self.cleanup.append(network_offering) + network_offering.update(self.apiclient, state='Enabled') + return network_offering + + def createIpv4NetworkTierOffering(self): + self.network_offering = self.createNetworkTierOfferingInternal(False) + + def createIpv6NetworkTierOffering(self, remove_lb=True): + self.network_offering = self.createNetworkTierOfferingInternal(True) + + def createIpv6NetworkTierOfferingForUpdate(self): + self.network_offering_update = self.createNetworkTierOfferingInternal(True) + + def deployAllowAllVpcInternal(self, cidr): + service = self.services["vpc"] + service["cidr"] = cidr + vpc = VPC.create( + self.apiclient, + self.services["vpc"], + vpcofferingid=self.vpc_offering.id, + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid + ) + self.cleanup.append(vpc) + acl = NetworkACLList.create( + self.apiclient, + services={}, + name="allowall", + description="allowall", + vpcid=vpc.id + ) + rule ={ + "protocol": "all", + "traffictype": "ingress", + } + NetworkACL.create(self.apiclient, + services=rule, + aclid=acl.id + ) + rule["traffictype"] = "egress" + NetworkACL.create(self.apiclient, + services=rule, + aclid=acl.id + ) + self.vpcAllowAllAclDetailsMap[vpc.id] = acl.id + return vpc + + def deployVpc(self): + self.vpc = self.deployAllowAllVpcInternal(VPC_DATA["cidr"]) + + def deployNetworkTierInternal(self, network_offering_id, vpc_id, tier_gateway, tier_netmask, acl_id=None, tier_name=None): + if not acl_id and vpc_id in self.vpcAllowAllAclDetailsMap: + acl_id = self.vpcAllowAllAclDetailsMap[vpc_id] + service = self.services["ntwk"] + if tier_name: + service["name"] = tier_name + service["displaytext"] = "vpc-%s" % tier_name + network = Network.create( + self.apiclient, + service, + self.account.name, + self.account.domainid, + networkofferingid=network_offering_id, + vpcid=vpc_id, + zoneid=self.zone.id, + gateway=tier_gateway, + netmask=tier_netmask, + aclid=acl_id + ) + self.cleanup.append(network) + return network + + def deployNetworkTier(self): + self.network = self.deployNetworkTierInternal( + self.network_offering.id, + self.vpc.id, + VPC_DATA["tier1_gateway"], + VPC_DATA["tier_netmask"] + ) + + def deployNetworkTierVmInternal(self, network): + if self.template == FAILED: + assert False, "get_test_template() failed to return template" + self.services["virtual_machine"]["zoneid"] = self.zone.id + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + networkids=network, + serviceofferingid=self.service_offering.id + ) + self.cleanup.append(virtual_machine) + return virtual_machine + + def deployNetworkTierVm(self): + self.virtual_machine = self.deployNetworkTierVmInternal(self.network.id) + + def checkIpv6Vpc(self): + self.debug("Listing VPC: %s" % (self.vpc.name)) + ipv6_vpc = VPC.list(self.apiclient,listall="true",id=self.vpc.id) + self.assertTrue( + isinstance(ipv6_vpc, list), + "Check listVpcs response returns a valid list" + ) + self.assertEqual( + len(ipv6_vpc), + 1, + "Network not found" + ) + ipv6_vpc = ipv6_vpc[0] + self.assertNotEqual(ipv6_vpc.ip6routes, + None, + "IPv6 routes for network is empty") + + def checkIpv6NetworkTierBasic(self): + self.debug("Listing network: %s" % (self.network.name)) + ipv6_network = Network.list(self.apiclient,listall="true",id=self.network.id) + self.assertTrue( + isinstance(ipv6_network, list), + "Check listNetworks response returns a valid list" + ) + self.assertEqual( + len(ipv6_network), + 1, + "Network not found" + ) + ipv6_network = ipv6_network[0] + self.assertNotEqual(ipv6_network, + None, + "User is not able to retrieve network details %s" % self.network.id) + self.assertNotEqual(ipv6_network.ip6cidr, + None, + "IPv6 CIDR for network is empty") + self.assertNotEqual(ipv6_network.ip6gateway, + None, + "IPv6 gateway for network is empty") + self.assertNotEqual(ipv6_network.ip6routes, + None, + "IPv6 routes for network is empty") + + def checkIpv6VpcRoutersBasic(self): + self.debug("Listing routers for VPC: %s" % self.vpc.name) + self.routers = Router.list( + self.apiclient, + vpcid=self.vpc.id, + listall=True + ) + self.assertTrue( + isinstance(self.routers, list), + "Check listRouters response returns a valid list" + ) + self.assertTrue( + len(self.routers) > 0, + "Router for the network isn't found" + ) + for router in self.routers: + self.assertFalse( + router.isredundantrouter == True and router.redundantstate == "FAULT", + "Router for the network is in FAULT state" + ) + nics = router.nic + for nic in nics: + if (nic.traffictype == 'Guest' and router.isredundantrouter == False) or nic.traffictype == 'Public': + self.assertNotEqual(nic.ip6address, + None, + "IPv6 address for router %s NIC is empty" % nic.traffictype) + self.assertNotEqual(nic.ip6cidr, + None, + "IPv6 CIDR for router %s NIC is empty" % nic.traffictype) + self.assertNotEqual(nic.ip6gateway, + None, + "IPv6 gateway for router %s NIC is empty" % nic.traffictype) + + + def getRouterProcessStatus(self, router, cmd): + if router.id not in self.routerDetailsMap or self.routerDetailsMap[router.id] is None: + connect_ip = self.apiclient.connection.mgtSvr + connect_user = self.apiclient.connection.user + connect_passwd = self.apiclient.connection.passwd + hypervisor = self.hypervisor + if self.hypervisor.lower() not in ('vmware', 'hyperv'): + hosts = Host.list( + self.apiclient, + zoneid=router.zoneid, + type='Routing', + state='Up', + id=router.hostid + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list host returns a valid list" + ) + host = hosts[0] + connect_ip = host.ipaddress + hypervisor = None + try: + connect_user, connect_passwd= get_host_credentials( + self.config, host.ipaddress) + except KeyError: + self.skipTest( + "Marvin configuration has no host credentials to\ + check router services") + details = {} + details['connect_ip'] = connect_ip + details['connect_user'] = connect_user + details['connect_passwd'] = connect_passwd + details['hypervisor'] = hypervisor + self.routerDetailsMap[router.id] = details + result = get_process_status( + self.routerDetailsMap[router.id]['connect_ip'], + 22, + self.routerDetailsMap[router.id]['connect_user'], + self.routerDetailsMap[router.id]['connect_passwd'], + router.linklocalip, + cmd, + hypervisor=self.routerDetailsMap[router.id]['hypervisor'] + ) + self.assertTrue(type(result) == list and len(result) > 0, + "%s on router %s returned invalid result" % (cmd, router.id)) + result = '\n'.join(result) + return result + + def getVpcRouter(self, vpc, red_state="PRIMARY"): + routers = Router.list( + self.apiclient, + vpcid=vpc.id, + listall=True + ) + self.assertTrue( + isinstance(routers, list) and len(routers) > 0, + "No routers found for VPC %s" % vpc.id + ) + if len(routers) == 1: + return routers[0] + for router in routers: + if router.redundantstate == red_state: + return router + + def getNetworkGateway(self, network): + ipv6_network = Network.list(self.apiclient,listall="true",id=network.id) + self.assertTrue( + isinstance(ipv6_network, list), + "Check listNetworks response returns a valid list" + ) + self.assertEqual( + len(ipv6_network), + 1, + "Network not found" + ) + ipv6_network = ipv6_network[0] + self.assertNotEqual(ipv6_network.ip6gateway, + None, + "IPv6 gateway for network is empty") + return ipv6_network.ip6gateway + + def getNetworkRoutes(self, network): + ipv6_network = Network.list(self.apiclient,listall="true",id=network.id) + self.assertTrue( + isinstance(ipv6_network, list), + "Check listNetworks response returns a valid list" + ) + self.assertEqual( + len(ipv6_network), + 1, + "Network not found" + ) + ipv6_network = ipv6_network[0] + self.assertNotEqual(ipv6_network.ip6routes, + None, + "IPv6 routes for network is empty") + return ipv6_network.ip6routes + + def isNetworkEgressDefaultPolicyAllow(self, network): + ipv6_network = Network.list(self.apiclient,listall="true",id=network.id) + if len(ipv6_network) == 1: + ipv6_network = ipv6_network[0] + return ipv6_network.egressdefaultpolicy + return False + + def checkRouterNicState(self, router, dev, state): + st = "state %s" % state + cmd = "ip link show %s | grep '%s'" % (dev, st) + res = self.getRouterProcessStatus(router, cmd) + self.assertTrue(type(res) == str and len(res) > 0 and st in res, + "%s failed on router %s" % (cmd, router.id)) + + def checkIpv6VpcPrimaryRouter(self, router, network_ip6gateway): + self.checkRouterNicState(router, VPC_ROUTER_GUEST_NIC, "UP") + guest_gateway_check_cmd = "ip -6 address show %s | grep 'inet6 %s'" % (VPC_ROUTER_GUEST_NIC, network_ip6gateway) + res = self.getRouterProcessStatus(router, guest_gateway_check_cmd) + self.assertTrue(type(res) == str and len(res) > 0 and network_ip6gateway in res, + "%s failed on router %s" % (guest_gateway_check_cmd, router.id)) + self.assertFalse("dadfailed" in res, + "dadfailed for IPv6 guest gateway on router %s" % router.id) + self.checkRouterNicState(router, VPC_ROUTER_PUBLIC_NIC, "UP") + public_ipv6 = None + public_ipv6_gateway = None + nics = router.nic + for nic in nics: + if nic.traffictype == 'Public': + public_ipv6 = nic.ip6address + public_ipv6_gateway = nic.ip6gateway + break + self.assertNotEqual(public_ipv6, + None, + "IPv6 address for router Public NIC is empty") + public_ip_check_cmd = "ip -6 address show %s | grep 'inet6 %s'" % (VPC_ROUTER_PUBLIC_NIC, public_ipv6) + res = self.getRouterProcessStatus(router, public_ip_check_cmd) + self.assertTrue(type(res) == str and len(res) > 0 and public_ipv6 in res, + "%s failed on router %s" % (public_ip_check_cmd, router.id)) + self.assertFalse("dadfailed" in res, + "dadfailed for public IPv6 on router %s" % router.id) + self.assertNotEqual(public_ipv6_gateway, + None, + "IPv6 gateway for router Public NIC is empty") + default_route_check_cmd = "ip -6 route | grep 'default via %s'" % (public_ipv6_gateway) + res = self.getRouterProcessStatus(router, default_route_check_cmd) + self.assertTrue(type(res) == str and len(res) > 0 and public_ipv6_gateway in res, + "%s failed on router %s" % (default_route_check_cmd, router.id)) + + def checkIpv6VpcBackupRouter(self, router, network_ip6gateway): + self.checkRouterNicState(router, VPC_ROUTER_GUEST_NIC, "UP") + guest_gateway_check_cmd = "ip -6 address show %s | grep 'inet6 %s'" % ("eth0", network_ip6gateway) + res = self.getRouterProcessStatus(router, guest_gateway_check_cmd) + self.assertFalse(type(res) == str and len(res) > 0 and network_ip6gateway in res, + "%s failed on router %s" % (guest_gateway_check_cmd, router.id)) + self.checkRouterNicState(router, VPC_ROUTER_PUBLIC_NIC, "DOWN") + + def checkIpv6VpcRoutersInternal(self): + network_ip6gateway = self.getNetworkGateway(self.network) + for router in self.routers: + if router.state != "Running": + continue + if router.isredundantrouter == True and router.redundantstate == 'BACKUP': + self.checkIpv6VpcBackupRouter(router, network_ip6gateway) + continue + self.checkIpv6VpcPrimaryRouter(router, network_ip6gateway) + + + def checkIpv6NetworkTierVm(self): + self.debug("Listing NICS for VM %s in network tier: %s" % (self.virtual_machine.name, self.network.name)) + nics = NIC.list( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + networkid=self.network.id + ) + self.assertEqual( + len(nics), + 1, + "VM NIC for the network tier isn't found" + ) + nic = nics[0] + self.assertNotEqual(nic.ip6address, + None, + "IPv6 address for VM %s NIC is empty" % nic.traffictype) + self.virtual_machine_ipv6_address = nic.ip6address + self.assertNotEqual(nic.ip6cidr, + None, + "IPv6 CIDR for VM %s NIC is empty" % nic.traffictype) + self.assertNotEqual(nic.ip6gateway, + None, + "IPv6 gateway for VM %s NIC is empty" % nic.traffictype) + + def restartVpcWithCleanup(self): + self.vpc.restart(self.apiclient, cleanup=True) + time.sleep(SLEEP_BEFORE_VR_CHANGES) + + def updateNetworkTierWithOffering(self): + self.network.update(self.apiclient, networkofferingid=self.network_offering_update.id) + time.sleep(SLEEP_BEFORE_VR_CHANGES) + + def deployRoutingTestResources(self): + self.routing_test_vpc = self.deployAllowAllVpcInternal(ROUTE_TEST_VPC_DATA["cidr"]) + self.routing_test_network_offering = self.createNetworkTierOfferingInternal(True) + self.routing_test_network = self.deployNetworkTierInternal( + self.routing_test_network_offering.id, + self.routing_test_vpc.id, + ROUTE_TEST_VPC_DATA["tier1_gateway"], + ROUTE_TEST_VPC_DATA["tier_netmask"] + ) + self.services["virtual_machine"]["zoneid"] = self.zone.id + self.routing_test_vm = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + networkids=[self.routing_test_network.id], + serviceofferingid=self.service_offering.id, + mode="advanced", + vpcid=self.routing_test_vpc.id + ) + self.cleanup.append(self.routing_test_vm) + + def prepareRoutingTestResourcesInBackground(self): + self.thread = threading.Thread(target=self.deployRoutingTestResources, args=()) + self.thread.daemon = True + self.thread.start() + + def checkVpcRouting(self): + if not self.thread: + self.deployRoutingTestResources() + else: + self.thread.join(5*60) + self.assertFalse(not self.routing_test_vpc or not self.routing_test_network or not self.routing_test_vm, + "Routing resources failure") + + test_vpc_router = self.getVpcRouter(self.routing_test_vpc) + routes = self.getNetworkRoutes(self.network) + self.logger.debug("Adding vpc routes in routing_test_vpc %s" % routes) + for route in routes: + add_route_cmd = "ip -6 route add %s via %s" % (route.subnet, route.gateway) + self.getRouterProcessStatus(test_vpc_router, add_route_cmd) + + vpc_router = self.getVpcRouter(self.vpc) + routes = self.getNetworkRoutes(self.routing_test_network) + self.logger.debug("Adding routing_test_vpc routes in vpc %s" % routes) + for route in routes: + add_route_cmd = "ip -6 route add %s via %s" % (route.subnet, route.gateway) + self.getRouterProcessStatus(vpc_router, add_route_cmd) + + ping_cmd = "ping6 -c 4 %s" % self.virtual_machine_ipv6_address + count = 0 + while count < PING_RETRIES: + count = count + 1 + res = self.getRouterProcessStatus(test_vpc_router, ping_cmd) + if " 0% packet loss" in res: + break + time.sleep(PING_SLEEP) + self.assertTrue(" 0% packet loss" in res, + "Ping from router %s of VPC %s to VM %s of VPC %s is unsuccessful" % (test_vpc_router.id, self.routing_test_vpc.id, self.virtual_machine.id, self.vpc.id)) + + ssh = self.routing_test_vm.get_ssh_client(retries=5) + count = 0 + while count < PING_RETRIES: + count = count + 1 + res = ssh.execute(ping_cmd) + if type(res) == list and len(res) > 0 and " 0% packet loss" in '\n'.join(res): + break + time.sleep(PING_SLEEP) + self.assertTrue(type(res) == list and len(res) > 0, + "%s on VM %s returned invalid result" % (ping_cmd, self.routing_test_vm.id)) + self.logger.debug(res) + res = '\n'.join(res) + self.assertTrue(" 0% packet loss" in res, + "Ping from VM %s of VPC %s to VM %s of VPC %s is unsuccessful" % (self.routing_test_vm.id, self.routing_test_vpc.id, self.virtual_machine.id, self.vpc.id)) + + def createNetworkAclRule(self, rule, aclid): + return NetworkACL.create(self.apiclient, + services=rule, + aclid=aclid + ) + + def verifyAclRulesInRouter(self, nic, rules, router): + for rule in rules: + acl_chain = nic + ACL_CHAINS_SUFFIX[rule["traffictype"]] + routerCmd = "nft list chain ip6 %s %s" % (ACL_TABLE, acl_chain) + res = self.getRouterProcessStatus(router, routerCmd) + self.assertTrue(rule["parsedrule"] in res, + "Listing firewall rule with nft list chain failure for rule: %s" % rule["parsedrule"]) + + def checkIpv6AclRule(self): + router = self.getVpcRouter(self.vpc) + + tier1_acl = NetworkACLList.create( + self.apiclient, + services={}, + name="tier1_acl", + description="tier1_acl", + vpcid=self.vpc.id + ) + rules = [] + # Ingress - ip6 saddr SOURCE_CIDR tcp dport { START_PORT-END_PORT } accept + rule = {} + rule["traffictype"] = "Ingress" + rule["cidrlist"] = self.getRandomIpv6Cidr() + rule["protocol"] = "tcp" + rule["startport"] = randint(3000, 5000) + rule["endport"] = rule["startport"] + randint(1, 8) + parsedrule = "ip6 saddr %s %s dport { %d-%d } accept" % (rule["cidrlist"], rule["protocol"], rule["startport"], rule["endport"]) + rules.append({"traffictype": rule["traffictype"], "parsedrule": parsedrule}) + self.createNetworkAclRule(rule, tier1_acl.id) + # Egress - ip6 daddr DEST_CIDR icmpv6 type TYPE code CODE accept + rule = {} + rule["traffictype"] = "Egress" + rule["cidrlist"] = self.getRandomIpv6Cidr() + rule["protocol"] = "icmp" + rule["icmptype"] = choice(list(ICMPV6_TYPE.keys())) + rule["icmpcode"] = choice(list(ICMPV6_CODE_TYPE.keys())) + parsedrule = "ip6 daddr %s %sv6 type %s %sv6 code %s accept" % (rule["cidrlist"], rule["protocol"], ICMPV6_TYPE[rule["icmptype"]], rule["protocol"], ICMPV6_CODE_TYPE[rule["icmpcode"]]) + rules.append({"traffictype": rule["traffictype"], "parsedrule": parsedrule}) + self.createNetworkAclRule(rule, tier1_acl.id) + + self.network.replaceACLList(self.apiclient, tier1_acl.id) + + self.verifyAclRulesInRouter("eth2", rules, router) + + + tier2_acl = NetworkACLList.create( + self.apiclient, + services={}, + name="tier2_acl", + description="tier2_acl", + vpcid=self.vpc.id + ) + rules = [] + # Ingress - ip6 saddr ::/0 udp dport { 0-65355 } ACTION + rule = {} + rule["traffictype"] = "Ingress" + rule["cidrlist"] = CIDR_IPV6_ANY + rule["protocol"] = "udp" + parsedrule = "ip6 saddr %s %s dport %s accept" % (rule["cidrlist"], rule["protocol"], TCP_UDP_PORT_ANY) + rules.append({"traffictype": rule["traffictype"], "parsedrule": parsedrule}) + self.createNetworkAclRule(rule, tier2_acl.id) + # Egress - ip6 daddr DEST_CIDR icmpv6 type TYPE code CODE accept + rule = {} + rule["traffictype"] = "Egress" + rule["protocol"] = "all" + parsedrule = "ip6 daddr %s accept" % (CIDR_IPV6_ANY) + rules.append({"traffictype": rule["traffictype"], "parsedrule": parsedrule}) + self.createNetworkAclRule(rule, tier2_acl.id) + + self.network_offering_tier2 = self.createNetworkTierOfferingInternal(True, False) + self.tier2_network = self.deployNetworkTierInternal( + self.network_offering_tier2.id, + self.vpc.id, + VPC_DATA["tier2_gateway"], + VPC_DATA["tier_netmask"], + tier2_acl.id, + "tier2" + ) + self.tier2_vm = self.deployNetworkTierVmInternal(self.tier2_network.id) + + self.verifyAclRulesInRouter("eth3", rules, router) + + def checkVpcVRRedundancy(self): + network_ip6gateway = self.getNetworkGateway(self.network) + primary_router = self.getVpcRouter(self.vpc) + Router.stop( + self.apiclient, + id=primary_router.id + ) + time.sleep(self.services["sleep"]/2) + new_primary_router = self.getVpcRouter(self.vpc) + self.assertNotEqual(new_primary_router.id, primary_router.id, + "Original primary router ID: %s of VPC is still the primary router after stopping" % (primary_router.id)) + self.checkIpv6VpcPrimaryRouter(new_primary_router, network_ip6gateway) + + def checkIpv6VpcNetworking(self, check_vm=False): + self.checkIpv6Vpc() + self.checkIpv6NetworkTierBasic() + self.checkIpv6VpcRoutersBasic() + self.checkIpv6VpcRoutersInternal() + if check_vm: + self.checkIpv6NetworkTierVm() + + @attr( + tags=[ + "advanced", + "basic", + "eip", + "sg", + "advancedns", + "smoke"], + required_hardware="false") + @skipTestIf("ipv6NotSupported") + def test_01_verify_ipv6_vpc(self): + """Test to verify IPv6 VPC + + # Validate the following: + # 1. Create IPv6 VPC, add tiers, deploy VM + # 2. Verify VPC, tier has required IPv6 details + # 3. List router for the VPC and verify it has required IPv6 details for Guest and Public NIC of the VR + # 4. SSH into VR(s) and verify correct details are present for its NICs + # 5. Verify VM in network tier has required IPv6 details + # 6. Restart VPC with cleanup and re-verify VPC networking + # 7. Update network tier with a new offering and re-verify VPC networking + # 8. Deploy another IPv6 VPC with tier and check routing between two VPC and their VM + # 9. Create IPv6 ACL rules in two different VPC tiers and verify in VR if they get implemented correctly + """ + + self.createIpv6VpcOffering() + self.deployVpc() + self.createIpv6NetworkTierOffering() + self.createIpv6NetworkTierOfferingForUpdate() + self.createTinyServiceOffering() + self.deployNetworkTier() + self.deployNetworkTierVm() + self.checkIpv6VpcNetworking(True) + self.prepareRoutingTestResourcesInBackground() + self.restartVpcWithCleanup() + self.checkIpv6VpcNetworking() + self.updateNetworkTierWithOffering() + self.checkIpv6VpcNetworking() + self.checkVpcRouting() + self.checkIpv6AclRule() diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py index 6567d944759..00d212b139f 100755 --- a/tools/marvin/marvin/lib/base.py +++ b/tools/marvin/marvin/lib/base.py @@ -3292,6 +3292,14 @@ class Network: cmd.resume = resume return (apiclient.migrateNetwork(cmd)) + def replaceACLList(self, apiclient, aclid, gatewayid=None): + cmd = replaceNetworkACLList.replaceNetworkACLListCmd() + cmd.networkid = self.id + cmd.aclid = aclid + if gatewayid: + cmd.gatewayid = gatewayid + return (apiclient.replaceNetworkACLList(cmd)) + @classmethod def list(cls, apiclient, **kwargs): """List all Networks matching criteria""" @@ -3329,6 +3337,11 @@ class NetworkACL: elif protocol: cmd.protocol = protocol + if "icmptype" in services: + cmd.icmptype = services["icmptype"] + if "icmpcode" in services: + cmd.icmpcode = services["icmpcode"] + if "startport" in services: cmd.startport = services["startport"] if "endport" in services: diff --git a/tools/marvin/marvin/sshClient.py b/tools/marvin/marvin/sshClient.py index 8b8405cc8c8..f34720fb798 100644 --- a/tools/marvin/marvin/sshClient.py +++ b/tools/marvin/marvin/sshClient.py @@ -61,9 +61,9 @@ class SshClient(object): self.retryCnt = 0 self.delay = 0 self.timeout = 3.0 - ch = logging.StreamHandler() - ch.setLevel(log_lvl) - self.logger.addHandler(ch) + self.ch = logging.StreamHandler() + self.ch.setLevel(log_lvl) + self.logger.addHandler(self.ch) # Check invalid host value and raise exception # Atleast host is required for connection @@ -205,6 +205,8 @@ class SshClient(object): if self.ssh is not None: self.ssh.close() self.ssh = None + if self.ch is not None: + self.logger.removeHandler(self.ch) if __name__ == "__main__": From f27de6364402569fd3c2d1286ed5c749b9bd6e12 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Tue, 12 Jul 2022 17:57:42 +0530 Subject: [PATCH 2/7] saml: Safer DocumentBuilderFactory and ParserPool configuration This implements safer DocumentBuilderFactory and ParserPool utilities to be used throughout the codebase to prevent potential XXE exploits. References: https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html https://www.blackhat.com/docs/us-15/materials/us-15-Wang-FileCry-The-New-Age-Of-XXE-java-wp.pdf Signed-off-by: Rohit Yadav (cherry picked from commit 8e0e68ef368ebe2793ef80e2c3821eaecb47b593) Signed-off-by: Rohit Yadav --- plugins/user-authenticators/saml2/pom.xml | 1 + .../GetServiceProviderMetaDataCmd.java | 3 +- .../cloudstack/saml/SAML2AuthManagerImpl.java | 3 +- .../org/apache/cloudstack/saml/SAMLUtils.java | 22 +++++++++- pom.xml | 2 +- .../utils/security/ParserUtils.java | 41 +++++++++++++++++++ 6 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 utils/src/main/java/org/apache/cloudstack/utils/security/ParserUtils.java diff --git a/plugins/user-authenticators/saml2/pom.xml b/plugins/user-authenticators/saml2/pom.xml index 4b665e9b2bf..9f6cdd7c29c 100644 --- a/plugins/user-authenticators/saml2/pom.xml +++ b/plugins/user-authenticators/saml2/pom.xml @@ -31,6 +31,7 @@ org.opensaml opensaml + ${cs.opensaml.version} org.apache.cloudstack diff --git a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/command/GetServiceProviderMetaDataCmd.java b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/command/GetServiceProviderMetaDataCmd.java index 86034e72a8b..e5f1ade4d55 100644 --- a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/command/GetServiceProviderMetaDataCmd.java +++ b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/command/GetServiceProviderMetaDataCmd.java @@ -30,6 +30,7 @@ import org.apache.cloudstack.api.auth.PluggableAPIAuthenticator; import org.apache.cloudstack.api.response.SAMLMetaDataResponse; import org.apache.cloudstack.saml.SAML2AuthManager; import org.apache.cloudstack.saml.SAMLProviderMetadata; +import org.apache.cloudstack.utils.security.ParserUtils; import org.apache.log4j.Logger; import org.opensaml.Configuration; import org.opensaml.DefaultBootstrap; @@ -239,7 +240,7 @@ public class GetServiceProviderMetaDataCmd extends BaseCmd implements APIAuthent StringWriter stringWriter = new StringWriter(); try { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilderFactory factory = ParserUtils.getSaferDocumentBuilderFactory(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.newDocument(); Marshaller out = Configuration.getMarshallerFactory().getMarshaller(spEntityDescriptor); diff --git a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAML2AuthManagerImpl.java b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAML2AuthManagerImpl.java index f0a5bab4c4a..ba85b151eea 100644 --- a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAML2AuthManagerImpl.java +++ b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAML2AuthManagerImpl.java @@ -78,7 +78,6 @@ import org.opensaml.saml2.metadata.provider.HTTPMetadataProvider; import org.opensaml.saml2.metadata.provider.MetadataProviderException; import org.opensaml.xml.ConfigurationException; import org.opensaml.xml.XMLObject; -import org.opensaml.xml.parse.BasicParserPool; import org.opensaml.xml.security.credential.UsageType; import org.opensaml.xml.security.keyinfo.KeyInfoHelper; import org.springframework.stereotype.Component; @@ -389,7 +388,7 @@ public class SAML2AuthManagerImpl extends AdapterBase implements SAML2AuthManage } } _idpMetaDataProvider.setRequireValidMetadata(true); - _idpMetaDataProvider.setParserPool(new BasicParserPool()); + _idpMetaDataProvider.setParserPool(SAMLUtils.getSaferParserPool()); _idpMetaDataProvider.initialize(); _timer.scheduleAtFixedRate(new MetadataRefreshTask(), 0, _refreshInterval * 1000); diff --git a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAMLUtils.java b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAMLUtils.java index cbbdbd28bf8..a6fcd6bd168 100644 --- a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAMLUtils.java +++ b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAMLUtils.java @@ -42,12 +42,15 @@ import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; +import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; @@ -56,6 +59,7 @@ import javax.xml.stream.FactoryConfigurationError; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.response.LoginCmdResponse; import org.apache.cloudstack.utils.security.CertUtils; +import org.apache.cloudstack.utils.security.ParserUtils; import org.apache.log4j.Logger; import org.bouncycastle.operator.OperatorCreationException; import org.joda.time.DateTime; @@ -88,6 +92,7 @@ import org.opensaml.xml.io.MarshallingException; import org.opensaml.xml.io.Unmarshaller; import org.opensaml.xml.io.UnmarshallerFactory; import org.opensaml.xml.io.UnmarshallingException; +import org.opensaml.xml.parse.BasicParserPool; import org.opensaml.xml.signature.SignatureConstants; import org.opensaml.xml.util.Base64; import org.opensaml.xml.util.XMLHelper; @@ -230,7 +235,7 @@ public class SAMLUtils { public static Response decodeSAMLResponse(String responseMessage) throws ConfigurationException, ParserConfigurationException, SAXException, IOException, UnmarshallingException { - DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilderFactory documentBuilderFactory = ParserUtils.getSaferDocumentBuilderFactory(); documentBuilderFactory.setNamespaceAware(true); DocumentBuilder docBuilder = documentBuilderFactory.newDocumentBuilder(); byte[] base64DecodedResponse = Base64.decode(responseMessage); @@ -364,4 +369,19 @@ public class SAMLUtils { "CN=ApacheCloudStack", "CN=ApacheCloudStack", 3, "SHA256WithRSA"); } + + public static BasicParserPool getSaferParserPool() { + final Map features = new HashMap<>(); + features.put(XMLConstants.FEATURE_SECURE_PROCESSING, true); + features.put("http://apache.org/xml/features/disallow-doctype-decl", true); + features.put("http://xml.org/sax/features/external-general-entities", false); + features.put("http://xml.org/sax/features/external-parameter-entities", false); + features.put("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + final BasicParserPool parserPool = new BasicParserPool(); + parserPool.setXincludeAware(false); + parserPool.setIgnoreComments(true); + parserPool.setExpandEntityReferences(false); + parserPool.setBuilderFeatures(features); + return parserPool; + } } diff --git a/pom.xml b/pom.xml index ec94ec5c50c..7c0a1e18d14 100644 --- a/pom.xml +++ b/pom.xml @@ -163,7 +163,7 @@ 8.0.19 2.0.4 10.1 - 2.6.4 + 2.6.6 0.6.0 0.3.0 0.9.12 diff --git a/utils/src/main/java/org/apache/cloudstack/utils/security/ParserUtils.java b/utils/src/main/java/org/apache/cloudstack/utils/security/ParserUtils.java new file mode 100644 index 00000000000..0e8e9d61740 --- /dev/null +++ b/utils/src/main/java/org/apache/cloudstack/utils/security/ParserUtils.java @@ -0,0 +1,41 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.utils.security; + +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +public class ParserUtils { + public static DocumentBuilderFactory getSaferDocumentBuilderFactory() throws ParserConfigurationException { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + + // REDHAT https://www.blackhat.com/docs/us-15/materials/us-15-Wang-FileCry-The-New-Age-Of-XXE-java-wp.pdf + // OWASP https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html + // and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks" + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + factory.setFeature("http://xml.org/sax/features/external-general-entities", false); + factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + factory.setXIncludeAware(false); + factory.setExpandEntityReferences(false); + + return factory; + } +} From 5bc7024af5ee6e5475a21c8b925d9ae3ffacdaf4 Mon Sep 17 00:00:00 2001 From: Hoang Nguyen Date: Mon, 20 Jun 2022 13:39:13 +0700 Subject: [PATCH 3/7] ui: add a default value for customColumns (#6462) Fixes #6459 (cherry picked from commit c6f12f37eefc036e46583c7ef36f6368742b417d) Signed-off-by: Rohit Yadav --- ui/src/store/modules/user.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/src/store/modules/user.js b/ui/src/store/modules/user.js index 3a5233aa13b..cf4e420df03 100644 --- a/ui/src/store/modules/user.js +++ b/ui/src/store/modules/user.js @@ -58,7 +58,8 @@ const user = { darkMode: false, defaultListViewPageSize: 20, countNotify: 0, - logoutFlag: false + logoutFlag: false, + customColumns: {} }, mutations: { From ce7c3694c82232b5fa08f5a3fa8d5ff2b95542a3 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Thu, 14 Jul 2022 17:36:02 +0530 Subject: [PATCH 4/7] storpool: fix mvn pom.xml build issue Signed-off-by: Rohit Yadav --- plugins/storage/volume/storpool/pom.xml | 135 ++++++++++++------------ 1 file changed, 70 insertions(+), 65 deletions(-) diff --git a/plugins/storage/volume/storpool/pom.xml b/plugins/storage/volume/storpool/pom.xml index 93e9f5dcc31..8d6a9dea345 100644 --- a/plugins/storage/volume/storpool/pom.xml +++ b/plugins/storage/volume/storpool/pom.xml @@ -1,68 +1,73 @@ + license agreements. See the NOTICE file distributed with this work for additional + information regarding copyright ownership. The ASF licenses this file to + you under the Apache License, Version 2.0 (the "License"); you may not use + this file except in compliance with the License. You may obtain a copy of + the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required + by applicable law or agreed to in writing, software distributed under the + License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS + OF ANY KIND, either express or implied. See the License for the specific + language governing permissions and limitations under the License. --> - 4.0.0 - cloud-plugin-storage-volume-storpool - Apache CloudStack Plugin - Storage Volume StorPool provider - - org.apache.cloudstack - cloudstack-plugins - 4.17.0.0 - ../../../pom.xml - - - - org.apache.cloudstack - cloud-engine-storage-volume - ${project.version} - - - org.apache.cloudstack - cloud-engine-storage-snapshot - ${project.version} - - - org.apache.cloudstack - cloud-plugin-hypervisor-kvm - ${project.version} - - - org.apache.cloudstack - cloud-engine-orchestration - ${project.version} - - - org.apache.commons - commons-collections4 - 4.4 - - - - - - maven-surefire-plugin - - true - - - - integration-test - - test - - - - - - + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + cloud-plugin-storage-volume-storpool + Apache CloudStack Plugin - Storage Volume StorPool provider + + org.apache.cloudstack + cloudstack-plugins + 4.17.0.0 + ../../../pom.xml + + + + org.apache.cloudstack + cloud-engine-storage-volume + ${project.version} + + + org.apache.cloudstack + cloud-engine-storage-snapshot + ${project.version} + + + org.apache.cloudstack + cloud-plugin-hypervisor-kvm + ${project.version} + + + org.apache.cloudstack + cloud-engine-orchestration + ${project.version} + + + ch.qos.reload4j + reload4j + ${cs.reload4j.version} + + + org.apache.commons + commons-collections4 + 4.4 + + + + + + maven-surefire-plugin + + true + + + + integration-test + + test + + + + + + From c09ee40984b1ea64591bf06a07f972000db16a8a Mon Sep 17 00:00:00 2001 From: Hoang Nguyen Date: Fri, 15 Jul 2022 18:31:58 +0700 Subject: [PATCH 5/7] Fix can't select schedule interval type in configure backup schedule (#6547) --- ui/src/views/compute/backup/FormSchedule.vue | 3 --- 1 file changed, 3 deletions(-) diff --git a/ui/src/views/compute/backup/FormSchedule.vue b/ui/src/views/compute/backup/FormSchedule.vue index eb059efa772..d1512623099 100644 --- a/ui/src/views/compute/backup/FormSchedule.vue +++ b/ui/src/views/compute/backup/FormSchedule.vue @@ -226,9 +226,6 @@ export default { } }, handleChangeIntervalType (e) { - this.form.intervaltype = e.target.value - this.resetForm() - switch (this.form.intervaltype) { case 'weekly': this.fetchDayOfWeek() From b30a4a99d1b530efbf652373eda229f2cd5133b1 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Fri, 15 Jul 2022 18:16:45 +0530 Subject: [PATCH 6/7] engine/schema: bake-in upgrade path from 4.16.1.1 to 4.17.0.1 Signed-off-by: Rohit Yadav --- .../src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java index 1a189c0d296..47d059e71c1 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java @@ -205,6 +205,7 @@ public class DatabaseUpgradeChecker implements SystemIntegrityChecker { .next("4.15.2.0", new Upgrade41520to41600()) .next("4.16.0.0", new Upgrade41600to41610()) .next("4.16.1.0", new Upgrade41610to41700()) + .next("4.16.1.1", new Upgrade41610to41700()) .build(); } From 1c7efcbd0d574637d522178c900a9c13732639b4 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Fri, 15 Jul 2022 18:18:40 +0530 Subject: [PATCH 7/7] Updating pom.xml version numbers for release 4.17.0.1 Signed-off-by: Rohit Yadav --- agent/pom.xml | 2 +- api/pom.xml | 2 +- client/pom.xml | 2 +- core/pom.xml | 2 +- debian/changelog | 6 ++++++ developer/pom.xml | 2 +- engine/api/pom.xml | 2 +- engine/components-api/pom.xml | 2 +- engine/network/pom.xml | 2 +- engine/orchestration/pom.xml | 2 +- engine/pom.xml | 2 +- engine/schema/pom.xml | 2 +- engine/service/pom.xml | 2 +- engine/storage/cache/pom.xml | 2 +- engine/storage/configdrive/pom.xml | 2 +- engine/storage/datamotion/pom.xml | 2 +- engine/storage/image/pom.xml | 2 +- engine/storage/integration-test/pom.xml | 2 +- engine/storage/pom.xml | 2 +- engine/storage/snapshot/pom.xml | 2 +- engine/storage/volume/pom.xml | 2 +- framework/agent-lb/pom.xml | 2 +- framework/ca/pom.xml | 2 +- framework/cluster/pom.xml | 2 +- framework/config/pom.xml | 2 +- framework/db/pom.xml | 2 +- framework/direct-download/pom.xml | 4 ++-- framework/events/pom.xml | 2 +- framework/ipc/pom.xml | 2 +- framework/jobs/pom.xml | 2 +- framework/managed-context/pom.xml | 2 +- framework/pom.xml | 2 +- framework/quota/pom.xml | 2 +- framework/rest/pom.xml | 2 +- framework/security/pom.xml | 2 +- framework/spring/lifecycle/pom.xml | 2 +- framework/spring/module/pom.xml | 2 +- plugins/acl/dynamic-role-based/pom.xml | 2 +- plugins/acl/project-role-based/pom.xml | 2 +- plugins/acl/static-role-based/pom.xml | 2 +- .../affinity-group-processors/explicit-dedication/pom.xml | 2 +- plugins/affinity-group-processors/host-affinity/pom.xml | 2 +- .../affinity-group-processors/host-anti-affinity/pom.xml | 2 +- plugins/alert-handlers/snmp-alerts/pom.xml | 2 +- plugins/alert-handlers/syslog-alerts/pom.xml | 2 +- plugins/api/discovery/pom.xml | 2 +- plugins/api/rate-limit/pom.xml | 2 +- plugins/api/solidfire-intg-test/pom.xml | 2 +- plugins/api/vmware-sioc/pom.xml | 2 +- plugins/backup/dummy/pom.xml | 2 +- plugins/backup/veeam/pom.xml | 2 +- plugins/ca/root-ca/pom.xml | 2 +- plugins/database/mysql-ha/pom.xml | 2 +- plugins/database/quota/pom.xml | 2 +- plugins/dedicated-resources/pom.xml | 2 +- plugins/deployment-planners/implicit-dedication/pom.xml | 2 +- plugins/deployment-planners/user-concentrated-pod/pom.xml | 2 +- plugins/deployment-planners/user-dispersing/pom.xml | 2 +- plugins/event-bus/inmemory/pom.xml | 2 +- plugins/event-bus/kafka/pom.xml | 2 +- plugins/event-bus/rabbitmq/pom.xml | 2 +- plugins/ha-planners/skip-heurestics/pom.xml | 2 +- plugins/host-allocators/random/pom.xml | 2 +- plugins/hypervisors/baremetal/pom.xml | 2 +- plugins/hypervisors/hyperv/pom.xml | 2 +- plugins/hypervisors/kvm/pom.xml | 2 +- plugins/hypervisors/ovm/pom.xml | 2 +- plugins/hypervisors/ovm3/pom.xml | 2 +- plugins/hypervisors/simulator/pom.xml | 2 +- plugins/hypervisors/ucs/pom.xml | 2 +- plugins/hypervisors/vmware/pom.xml | 2 +- plugins/hypervisors/xenserver/pom.xml | 2 +- plugins/integrations/cloudian/pom.xml | 2 +- plugins/integrations/kubernetes-service/pom.xml | 2 +- plugins/integrations/prometheus/pom.xml | 2 +- plugins/metrics/pom.xml | 2 +- plugins/network-elements/bigswitch/pom.xml | 2 +- plugins/network-elements/brocade-vcs/pom.xml | 2 +- plugins/network-elements/cisco-vnmc/pom.xml | 2 +- plugins/network-elements/dns-notifier/pom.xml | 2 +- plugins/network-elements/elastic-loadbalancer/pom.xml | 2 +- plugins/network-elements/globodns/pom.xml | 2 +- plugins/network-elements/internal-loadbalancer/pom.xml | 2 +- plugins/network-elements/juniper-contrail/pom.xml | 2 +- plugins/network-elements/juniper-srx/pom.xml | 2 +- plugins/network-elements/netscaler/pom.xml | 2 +- plugins/network-elements/nicira-nvp/pom.xml | 2 +- plugins/network-elements/opendaylight/pom.xml | 2 +- plugins/network-elements/ovs/pom.xml | 2 +- plugins/network-elements/palo-alto/pom.xml | 2 +- plugins/network-elements/stratosphere-ssp/pom.xml | 2 +- plugins/network-elements/vxlan/pom.xml | 2 +- plugins/outofbandmanagement-drivers/ipmitool/pom.xml | 2 +- .../outofbandmanagement-drivers/nested-cloudstack/pom.xml | 2 +- plugins/outofbandmanagement-drivers/redfish/pom.xml | 2 +- plugins/pom.xml | 2 +- plugins/storage-allocators/random/pom.xml | 2 +- plugins/storage/image/default/pom.xml | 2 +- plugins/storage/image/s3/pom.xml | 2 +- plugins/storage/image/sample/pom.xml | 2 +- plugins/storage/image/swift/pom.xml | 2 +- plugins/storage/volume/cloudbyte/pom.xml | 2 +- plugins/storage/volume/datera/pom.xml | 2 +- plugins/storage/volume/default/pom.xml | 2 +- plugins/storage/volume/linstor/pom.xml | 2 +- plugins/storage/volume/nexenta/pom.xml | 2 +- plugins/storage/volume/sample/pom.xml | 2 +- plugins/storage/volume/scaleio/pom.xml | 2 +- plugins/storage/volume/solidfire/pom.xml | 2 +- plugins/storage/volume/storpool/pom.xml | 2 +- plugins/user-authenticators/ldap/pom.xml | 2 +- plugins/user-authenticators/md5/pom.xml | 2 +- plugins/user-authenticators/pbkdf2/pom.xml | 2 +- plugins/user-authenticators/plain-text/pom.xml | 2 +- plugins/user-authenticators/saml2/pom.xml | 2 +- plugins/user-authenticators/sha256salted/pom.xml | 2 +- pom.xml | 2 +- quickcloud/pom.xml | 2 +- server/pom.xml | 2 +- services/console-proxy/pom.xml | 2 +- services/console-proxy/rdpconsole/pom.xml | 2 +- services/console-proxy/server/pom.xml | 2 +- services/pom.xml | 2 +- services/secondary-storage/controller/pom.xml | 2 +- services/secondary-storage/pom.xml | 2 +- services/secondary-storage/server/pom.xml | 2 +- systemvm/pom.xml | 2 +- test/pom.xml | 2 +- tools/apidoc/pom.xml | 2 +- tools/checkstyle/pom.xml | 2 +- tools/devcloud-kvm/pom.xml | 2 +- tools/devcloud4/pom.xml | 2 +- tools/marvin/pom.xml | 2 +- tools/pom.xml | 2 +- usage/pom.xml | 2 +- utils/pom.xml | 2 +- vmware-base/pom.xml | 2 +- 137 files changed, 143 insertions(+), 137 deletions(-) diff --git a/agent/pom.xml b/agent/pom.xml index 65324390ab2..320c0efa2f0 100644 --- a/agent/pom.xml +++ b/agent/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.17.0.0 + 4.17.0.1 diff --git a/api/pom.xml b/api/pom.xml index 91e67ab2a74..3220822422c 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.17.0.0 + 4.17.0.1 diff --git a/client/pom.xml b/client/pom.xml index 4e97f8f159f..88098014c05 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.17.0.0 + 4.17.0.1 diff --git a/core/pom.xml b/core/pom.xml index 4f8d6a4f9a4..f2c5f8af524 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.17.0.0 + 4.17.0.1 diff --git a/debian/changelog b/debian/changelog index 0ea593f4988..01cbe7404b8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +cloudstack (4.17.0.1) unstable; urgency=low + + * Update the version to 4.17.0.1 + + -- the Apache CloudStack project Fri, 15 Jul 2022 18:18:39 +0530 + cloudstack (4.17.0.0) unstable; urgency=low * Update the version to 4.17.0.0 diff --git a/developer/pom.xml b/developer/pom.xml index 7cbb8c0f2b0..a33738830ef 100644 --- a/developer/pom.xml +++ b/developer/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.17.0.0 + 4.17.0.1 diff --git a/engine/api/pom.xml b/engine/api/pom.xml index 4ffa76425eb..288631c97fc 100644 --- a/engine/api/pom.xml +++ b/engine/api/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/engine/components-api/pom.xml b/engine/components-api/pom.xml index 3beb2692b27..73e2cedfc99 100644 --- a/engine/components-api/pom.xml +++ b/engine/components-api/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/engine/network/pom.xml b/engine/network/pom.xml index cfa52411f1d..7f1bec21751 100644 --- a/engine/network/pom.xml +++ b/engine/network/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/engine/orchestration/pom.xml b/engine/orchestration/pom.xml index 7a5e92239e0..68e70d3c324 100755 --- a/engine/orchestration/pom.xml +++ b/engine/orchestration/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/engine/pom.xml b/engine/pom.xml index ad1bb7a829c..ee20f2865b7 100644 --- a/engine/pom.xml +++ b/engine/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/engine/schema/pom.xml b/engine/schema/pom.xml index 4cf49fc1dfa..0ab24247441 100644 --- a/engine/schema/pom.xml +++ b/engine/schema/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/engine/service/pom.xml b/engine/service/pom.xml index 0fd7ac1f747..55f78a920b3 100644 --- a/engine/service/pom.xml +++ b/engine/service/pom.xml @@ -22,7 +22,7 @@ org.apache.cloudstack cloud-engine - 4.17.0.0 + 4.17.0.1 cloud-engine-service war diff --git a/engine/storage/cache/pom.xml b/engine/storage/cache/pom.xml index d4a849c1c89..b282559344c 100644 --- a/engine/storage/cache/pom.xml +++ b/engine/storage/cache/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/engine/storage/configdrive/pom.xml b/engine/storage/configdrive/pom.xml index 38b535d3e68..93b6d88463f 100644 --- a/engine/storage/configdrive/pom.xml +++ b/engine/storage/configdrive/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/engine/storage/datamotion/pom.xml b/engine/storage/datamotion/pom.xml index ae65f917a20..b0ef0b18ada 100644 --- a/engine/storage/datamotion/pom.xml +++ b/engine/storage/datamotion/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/engine/storage/image/pom.xml b/engine/storage/image/pom.xml index b98bdc4f96d..3e773a45acb 100644 --- a/engine/storage/image/pom.xml +++ b/engine/storage/image/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/engine/storage/integration-test/pom.xml b/engine/storage/integration-test/pom.xml index 3f89607f03b..4d521dfd2f0 100644 --- a/engine/storage/integration-test/pom.xml +++ b/engine/storage/integration-test/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/engine/storage/pom.xml b/engine/storage/pom.xml index 35b90d7bf3e..0228d95de26 100644 --- a/engine/storage/pom.xml +++ b/engine/storage/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/engine/storage/snapshot/pom.xml b/engine/storage/snapshot/pom.xml index 30167af0532..fa5d388ecc2 100644 --- a/engine/storage/snapshot/pom.xml +++ b/engine/storage/snapshot/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/engine/storage/volume/pom.xml b/engine/storage/volume/pom.xml index e757b2d06d7..cf7072ef77d 100644 --- a/engine/storage/volume/pom.xml +++ b/engine/storage/volume/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/framework/agent-lb/pom.xml b/framework/agent-lb/pom.xml index 457b71641b7..e495c43db42 100644 --- a/framework/agent-lb/pom.xml +++ b/framework/agent-lb/pom.xml @@ -24,7 +24,7 @@ cloudstack-framework org.apache.cloudstack - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/framework/ca/pom.xml b/framework/ca/pom.xml index 0118401dff2..0faefc38b3c 100644 --- a/framework/ca/pom.xml +++ b/framework/ca/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/framework/cluster/pom.xml b/framework/cluster/pom.xml index 84437a38e09..94c7799b481 100644 --- a/framework/cluster/pom.xml +++ b/framework/cluster/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/framework/config/pom.xml b/framework/config/pom.xml index 56d1d736a5f..5694c3b048a 100644 --- a/framework/config/pom.xml +++ b/framework/config/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/framework/db/pom.xml b/framework/db/pom.xml index 72bc633d67b..2be0e3660d8 100644 --- a/framework/db/pom.xml +++ b/framework/db/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/framework/direct-download/pom.xml b/framework/direct-download/pom.xml index 0fc09790e75..fd0846e4da9 100644 --- a/framework/direct-download/pom.xml +++ b/framework/direct-download/pom.xml @@ -25,14 +25,14 @@ org.apache.cloudstack cloud-utils - 4.17.0.0 + 4.17.0.1 compile cloudstack-framework org.apache.cloudstack - 4.17.0.0 + 4.17.0.1 ../pom.xml \ No newline at end of file diff --git a/framework/events/pom.xml b/framework/events/pom.xml index 9651e084eb0..b93e599e8b7 100644 --- a/framework/events/pom.xml +++ b/framework/events/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/framework/ipc/pom.xml b/framework/ipc/pom.xml index 9db9ec8ad29..a59ce4a2b84 100644 --- a/framework/ipc/pom.xml +++ b/framework/ipc/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/framework/jobs/pom.xml b/framework/jobs/pom.xml index d07a4d07dbd..940e4cf15c3 100644 --- a/framework/jobs/pom.xml +++ b/framework/jobs/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/framework/managed-context/pom.xml b/framework/managed-context/pom.xml index 43c77510f6f..6b1db71be04 100644 --- a/framework/managed-context/pom.xml +++ b/framework/managed-context/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/framework/pom.xml b/framework/pom.xml index 2ab92cc8264..0bae4dcd2f3 100644 --- a/framework/pom.xml +++ b/framework/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.17.0.0 + 4.17.0.1 diff --git a/framework/quota/pom.xml b/framework/quota/pom.xml index ba0e010605d..16844af9785 100644 --- a/framework/quota/pom.xml +++ b/framework/quota/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/framework/rest/pom.xml b/framework/rest/pom.xml index 49938d0b346..ca0e8de083b 100644 --- a/framework/rest/pom.xml +++ b/framework/rest/pom.xml @@ -22,7 +22,7 @@ org.apache.cloudstack cloudstack-framework - 4.17.0.0 + 4.17.0.1 ../pom.xml cloud-framework-rest diff --git a/framework/security/pom.xml b/framework/security/pom.xml index cdeebfcb298..39c96bebb4a 100644 --- a/framework/security/pom.xml +++ b/framework/security/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/framework/spring/lifecycle/pom.xml b/framework/spring/lifecycle/pom.xml index 450cff6866c..6d39717e255 100644 --- a/framework/spring/lifecycle/pom.xml +++ b/framework/spring/lifecycle/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.17.0.0 + 4.17.0.1 ../../../pom.xml diff --git a/framework/spring/module/pom.xml b/framework/spring/module/pom.xml index c022f1d3aa5..20bc2f9daff 100644 --- a/framework/spring/module/pom.xml +++ b/framework/spring/module/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.17.0.0 + 4.17.0.1 ../../../pom.xml diff --git a/plugins/acl/dynamic-role-based/pom.xml b/plugins/acl/dynamic-role-based/pom.xml index 98d8ca8bdb8..0b0cab3b59f 100644 --- a/plugins/acl/dynamic-role-based/pom.xml +++ b/plugins/acl/dynamic-role-based/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/acl/project-role-based/pom.xml b/plugins/acl/project-role-based/pom.xml index e51d24c9b6a..d33c4d1d9fa 100644 --- a/plugins/acl/project-role-based/pom.xml +++ b/plugins/acl/project-role-based/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/acl/static-role-based/pom.xml b/plugins/acl/static-role-based/pom.xml index 37d5f7e5987..8807247fce1 100644 --- a/plugins/acl/static-role-based/pom.xml +++ b/plugins/acl/static-role-based/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/affinity-group-processors/explicit-dedication/pom.xml b/plugins/affinity-group-processors/explicit-dedication/pom.xml index 756290f27ae..9a30dac61f6 100644 --- a/plugins/affinity-group-processors/explicit-dedication/pom.xml +++ b/plugins/affinity-group-processors/explicit-dedication/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/affinity-group-processors/host-affinity/pom.xml b/plugins/affinity-group-processors/host-affinity/pom.xml index 9651a7f13b9..8f332569607 100644 --- a/plugins/affinity-group-processors/host-affinity/pom.xml +++ b/plugins/affinity-group-processors/host-affinity/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/affinity-group-processors/host-anti-affinity/pom.xml b/plugins/affinity-group-processors/host-anti-affinity/pom.xml index d30421e419d..83c13007581 100644 --- a/plugins/affinity-group-processors/host-anti-affinity/pom.xml +++ b/plugins/affinity-group-processors/host-anti-affinity/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/alert-handlers/snmp-alerts/pom.xml b/plugins/alert-handlers/snmp-alerts/pom.xml index aa8acefd5b8..f476afe6831 100644 --- a/plugins/alert-handlers/snmp-alerts/pom.xml +++ b/plugins/alert-handlers/snmp-alerts/pom.xml @@ -24,7 +24,7 @@ cloudstack-plugins org.apache.cloudstack - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/alert-handlers/syslog-alerts/pom.xml b/plugins/alert-handlers/syslog-alerts/pom.xml index 11136af99fa..37a393c48fe 100644 --- a/plugins/alert-handlers/syslog-alerts/pom.xml +++ b/plugins/alert-handlers/syslog-alerts/pom.xml @@ -24,7 +24,7 @@ cloudstack-plugins org.apache.cloudstack - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/api/discovery/pom.xml b/plugins/api/discovery/pom.xml index 567c7f0487c..2d0ebcf1341 100644 --- a/plugins/api/discovery/pom.xml +++ b/plugins/api/discovery/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/api/rate-limit/pom.xml b/plugins/api/rate-limit/pom.xml index 24e0f4f64f9..19b1b13782c 100644 --- a/plugins/api/rate-limit/pom.xml +++ b/plugins/api/rate-limit/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/api/solidfire-intg-test/pom.xml b/plugins/api/solidfire-intg-test/pom.xml index b0986ab6d39..202afcfb5f0 100644 --- a/plugins/api/solidfire-intg-test/pom.xml +++ b/plugins/api/solidfire-intg-test/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/api/vmware-sioc/pom.xml b/plugins/api/vmware-sioc/pom.xml index bb5eae6bdfb..2e462df31a5 100644 --- a/plugins/api/vmware-sioc/pom.xml +++ b/plugins/api/vmware-sioc/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/backup/dummy/pom.xml b/plugins/backup/dummy/pom.xml index 0327b4410cf..4eba210ec43 100644 --- a/plugins/backup/dummy/pom.xml +++ b/plugins/backup/dummy/pom.xml @@ -23,7 +23,7 @@ cloudstack-plugins org.apache.cloudstack - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/backup/veeam/pom.xml b/plugins/backup/veeam/pom.xml index c43eba4a9de..d738d4b8d9c 100644 --- a/plugins/backup/veeam/pom.xml +++ b/plugins/backup/veeam/pom.xml @@ -23,7 +23,7 @@ cloudstack-plugins org.apache.cloudstack - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/ca/root-ca/pom.xml b/plugins/ca/root-ca/pom.xml index dca56370869..08bba30a7b1 100644 --- a/plugins/ca/root-ca/pom.xml +++ b/plugins/ca/root-ca/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/database/mysql-ha/pom.xml b/plugins/database/mysql-ha/pom.xml index b395f41b6ab..9ad18ee21f7 100644 --- a/plugins/database/mysql-ha/pom.xml +++ b/plugins/database/mysql-ha/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/database/quota/pom.xml b/plugins/database/quota/pom.xml index cbc92fdd4a1..04591229a51 100644 --- a/plugins/database/quota/pom.xml +++ b/plugins/database/quota/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/dedicated-resources/pom.xml b/plugins/dedicated-resources/pom.xml index 4cfc6ecd81f..bd8e381f8ef 100644 --- a/plugins/dedicated-resources/pom.xml +++ b/plugins/dedicated-resources/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/plugins/deployment-planners/implicit-dedication/pom.xml b/plugins/deployment-planners/implicit-dedication/pom.xml index bc99b1ebf0d..db13f92bdae 100644 --- a/plugins/deployment-planners/implicit-dedication/pom.xml +++ b/plugins/deployment-planners/implicit-dedication/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/deployment-planners/user-concentrated-pod/pom.xml b/plugins/deployment-planners/user-concentrated-pod/pom.xml index a4f045f4c76..34b3b5de3cb 100644 --- a/plugins/deployment-planners/user-concentrated-pod/pom.xml +++ b/plugins/deployment-planners/user-concentrated-pod/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/deployment-planners/user-dispersing/pom.xml b/plugins/deployment-planners/user-dispersing/pom.xml index f961f6ddc9f..f49f2a358a3 100644 --- a/plugins/deployment-planners/user-dispersing/pom.xml +++ b/plugins/deployment-planners/user-dispersing/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/event-bus/inmemory/pom.xml b/plugins/event-bus/inmemory/pom.xml index cc35ca4aa49..62f7738089e 100644 --- a/plugins/event-bus/inmemory/pom.xml +++ b/plugins/event-bus/inmemory/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/event-bus/kafka/pom.xml b/plugins/event-bus/kafka/pom.xml index 09c0aac0d2d..e54104b8b15 100644 --- a/plugins/event-bus/kafka/pom.xml +++ b/plugins/event-bus/kafka/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/event-bus/rabbitmq/pom.xml b/plugins/event-bus/rabbitmq/pom.xml index 4679ddc649f..71ba1ec5564 100644 --- a/plugins/event-bus/rabbitmq/pom.xml +++ b/plugins/event-bus/rabbitmq/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/ha-planners/skip-heurestics/pom.xml b/plugins/ha-planners/skip-heurestics/pom.xml index f31000d3d23..cfe695c6fb3 100644 --- a/plugins/ha-planners/skip-heurestics/pom.xml +++ b/plugins/ha-planners/skip-heurestics/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/host-allocators/random/pom.xml b/plugins/host-allocators/random/pom.xml index e56801f7ea5..eb82fdabf92 100644 --- a/plugins/host-allocators/random/pom.xml +++ b/plugins/host-allocators/random/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/hypervisors/baremetal/pom.xml b/plugins/hypervisors/baremetal/pom.xml index 7cf9c39d528..8327d490477 100755 --- a/plugins/hypervisors/baremetal/pom.xml +++ b/plugins/hypervisors/baremetal/pom.xml @@ -22,7 +22,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml cloud-plugin-hypervisor-baremetal diff --git a/plugins/hypervisors/hyperv/pom.xml b/plugins/hypervisors/hyperv/pom.xml index 756d8d1092e..2a4ab05d012 100644 --- a/plugins/hypervisors/hyperv/pom.xml +++ b/plugins/hypervisors/hyperv/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/hypervisors/kvm/pom.xml b/plugins/hypervisors/kvm/pom.xml index 20884737310..1fada1f06e3 100644 --- a/plugins/hypervisors/kvm/pom.xml +++ b/plugins/hypervisors/kvm/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/hypervisors/ovm/pom.xml b/plugins/hypervisors/ovm/pom.xml index 6940f664448..8d97f98a956 100644 --- a/plugins/hypervisors/ovm/pom.xml +++ b/plugins/hypervisors/ovm/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/hypervisors/ovm3/pom.xml b/plugins/hypervisors/ovm3/pom.xml index aef52fc02a1..fe6bf7fb461 100644 --- a/plugins/hypervisors/ovm3/pom.xml +++ b/plugins/hypervisors/ovm3/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/hypervisors/simulator/pom.xml b/plugins/hypervisors/simulator/pom.xml index adba26c5939..f669498c581 100644 --- a/plugins/hypervisors/simulator/pom.xml +++ b/plugins/hypervisors/simulator/pom.xml @@ -22,7 +22,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml cloud-plugin-hypervisor-simulator diff --git a/plugins/hypervisors/ucs/pom.xml b/plugins/hypervisors/ucs/pom.xml index 5d0269193f4..e999b256f88 100644 --- a/plugins/hypervisors/ucs/pom.xml +++ b/plugins/hypervisors/ucs/pom.xml @@ -23,7 +23,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml cloud-plugin-hypervisor-ucs diff --git a/plugins/hypervisors/vmware/pom.xml b/plugins/hypervisors/vmware/pom.xml index 28491aca8c1..2cbc47ae1b5 100644 --- a/plugins/hypervisors/vmware/pom.xml +++ b/plugins/hypervisors/vmware/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/hypervisors/xenserver/pom.xml b/plugins/hypervisors/xenserver/pom.xml index 759c090ef5b..d73eebad88d 100644 --- a/plugins/hypervisors/xenserver/pom.xml +++ b/plugins/hypervisors/xenserver/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/integrations/cloudian/pom.xml b/plugins/integrations/cloudian/pom.xml index 676f1c0ea62..976617ce12d 100644 --- a/plugins/integrations/cloudian/pom.xml +++ b/plugins/integrations/cloudian/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/integrations/kubernetes-service/pom.xml b/plugins/integrations/kubernetes-service/pom.xml index d764ee89ebd..f461cd07cf2 100644 --- a/plugins/integrations/kubernetes-service/pom.xml +++ b/plugins/integrations/kubernetes-service/pom.xml @@ -26,7 +26,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/integrations/prometheus/pom.xml b/plugins/integrations/prometheus/pom.xml index 40b7d570800..044588cc81b 100644 --- a/plugins/integrations/prometheus/pom.xml +++ b/plugins/integrations/prometheus/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/metrics/pom.xml b/plugins/metrics/pom.xml index 790697de91d..0138e6d1d28 100644 --- a/plugins/metrics/pom.xml +++ b/plugins/metrics/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/plugins/network-elements/bigswitch/pom.xml b/plugins/network-elements/bigswitch/pom.xml index 1b93ad7e885..078b22d6d86 100644 --- a/plugins/network-elements/bigswitch/pom.xml +++ b/plugins/network-elements/bigswitch/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/network-elements/brocade-vcs/pom.xml b/plugins/network-elements/brocade-vcs/pom.xml index 5deaf824928..e6250afd9c7 100644 --- a/plugins/network-elements/brocade-vcs/pom.xml +++ b/plugins/network-elements/brocade-vcs/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/network-elements/cisco-vnmc/pom.xml b/plugins/network-elements/cisco-vnmc/pom.xml index cb23850774e..374994ad006 100644 --- a/plugins/network-elements/cisco-vnmc/pom.xml +++ b/plugins/network-elements/cisco-vnmc/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/network-elements/dns-notifier/pom.xml b/plugins/network-elements/dns-notifier/pom.xml index ab25d8b1d06..770bc781d5e 100644 --- a/plugins/network-elements/dns-notifier/pom.xml +++ b/plugins/network-elements/dns-notifier/pom.xml @@ -22,7 +22,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml cloud-plugin-example-dns-notifier diff --git a/plugins/network-elements/elastic-loadbalancer/pom.xml b/plugins/network-elements/elastic-loadbalancer/pom.xml index 066a46b6334..19a1ef9b2fb 100644 --- a/plugins/network-elements/elastic-loadbalancer/pom.xml +++ b/plugins/network-elements/elastic-loadbalancer/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/network-elements/globodns/pom.xml b/plugins/network-elements/globodns/pom.xml index 73071628d5b..f1a244ba83d 100644 --- a/plugins/network-elements/globodns/pom.xml +++ b/plugins/network-elements/globodns/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/network-elements/internal-loadbalancer/pom.xml b/plugins/network-elements/internal-loadbalancer/pom.xml index 94259681770..cddd877d76f 100644 --- a/plugins/network-elements/internal-loadbalancer/pom.xml +++ b/plugins/network-elements/internal-loadbalancer/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/network-elements/juniper-contrail/pom.xml b/plugins/network-elements/juniper-contrail/pom.xml index 01381945ad2..6fa5eb70235 100644 --- a/plugins/network-elements/juniper-contrail/pom.xml +++ b/plugins/network-elements/juniper-contrail/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/network-elements/juniper-srx/pom.xml b/plugins/network-elements/juniper-srx/pom.xml index c393da5ef6d..0161d3b0aee 100644 --- a/plugins/network-elements/juniper-srx/pom.xml +++ b/plugins/network-elements/juniper-srx/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/network-elements/netscaler/pom.xml b/plugins/network-elements/netscaler/pom.xml index 27a2429c054..44898f796e6 100644 --- a/plugins/network-elements/netscaler/pom.xml +++ b/plugins/network-elements/netscaler/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/network-elements/nicira-nvp/pom.xml b/plugins/network-elements/nicira-nvp/pom.xml index 3c430539f38..f1f9a96f791 100644 --- a/plugins/network-elements/nicira-nvp/pom.xml +++ b/plugins/network-elements/nicira-nvp/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/network-elements/opendaylight/pom.xml b/plugins/network-elements/opendaylight/pom.xml index a661d98b622..5983501cf97 100644 --- a/plugins/network-elements/opendaylight/pom.xml +++ b/plugins/network-elements/opendaylight/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/network-elements/ovs/pom.xml b/plugins/network-elements/ovs/pom.xml index b9eb8f49fc9..cbdfa26c9ee 100644 --- a/plugins/network-elements/ovs/pom.xml +++ b/plugins/network-elements/ovs/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/network-elements/palo-alto/pom.xml b/plugins/network-elements/palo-alto/pom.xml index 7d46356a362..f6690063f83 100644 --- a/plugins/network-elements/palo-alto/pom.xml +++ b/plugins/network-elements/palo-alto/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/network-elements/stratosphere-ssp/pom.xml b/plugins/network-elements/stratosphere-ssp/pom.xml index 36949ae19d3..e1980b33ac0 100644 --- a/plugins/network-elements/stratosphere-ssp/pom.xml +++ b/plugins/network-elements/stratosphere-ssp/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/network-elements/vxlan/pom.xml b/plugins/network-elements/vxlan/pom.xml index 29e3754b70a..452781cb0b2 100644 --- a/plugins/network-elements/vxlan/pom.xml +++ b/plugins/network-elements/vxlan/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/outofbandmanagement-drivers/ipmitool/pom.xml b/plugins/outofbandmanagement-drivers/ipmitool/pom.xml index 33aaaf308f6..56af4795945 100644 --- a/plugins/outofbandmanagement-drivers/ipmitool/pom.xml +++ b/plugins/outofbandmanagement-drivers/ipmitool/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/outofbandmanagement-drivers/nested-cloudstack/pom.xml b/plugins/outofbandmanagement-drivers/nested-cloudstack/pom.xml index 3721365964e..5ca4b0e6723 100644 --- a/plugins/outofbandmanagement-drivers/nested-cloudstack/pom.xml +++ b/plugins/outofbandmanagement-drivers/nested-cloudstack/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/outofbandmanagement-drivers/redfish/pom.xml b/plugins/outofbandmanagement-drivers/redfish/pom.xml index 85b84f5ac0c..72b6cbfcda1 100644 --- a/plugins/outofbandmanagement-drivers/redfish/pom.xml +++ b/plugins/outofbandmanagement-drivers/redfish/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/pom.xml b/plugins/pom.xml index 96d5a0871ab..5b44f4fef50 100755 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.17.0.0 + 4.17.0.1 diff --git a/plugins/storage-allocators/random/pom.xml b/plugins/storage-allocators/random/pom.xml index ecacbff530b..cc94242454f 100644 --- a/plugins/storage-allocators/random/pom.xml +++ b/plugins/storage-allocators/random/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/storage/image/default/pom.xml b/plugins/storage/image/default/pom.xml index 754610a2beb..3059903cda1 100644 --- a/plugins/storage/image/default/pom.xml +++ b/plugins/storage/image/default/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../../pom.xml diff --git a/plugins/storage/image/s3/pom.xml b/plugins/storage/image/s3/pom.xml index 12bc65903c5..df9254bd736 100644 --- a/plugins/storage/image/s3/pom.xml +++ b/plugins/storage/image/s3/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../../pom.xml diff --git a/plugins/storage/image/sample/pom.xml b/plugins/storage/image/sample/pom.xml index 303b2bcb6a4..ade277d12d7 100644 --- a/plugins/storage/image/sample/pom.xml +++ b/plugins/storage/image/sample/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../../pom.xml diff --git a/plugins/storage/image/swift/pom.xml b/plugins/storage/image/swift/pom.xml index 6d280d21f37..c0ef53d1387 100644 --- a/plugins/storage/image/swift/pom.xml +++ b/plugins/storage/image/swift/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../../pom.xml diff --git a/plugins/storage/volume/cloudbyte/pom.xml b/plugins/storage/volume/cloudbyte/pom.xml index 939c23f4d8a..408382d2c22 100644 --- a/plugins/storage/volume/cloudbyte/pom.xml +++ b/plugins/storage/volume/cloudbyte/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../../pom.xml diff --git a/plugins/storage/volume/datera/pom.xml b/plugins/storage/volume/datera/pom.xml index cf1825b777b..cca09f1eb9e 100644 --- a/plugins/storage/volume/datera/pom.xml +++ b/plugins/storage/volume/datera/pom.xml @@ -16,7 +16,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../../pom.xml diff --git a/plugins/storage/volume/default/pom.xml b/plugins/storage/volume/default/pom.xml index 8c5d5640648..baf05bb9297 100644 --- a/plugins/storage/volume/default/pom.xml +++ b/plugins/storage/volume/default/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../../pom.xml diff --git a/plugins/storage/volume/linstor/pom.xml b/plugins/storage/volume/linstor/pom.xml index 716459b1fa5..d5f3270855c 100644 --- a/plugins/storage/volume/linstor/pom.xml +++ b/plugins/storage/volume/linstor/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../../pom.xml diff --git a/plugins/storage/volume/nexenta/pom.xml b/plugins/storage/volume/nexenta/pom.xml index 0a2609fb85b..a9e5d423644 100644 --- a/plugins/storage/volume/nexenta/pom.xml +++ b/plugins/storage/volume/nexenta/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../../pom.xml diff --git a/plugins/storage/volume/sample/pom.xml b/plugins/storage/volume/sample/pom.xml index 669d40c3ed7..18ebab22492 100644 --- a/plugins/storage/volume/sample/pom.xml +++ b/plugins/storage/volume/sample/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../../pom.xml diff --git a/plugins/storage/volume/scaleio/pom.xml b/plugins/storage/volume/scaleio/pom.xml index 1de3256d53a..0d3ac7d499d 100644 --- a/plugins/storage/volume/scaleio/pom.xml +++ b/plugins/storage/volume/scaleio/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../../pom.xml diff --git a/plugins/storage/volume/solidfire/pom.xml b/plugins/storage/volume/solidfire/pom.xml index ae3300a4000..0649c7ac71a 100644 --- a/plugins/storage/volume/solidfire/pom.xml +++ b/plugins/storage/volume/solidfire/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../../pom.xml diff --git a/plugins/storage/volume/storpool/pom.xml b/plugins/storage/volume/storpool/pom.xml index 8d6a9dea345..f679d713a4d 100644 --- a/plugins/storage/volume/storpool/pom.xml +++ b/plugins/storage/volume/storpool/pom.xml @@ -17,7 +17,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../../pom.xml diff --git a/plugins/user-authenticators/ldap/pom.xml b/plugins/user-authenticators/ldap/pom.xml index 0a4be911fa3..03103aa20eb 100644 --- a/plugins/user-authenticators/ldap/pom.xml +++ b/plugins/user-authenticators/ldap/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/user-authenticators/md5/pom.xml b/plugins/user-authenticators/md5/pom.xml index ed075763ab3..6920f6dc323 100644 --- a/plugins/user-authenticators/md5/pom.xml +++ b/plugins/user-authenticators/md5/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/user-authenticators/pbkdf2/pom.xml b/plugins/user-authenticators/pbkdf2/pom.xml index 65e93497e96..c21c2cfd3a4 100644 --- a/plugins/user-authenticators/pbkdf2/pom.xml +++ b/plugins/user-authenticators/pbkdf2/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/user-authenticators/plain-text/pom.xml b/plugins/user-authenticators/plain-text/pom.xml index e429d1634af..78de00b8121 100644 --- a/plugins/user-authenticators/plain-text/pom.xml +++ b/plugins/user-authenticators/plain-text/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/user-authenticators/saml2/pom.xml b/plugins/user-authenticators/saml2/pom.xml index 9f6cdd7c29c..45cbddde0b0 100644 --- a/plugins/user-authenticators/saml2/pom.xml +++ b/plugins/user-authenticators/saml2/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/plugins/user-authenticators/sha256salted/pom.xml b/plugins/user-authenticators/sha256salted/pom.xml index 6f8edd0d1bc..f86a2ce4489 100644 --- a/plugins/user-authenticators/sha256salted/pom.xml +++ b/plugins/user-authenticators/sha256salted/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.17.0.0 + 4.17.0.1 ../../pom.xml diff --git a/pom.xml b/pom.xml index 7c0a1e18d14..79782e70d1f 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ org.apache.cloudstack cloudstack - 4.17.0.0 + 4.17.0.1 pom Apache CloudStack Apache CloudStack is an IaaS ("Infrastructure as a Service") cloud orchestration platform. diff --git a/quickcloud/pom.xml b/quickcloud/pom.xml index ff8455e0c5e..944c4fcf936 100644 --- a/quickcloud/pom.xml +++ b/quickcloud/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/server/pom.xml b/server/pom.xml index 79d68a05cc9..fa616ed4dc3 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.17.0.0 + 4.17.0.1 diff --git a/services/console-proxy/pom.xml b/services/console-proxy/pom.xml index 1e847d368fb..23ddebd889b 100644 --- a/services/console-proxy/pom.xml +++ b/services/console-proxy/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack-services - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/services/console-proxy/rdpconsole/pom.xml b/services/console-proxy/rdpconsole/pom.xml index c9228308aba..3428479fa8b 100644 --- a/services/console-proxy/rdpconsole/pom.xml +++ b/services/console-proxy/rdpconsole/pom.xml @@ -26,7 +26,7 @@ org.apache.cloudstack cloudstack-service-console-proxy - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/services/console-proxy/server/pom.xml b/services/console-proxy/server/pom.xml index 34a44250f01..b55eed1b581 100644 --- a/services/console-proxy/server/pom.xml +++ b/services/console-proxy/server/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-service-console-proxy - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/services/pom.xml b/services/pom.xml index a53713e921f..b62881b3a8e 100644 --- a/services/pom.xml +++ b/services/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/services/secondary-storage/controller/pom.xml b/services/secondary-storage/controller/pom.xml index ad8d0c24e27..f4326401d08 100644 --- a/services/secondary-storage/controller/pom.xml +++ b/services/secondary-storage/controller/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-service-secondary-storage - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/services/secondary-storage/pom.xml b/services/secondary-storage/pom.xml index b1be9106baa..d7ff728d036 100644 --- a/services/secondary-storage/pom.xml +++ b/services/secondary-storage/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack-services - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/services/secondary-storage/server/pom.xml b/services/secondary-storage/server/pom.xml index 0597253de2a..e5e0c127977 100644 --- a/services/secondary-storage/server/pom.xml +++ b/services/secondary-storage/server/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-service-secondary-storage - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/systemvm/pom.xml b/systemvm/pom.xml index ce96420ae5d..6b8f5afcc4d 100644 --- a/systemvm/pom.xml +++ b/systemvm/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index b666fabfa3e..9d24e19d7c3 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.17.0.0 + 4.17.0.1 diff --git a/tools/apidoc/pom.xml b/tools/apidoc/pom.xml index 30afc0081e5..ea009a7cf70 100644 --- a/tools/apidoc/pom.xml +++ b/tools/apidoc/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloud-tools - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/tools/checkstyle/pom.xml b/tools/checkstyle/pom.xml index 4c372906c0d..130c3f4dc16 100644 --- a/tools/checkstyle/pom.xml +++ b/tools/checkstyle/pom.xml @@ -22,7 +22,7 @@ Apache CloudStack Developer Tools - Checkstyle Configuration org.apache.cloudstack checkstyle - 4.17.0.0 + 4.17.0.1 UTF-8 diff --git a/tools/devcloud-kvm/pom.xml b/tools/devcloud-kvm/pom.xml index 596e3c2bd59..00c44890a0c 100644 --- a/tools/devcloud-kvm/pom.xml +++ b/tools/devcloud-kvm/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloud-tools - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/tools/devcloud4/pom.xml b/tools/devcloud4/pom.xml index 4773006300a..359dc4ca401 100644 --- a/tools/devcloud4/pom.xml +++ b/tools/devcloud4/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloud-tools - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/tools/marvin/pom.xml b/tools/marvin/pom.xml index a39b425a039..ff8c2112c36 100644 --- a/tools/marvin/pom.xml +++ b/tools/marvin/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloud-tools - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/tools/pom.xml b/tools/pom.xml index cee66a772f4..20af5492efa 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/usage/pom.xml b/usage/pom.xml index da90d2e5e71..faae46978f7 100644 --- a/usage/pom.xml +++ b/usage/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.17.0.0 + 4.17.0.1 diff --git a/utils/pom.xml b/utils/pom.xml index 01344476c60..6a120e62958 100755 --- a/utils/pom.xml +++ b/utils/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.17.0.0 + 4.17.0.1 ../pom.xml diff --git a/vmware-base/pom.xml b/vmware-base/pom.xml index 4b4255adde6..bf7cbee96ca 100644 --- a/vmware-base/pom.xml +++ b/vmware-base/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.17.0.0 + 4.17.0.1