/***************************************************************************
* Copyright (c) 2012-2015 VMware, Inc. All Rights Reserved.
* Licensed 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.vmware.bdd.dal.entity;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertTrue;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import com.vmware.bdd.dal.IIpBlockDAO;
import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.vmware.bdd.entity.IpBlockEntity;
import com.vmware.bdd.entity.IpBlockEntity.BlockType;
import com.vmware.bdd.entity.NetworkEntity;
import com.vmware.bdd.entity.NetworkEntity.AllocType;
import com.vmware.bdd.utils.AuAssert;
public class TestIpBlockEntity {
private static final Logger logger = Logger.getLogger(TestIpBlockEntity.class);
IIpBlockDAO ipBlockDao;
ApplicationContext ctx;
@BeforeMethod
public void setup() {
ctx = new ClassPathXmlApplicationContext("META-INF/spring/*-context.xml");
ipBlockDao = ctx.getBean(IIpBlockDAO.class);
}
@AfterMethod
public void tearDown() {
}
@AfterClass
public static void deleteAll() {
}
private String collectionToString(Collection<?> c) {
String str = "";
for (Object o : c) {
str += "\n" + o;
}
return str;
}
private List<IpBlockEntity> genRandBlocks(int count) {
List<IpBlockEntity> blocks = new ArrayList<IpBlockEntity>(count);
NetworkEntity network = new NetworkEntity("net1", "vmnet1", AllocType.IP_POOL,
"255.255.255.0", "192.168.1.1", "8.8.8.8", null, null);
Long[] owners = { 1L, 2L, 3L, 4L, 5L };
BlockType[] blockTypes = { BlockType.ASSIGNED, BlockType.FREE };
for (int i = 0; i < count; ++i) {
Long ownerId = IpBlockEntity.FREE_BLOCK_OWNER_ID;
BlockType blockType = blockTypes[(int) (Math.random() * blockTypes.length)
% blockTypes.length];
if (blockType != BlockType.FREE) {
ownerId = owners[(int) (Math.random() * owners.length) % owners.length];
}
int maxValue = count * 10;
Long end = 1 + (long) (Math.random() * maxValue) % maxValue;
Long begin = (long) (Math.random() * end) % end;
blocks.add(new IpBlockEntity(network, ownerId, blockType, begin, end));
}
return blocks;
}
/**
* Split the block randomly, the original block is kept unchanged.
*
* @param block
* original block
* @return the split block(s)
*/
private List<IpBlockEntity> randSplit(IpBlockEntity block) {
List<IpBlockEntity> split = new ArrayList<IpBlockEntity>(2);
if (block.getLength() > 1 && Math.random() < 0.5) {
Long splitIp = block.getBeginIp() +
(long) (Math.random() * (block.getLength() - 1))
% (block.getLength() - 1);
IpBlockEntity left = new IpBlockEntity(block.getNetwork(), block.getOwnerId(),
block.getType(),
block.getBeginIp(),
splitIp);
IpBlockEntity right = new IpBlockEntity(block.getNetwork(), block.getOwnerId(),
block.getType(),
splitIp + 1,
block.getEndIp());
split.add(left);
split.add(right);
} else {
IpBlockEntity dup = new IpBlockEntity(block.getNetwork(), block.getOwnerId(),
block.getType(), block.getBeginIp(), block.getEndIp());
split.add(dup);
}
return split;
}
/**
* Randomly expand a block in-place.
*
* @return expanded block
*/
private IpBlockEntity randExpand(IpBlockEntity block, long min, long max) {
if (Math.random() < 0.2) {
Long begin = block.getBeginIp();
Long end = block.getEndIp();
AuAssert.check(begin >= min && end <= max);
begin = Math.max(min, begin - (long) (Math.random() * 3));
end = Math.min(max, end + (long) (Math.random() * 3));
block.setBeginIp(begin);
block.setEndIp(end);
}
return block;
}
/**
* Random shuffle the blocks and randomly group them as separated groups.
*/
private List<List<IpBlockEntity>> randShuffleAndGroup(List<IpBlockEntity> blocks) {
Collections.shuffle(blocks);
List<List<IpBlockEntity>> result = new ArrayList<List<IpBlockEntity>>();
Iterator<IpBlockEntity> iter = blocks.iterator();
while (iter.hasNext()) {
List<IpBlockEntity> subGroup = new ArrayList<IpBlockEntity>();
subGroup.add(iter.next());
while (Math.random() < 0.9 && iter.hasNext()) {
subGroup.add(iter.next());
}
result.add(subGroup);
}
return result;
}
/**
* Split a block randomly into optionally overlapped pieces and randomly
* group them into several groups.
*
* @param original
* original block
* @param splitLevel
* split level
* @param allowOverlap
* whether allow overlapping pieces
* @return result set
*/
private List<List<IpBlockEntity>> torn(IpBlockEntity original, int splitLevel,
boolean allowOverlap) {
List<IpBlockEntity> split = new ArrayList<IpBlockEntity>();
split.add(original);
for (int i = 0; i < splitLevel; ++i) {
List<IpBlockEntity> base = split;
split = new ArrayList<IpBlockEntity>();
for (IpBlockEntity blk : base) {
split.addAll(randSplit(blk));
}
}
if (allowOverlap) {
for (IpBlockEntity blk : split) {
randExpand(blk, original.getBeginIp(), original.getEndIp());
}
}
return randShuffleAndGroup(split);
}
@Test
public void testCompareTo() {
List<IpBlockEntity> blocks = genRandBlocks(100);
for (IpBlockEntity b1 : blocks) {
for (IpBlockEntity b2 : blocks) {
// some redundancies
assertTrue(b1.compareTo(b2) == 0 && b2.compareTo(b1) == 0 ||
b1.compareTo(b2) > 0 && b2.compareTo(b1) < 0 ||
b1.compareTo(b2) < 0 && b2.compareTo(b1) > 0);
}
}
}
@Test
public void testContainAndOverlap() {
int N = 100;
int count = 0;
List<IpBlockEntity> blocks = genRandBlocks(N);
for (IpBlockEntity b1 : blocks) {
for (IpBlockEntity b2 : blocks) {
if (b1.contains(b2)) {
++count;
}
assertTrue(!b1.contains(b2) ||
b1.contains(b2) && b1.isOverlapedWith(b2) && b2.isOverlapedWith(b1));
assertTrue(!b1.isOverlapedWith(b2) ||
b1.isOverlapedWith(b2) && b2.isOverlapedWith(b1));
}
}
assertTrue(count >= N);
}
@Test
public void testMergeBasic() {
NetworkEntity network = new NetworkEntity("net1", "vmnet1", AllocType.IP_POOL,
"255.255.255.0", "192.168.1.1", "8.8.8.8", null, null);
IpBlockEntity blk1 = new IpBlockEntity(network, 1L, BlockType.ASSIGNED, 1L, 3L);
IpBlockEntity blk2 = new IpBlockEntity(network, 1L, BlockType.ASSIGNED, 4L, 6L);
IpBlockEntity blk3 = new IpBlockEntity(network, 1L, BlockType.ASSIGNED, 5L, 10L);
List<IpBlockEntity> split = new ArrayList<IpBlockEntity>();
split.add(blk1);
split.add(blk2);
split.add(blk3);
logger.info("split: " + collectionToString(split));
List<IpBlockEntity> merged = ipBlockDao.merge(split, false, false, true);
logger.info("merged: " + collectionToString(merged));
assertEquals(1, merged.size());
assertEquals(10, merged.get(0).getLength());
}
/**
* Randomly torn a whole IP blocks into optionally overlapped pieces and then
* shuffle and group the pieces into some random groups. Then merge these
* groups and join them in a single list and make a final merge. The finally
* merged list should match to the original one.
*/
@Test(enabled=false)
private void doMergeRandomTest(boolean allowOverlap) {
IpBlockEntity original = new IpBlockEntity(new NetworkEntity("net1", "vmnet1",
AllocType.IP_POOL, "255.255.255.0", "192.168.1.1", "8.8.8.8", null, null), 1L,
BlockType.ASSIGNED, 1L, 1L << 17/* 131072 */);
List<IpBlockEntity> allBlocks = new ArrayList<IpBlockEntity>();
for (List<IpBlockEntity> grp : torn(original, 20, allowOverlap)) {
allBlocks.addAll(ipBlockDao.merge(grp, false, false, allowOverlap));
}
List<IpBlockEntity> merged =
ipBlockDao.merge(allBlocks, false, false, allowOverlap);
logger.info("merged: " + collectionToString(merged));
assertEquals(1, merged.size());
assertEquals(original, merged.get(0));
}
@Test
public void testMergeRandom() {
doMergeRandomTest(true);
doMergeRandomTest(false);
}
@Test
public void testSubtractBasic() {
NetworkEntity network = new NetworkEntity("net1", "vmnet1", AllocType.IP_POOL,
"255.255.255.0", "192.168.1.1", "8.8.8.8", null, null);;
List<IpBlockEntity> setA = new ArrayList<IpBlockEntity>();
setA.add(new IpBlockEntity(network, 1L, BlockType.ASSIGNED, 1L, 10L));
List<IpBlockEntity> setB1 = new ArrayList<IpBlockEntity>();
setB1.add(new IpBlockEntity(network, 1L, BlockType.ASSIGNED, 1L, 3L));
List<IpBlockEntity> setB2 = new ArrayList<IpBlockEntity>();
setB2.add(new IpBlockEntity(network, 1L, BlockType.ASSIGNED, 4L, 6L));
List<IpBlockEntity> setB3 = new ArrayList<IpBlockEntity>();
setB3.add(new IpBlockEntity(network, 1L, BlockType.ASSIGNED, 5L, 10L));
List<IpBlockEntity> setDiff = setA;
setDiff = IpBlockEntity.subtract(setDiff, setB1);
setDiff = IpBlockEntity.subtract(setDiff, setB2);
setDiff = IpBlockEntity.subtract(setDiff, setB3);
assertTrue(setDiff.isEmpty());
List<IpBlockEntity> setC = new ArrayList<IpBlockEntity>();
setC.add(new IpBlockEntity(network, 1L, BlockType.ASSIGNED, 3337306476L, 3337306480L));
List<IpBlockEntity> setD = new ArrayList<IpBlockEntity>();
setD.add(new IpBlockEntity(network, 1L, BlockType.ASSIGNED, 3337306476L, 3337306480L));
setD.add(new IpBlockEntity(network, 1L, BlockType.ASSIGNED, 3337306476L, 3337306476L));
setDiff = setC;
setD = ipBlockDao.merge(setD, true, true, true);
setDiff = IpBlockEntity.subtract(setDiff, setD);
assertTrue(setDiff.isEmpty());
setDiff = setC;
setD = ipBlockDao.merge(setD, true, true, true);
setDiff = IpBlockEntity.subtract(setD, setDiff);
assertTrue(setDiff.isEmpty());
}
/**
* Randomly torn a whole IP blocks into optionally overlapped and shuffled
* pieces and then subtract the original block with all these pieces one by
* one. Finally the result diff will equals to en empty set.
*/
@Test(enabled=false)
private void doSubtractRandomTest(boolean allowOverlap) {
IpBlockEntity original = new IpBlockEntity(new NetworkEntity("net1", "vmnet1",
AllocType.IP_POOL, "255.255.255.0", "192.168.1.1", "8.8.8.8", null, null), 1L,
BlockType.ASSIGNED, 1L, 1L << 15);
List<IpBlockEntity> setA = new ArrayList<IpBlockEntity>();
setA.add(original);
List<IpBlockEntity> setDiff = setA;
for (List<IpBlockEntity> grp : torn(original, 18, allowOverlap)) {
setDiff = IpBlockEntity.subtract(setDiff,
ipBlockDao.merge(grp, false, false, allowOverlap));
logger.debug("#diff: " + IpBlockEntity.count(setDiff));
logger.debug("diff: " + collectionToString(setDiff));
}
assertTrue(setDiff.isEmpty());
}
@Test
public void testSubtractRandom() {
doSubtractRandomTest(true);
doSubtractRandomTest(false);
}
}