package org.zstack.network.l3; import org.zstack.core.db.SimpleQuery; import org.zstack.core.db.SimpleQuery.Op; import org.zstack.header.network.l3.*; import org.zstack.utils.Utils; import org.zstack.utils.logging.CLogger; import org.zstack.utils.network.NetworkUtils; import java.util.Collections; import java.util.List; import java.util.Random; public class RandomIpAllocatorStrategy extends AbstractIpAllocatorStrategy { private static final CLogger logger = Utils.getLogger(RandomIpAllocatorStrategy.class); public static final IpAllocatorType type = new IpAllocatorType(L3NetworkConstant.RANDOM_IP_ALLOCATOR_STRATEGY); @Override public IpAllocatorType getType() { return type; } @Override public UsedIpInventory allocateIp(IpAllocateMessage msg) { if (msg.getRequiredIp() != null) { return allocateRequiredIp(msg); } SimpleQuery<IpRangeVO> query = dbf.createQuery(IpRangeVO.class); query.add(IpRangeVO_.l3NetworkUuid, Op.EQ, msg.getL3NetworkUuid()); List<IpRangeVO> ranges = query.list(); Collections.shuffle(ranges); do { String ip = null; IpRangeVO tr = null; for (IpRangeVO r : ranges) { ip = allocateIp(r); tr = r; if (ip != null) { break; } } if (ip == null) { /* No available ip in ranges */ return null; } UsedIpInventory inv = l3NwMgr.reserveIp(IpRangeInventory.valueOf(tr), ip); if (inv != null) { return inv; } } while (true); } private String steppingAllocate(long s, long e, int total, String rangeUuid) { int step = 254; int failureCount = 0; int failureCheckPoint = 5; while (s <= e) { // if failing failureCheckPoint times, the range is probably full, // we check the range. // why don't we check before steppingAllocate()? because in that case we // have to count the used IP every time allocating a IP, and count operation // is a full scan in DB, which is very costly if (failureCheckPoint == failureCount++) { SimpleQuery<UsedIpVO> q = dbf.createQuery(UsedIpVO.class); q.add(UsedIpVO_.ipRangeUuid, Op.EQ, rangeUuid); long count = q.count(); if (count == total) { logger.debug(String.format("ip range[uuid:%s] has no ip available, try next one", rangeUuid)); return null; } else { failureCount = 0; } } long te = s + step; te = te > e ? e : te; SimpleQuery<UsedIpVO> q = dbf.createQuery(UsedIpVO.class); q.select(UsedIpVO_.ipInLong); q.add(UsedIpVO_.ipInLong, Op.GTE, s); q.add(UsedIpVO_.ipInLong, Op.LTE, te); q.add(UsedIpVO_.ipRangeUuid, Op.EQ, rangeUuid); List<Long> used = q.listValue(); if (te - s + 1 == used.size()) { s += step; continue; } Collections.sort(used); return NetworkUtils.randomAllocateIpv4Address(s, te, used); } return null; } private String allocateIp(IpRangeVO vo) { int total = vo.size(); Random random = new Random(); long s = random.nextInt(total) + NetworkUtils.ipv4StringToLong(vo.getStartIp()); long e = NetworkUtils.ipv4StringToLong(vo.getEndIp()); String ret = steppingAllocate(s ,e, total, vo.getUuid()); if (ret != null) { return ret; } e = s; s = NetworkUtils.ipv4StringToLong(vo.getStartIp()); return steppingAllocate(s ,e, total, vo.getUuid()); } }