package org.zstack.network.l2.vxlan.vxlanNetworkPool; import org.zstack.core.db.SimpleQuery; import org.zstack.network.l2.vxlan.vxlanNetwork.VxlanNetworkVO; import org.zstack.network.l2.vxlan.vxlanNetwork.VxlanNetworkVO_; import org.zstack.utils.Utils; import org.zstack.utils.logging.CLogger; import java.util.BitSet; import java.util.Collections; import java.util.List; import java.util.Random; /** * Created by weiwang on 10/03/2017. */ public class RandomVniAllocatorStrategy extends AbstractVniAllocatorStrategy { private static final CLogger logger = Utils.getLogger(RandomVniAllocatorStrategy.class); public static final VniAllocatorType type = new VniAllocatorType(VxlanNetworkPoolConstant.RANDOM_VNI_ALLOCATOR_STRATEGY); @Override public VniAllocatorType getType() { return type; } @Override public Integer allocateVni(VniAllocateMessage msg) { if (msg.getRequiredVni() != null) { return allocateRequiredVni(msg); } SimpleQuery<VniRangeVO> query = dbf.createQuery(VniRangeVO.class); query.add(VniRangeVO_.l2NetworkUuid, SimpleQuery.Op.EQ, msg.getL2NetworkUuid()); List<VniRangeVO> ranges = query.list(); Collections.shuffle(ranges); do { Integer vni = null; VniRangeVO tr = null; for (VniRangeVO r : ranges) { vni = allocateVni(r); tr = r; if (vni != null) { break; } } if (vni == null) { /* No available vniin ranges */ return null; } if (vni != null) { return vni; } } while (true); } private Integer allocateVni(VniRangeVO vo) { int total = vo.size(); Random random = new Random(); Integer s = random.nextInt(total) + vo.getStartVni(); Integer e = vo.getEndVni(); Integer ret = steppingAllocate(s, e, total, vo.getUuid(), vo.getL2NetworkUuid()); if (ret != null) { return ret; } e = s; s = vo.getStartVni(); return steppingAllocate(s, e, total, vo.getUuid(), vo.getL2NetworkUuid()); } private Integer steppingAllocate(Integer s, Integer e, int total, String rangeUuid, String poolUuid) { 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 Vni, and count operation // is a full scan in DB, which is very costly if (failureCheckPoint == failureCount++) { SimpleQuery<VxlanNetworkVO> q = dbf.createQuery(VxlanNetworkVO.class); q.add(VxlanNetworkVO_.poolUuid, SimpleQuery.Op.EQ, poolUuid); q.add(VxlanNetworkVO_.vni, SimpleQuery.Op.GTE, s); q.add(VxlanNetworkVO_.vni, SimpleQuery.Op.LTE, e); long count = q.count(); if (count == total) { logger.debug(String.format("vni range[uuid:%s] has no vni available, try next one", rangeUuid)); return null; } else { failureCount = 0; } } int te = s + step; te = te > e ? e : te; SimpleQuery<VxlanNetworkVO> q = dbf.createQuery(VxlanNetworkVO.class); q.select(VxlanNetworkVO_.vni); q.add(VxlanNetworkVO_.vni, SimpleQuery.Op.GTE, s); q.add(VxlanNetworkVO_.vni, SimpleQuery.Op.LTE, te); q.add(VxlanNetworkVO_.poolUuid, SimpleQuery.Op.EQ, poolUuid); List<Integer> used = q.listValue(); if (te - s + 1 == used.size()) { s += step; continue; } Collections.sort(used); return randomAllocateVni(s, te, used); } return null; } private static Integer randomAllocateVni(Integer startVni, Integer endVni, List<Integer> allocatedVnis) { int total = (endVni - startVni + 1); if (total == allocatedVnis.size()) { return null; } BitSet full = new BitSet(total); for (Integer alloc : allocatedVnis) { full.set(alloc - startVni); } Random random = new Random(); int next = random.nextInt(total); int a = full.nextClearBit(next); if (a >= total) { a = full.nextClearBit(0); } return a + startVni; } }