diff --git a/api/src/com/cloud/network/NetworkModel.java b/api/src/com/cloud/network/NetworkModel.java index 7e17405f434..57eeb290c05 100644 --- a/api/src/com/cloud/network/NetworkModel.java +++ b/api/src/com/cloud/network/NetworkModel.java @@ -250,4 +250,6 @@ public interface NetworkModel { boolean isNetworkInlineMode(Network network); Vlan getVlanForNetwork(long networkId); + + boolean isIP6AddressAvailable(long networkId); } \ No newline at end of file diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index 5e4996b144c..7592b6bdfca 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -97,7 +97,6 @@ public enum Config { VmNetworkThrottlingRate("Network", ManagementServer.class, Integer.class, "vm.network.throttling.rate", "200", "Default data transfer rate in megabits per second allowed in User vm's default network.", null), NetworkLockTimeout("Network", ManagementServer.class, Integer.class, "network.lock.timeout", "600", "Lock wait timeout (seconds) while implementing network", null), - SecurityGroupWorkCleanupInterval("Network", ManagementServer.class, Integer.class, "network.securitygroups.work.cleanup.interval", "120", "Time interval (seconds) in which finished work is cleaned up from the work table", null), SecurityGroupWorkerThreads("Network", ManagementServer.class, Integer.class, "network.securitygroups.workers.pool.size", "50", "Number of worker threads processing the security group update work queue", null), SecurityGroupWorkGlobalLockTimeout("Network", ManagementServer.class, Integer.class, "network.securitygroups.work.lock.timeout", "300", "Lock wait timeout (seconds) while updating the security group work queue", null), @@ -356,9 +355,9 @@ public enum Config { VpcCleanupInterval("Advanced", ManagementServer.class, Integer.class, "vpc.cleanup.interval", "3600", "The interval (in seconds) between cleanup for Inactive VPCs", null), VpcMaxNetworks("Advanced", ManagementServer.class, Integer.class, "vpc.max.networks", "3", "Maximum number of networks per vpc", null), DetailBatchQuerySize("Advanced", ManagementServer.class, Integer.class, "detail.batch.query.size", "2000", "Default entity detail batch query size for listing", null), - ConcurrentSnapshotsThresholdPerHost("Advanced", ManagementServer.class, Long.class, "concurrent.snapshots.threshold.perhost", null, "Limits number of snapshots that can be handled by the host concurrently; default is NULL - unlimited", null), + NetworkIPv6SearchRetryMax("Network", ManagementServer.class, Integer.class, "network.ipv6.search.retry.max", "10000", "The maximum number of retrying times to search for an available IPv6 address in the table", null), ExternalBaremetalSystemUrl("Advanced", ManagementServer.class, String.class, "external.baremetal.system.url", null, "url of external baremetal system that CloudStack will talk to", null), ExternalBaremetalResourceClassName("Advanced", ManagementServer.class, String.class, "external,baremetal.resource.classname", null, "class name for handling external baremetal resource", null), diff --git a/server/src/com/cloud/network/Ipv6AddressManagerImpl.java b/server/src/com/cloud/network/Ipv6AddressManagerImpl.java index 01227721614..f5ad7da1d14 100644 --- a/server/src/com/cloud/network/Ipv6AddressManagerImpl.java +++ b/server/src/com/cloud/network/Ipv6AddressManagerImpl.java @@ -25,14 +25,19 @@ import javax.naming.ConfigurationException; import org.apache.log4j.Logger; +import com.cloud.configuration.Config; +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenterVO; import com.cloud.dc.Vlan; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.VlanDao; import com.cloud.exception.InsufficientAddressCapacityException; +import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.UserIpv6AddressDao; import com.cloud.user.Account; import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.NumbersUtil; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; @@ -40,6 +45,9 @@ import com.cloud.utils.net.NetUtils; public class Ipv6AddressManagerImpl extends ManagerBase implements Ipv6AddressManager { public static final Logger s_logger = Logger.getLogger(Ipv6AddressManagerImpl.class.getName()); + String _name = null; + int _ipv6RetryMax = 0; + @Inject DataCenterDao _dcDao; @Inject @@ -48,30 +56,50 @@ public class Ipv6AddressManagerImpl extends ManagerBase implements Ipv6AddressMa NetworkModel _networkModel; @Inject UserIpv6AddressDao _ipv6Dao; + @Inject + NetworkDao _networkDao; + @Inject + ConfigurationDao _configDao; @Override + public boolean configure(String name, Map params) + throws ConfigurationException { + _name = name; + Map configs = _configDao.getConfiguration(params); + _ipv6RetryMax = NumbersUtil.parseInt(configs.get(Config.NetworkIPv6SearchRetryMax.key()), 10000); + return true; + } + public UserIpv6Address assignDirectIp6Address(long dcId, Account owner, Long networkId, String requestedIp6) throws InsufficientAddressCapacityException { + Network network = _networkDao.findById(networkId); + if (network == null) { + return null; + } Vlan vlan = _networkModel.getVlanForNetwork(networkId); if (vlan == null) { s_logger.debug("Cannot find related vlan or too many vlan attached to network " + networkId); return null; } - String ip = null; + String ip = null; if (requestedIp6 == null) { + if (!_networkModel.isIP6AddressAvailable(networkId)) { + throw new InsufficientAddressCapacityException("There is no more address available in the network " + network.getName(), DataCenter.class, network.getDataCenterId()); + } + ip = NetUtils.getIp6FromRange(vlan.getIp6Range()); int count = 0; - while (ip == null || count >= 10) { - ip = NetUtils.getIp6FromRange(vlan.getIp6Range()); - //Check for duplicate IP - if (_ipv6Dao.findByNetworkIdAndIp(networkId, ip) == null) { - break; - } else { - ip = null; - } + while (_ipv6Dao.findByNetworkIdAndIp(networkId, ip) != null) { + ip = NetUtils.getNextIp6InRange(ip, vlan.getIp6Range()); count ++; + // It's an arbitrate number to prevent the infinite loop + if (count > _ipv6RetryMax) { + ip = null; + break; + } } if (ip == null) { - throw new CloudRuntimeException("Fail to get unique ipv6 address after 10 times trying!"); + throw new InsufficientAddressCapacityException("Cannot find a usable IP in the network " + network.getName() + " after network.ipv6.search.retry.max = " + _ipv6RetryMax + " times retry!", + DataCenter.class, network.getDataCenterId()); } } else { if (!NetUtils.isIp6InRange(requestedIp6, vlan.getIp6Range())) { diff --git a/server/src/com/cloud/network/NetworkModelImpl.java b/server/src/com/cloud/network/NetworkModelImpl.java index ff97911493e..6e22782cad6 100644 --- a/server/src/com/cloud/network/NetworkModelImpl.java +++ b/server/src/com/cloud/network/NetworkModelImpl.java @@ -526,7 +526,7 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel { return false; } if (network.getIp6Gateway() != null) { - hasFreeIps = isIP6AddressAvailable(network); + hasFreeIps = isIP6AddressAvailable(network.getId()); } } else { hasFreeIps = (getAvailableIps(network, null)).size() > 0; @@ -544,8 +544,13 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel { } return vlans.get(0); } - - private boolean isIP6AddressAvailable(Network network) { + + @Override + public boolean isIP6AddressAvailable(long networkId) { + Network network = _networksDao.findById(networkId); + if (network == null) { + return false; + } if (network.getIp6Gateway() == null) { return false; } @@ -1868,4 +1873,4 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel { return offering.isInline(); } -} \ No newline at end of file +} diff --git a/server/test/com/cloud/network/MockNetworkModelImpl.java b/server/test/com/cloud/network/MockNetworkModelImpl.java index 1c36364f418..502354ea108 100644 --- a/server/test/com/cloud/network/MockNetworkModelImpl.java +++ b/server/test/com/cloud/network/MockNetworkModelImpl.java @@ -815,4 +815,9 @@ public class MockNetworkModelImpl extends ManagerBase implements NetworkModel { return null; } + @Override + public boolean isIP6AddressAvailable(long networkId) { + // TODO Auto-generated method stub + return false; + } } diff --git a/server/test/com/cloud/vpc/MockNetworkModelImpl.java b/server/test/com/cloud/vpc/MockNetworkModelImpl.java index 8097c346970..1080d48f1be 100644 --- a/server/test/com/cloud/vpc/MockNetworkModelImpl.java +++ b/server/test/com/cloud/vpc/MockNetworkModelImpl.java @@ -829,4 +829,10 @@ public class MockNetworkModelImpl extends ManagerBase implements NetworkModel { return null; } + @Override + public boolean isIP6AddressAvailable(long networkId) { + // TODO Auto-generated method stub + return false; + } + } diff --git a/utils/src/com/cloud/utils/net/NetUtils.java b/utils/src/com/cloud/utils/net/NetUtils.java index 8d3ff6d4b0a..0875f88879d 100755 --- a/utils/src/com/cloud/utils/net/NetUtils.java +++ b/utils/src/com/cloud/utils/net/NetUtils.java @@ -1243,4 +1243,27 @@ public class NetUtils { IPv6AddressRange range2 = IPv6AddressRange.fromFirstAndLast(start2, end2); return range1.overlaps(range2); } + + public static String getNextIp6InRange(String currentIp, String ipRange) { + String[] ips = ipRange.split("-"); + String startIp = ips[0]; + String endIp = null; + if (ips.length > 1) { + endIp = ips[1]; + } + IPv6Address start = IPv6Address.fromString(startIp); + IPv6Address end = IPv6Address.fromString(endIp); + IPv6Address current = IPv6Address.fromString(currentIp); + IPv6Address result = null; + if (current.equals(end)) { + result = start; + } else{ + result = current.add(1); + } + String resultIp = null; + if (result != null) { + resultIp = result.toString(); + } + return resultIp; + } } diff --git a/utils/test/com/cloud/utils/net/NetUtilsTest.java b/utils/test/com/cloud/utils/net/NetUtilsTest.java index 763a7986154..686025e55aa 100644 --- a/utils/test/com/cloud/utils/net/NetUtilsTest.java +++ b/utils/test/com/cloud/utils/net/NetUtilsTest.java @@ -88,6 +88,7 @@ public class NetUtilsTest extends TestCase { assertEquals(NetUtils.countIp6InRange("1234:5678::2-1234:5678::0"), 0); assertEquals(NetUtils.getIp6FromRange("1234:5678::1-1234:5678::1"), "1234:5678::1"); String ipString = null; + String range = "1234:5678::1-1234:5678::8000:0000"; IPv6Address ipStart = IPv6Address.fromString("1234:5678::1"); IPv6Address ipEnd = IPv6Address.fromString("1234:5678::8000:0000"); for (int i = 0; i < 10; i ++) { @@ -97,6 +98,7 @@ public class NetUtilsTest extends TestCase { assertTrue(ip.compareTo(ipStart) >= 0); assertTrue(ip.compareTo(ipEnd) <= 0); } + //Test isIp6RangeOverlap assertFalse(NetUtils.isIp6RangeOverlap("1234:5678::1-1234:5678::ffff", "1234:5678:1::1-1234:5678:1::ffff")); assertTrue(NetUtils.isIp6RangeOverlap("1234:5678::1-1234:5678::ffff", "1234:5678::2-1234:5678::f")); assertTrue(NetUtils.isIp6RangeOverlap("1234:5678::f-1234:5678::ffff", "1234:5678::2-1234:5678::f"));