String SRC_RLOCS = "src_rlocs";
String VNI = "vni";
String LCAF_SRCDST = "lcaf_srcdst";
+ String TIME_BUCKET_ID = "time_bucket_id";
String UNKOWN = "-1";
}
*/
package org.opendaylight.lispflowmapping.config;
+import java.util.concurrent.TimeUnit;
+
import org.opendaylight.lispflowmapping.interfaces.mappingservice.IMappingService;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
private long registrationValiditySb;
private long smrTimeout;
private int smrRetryCount;
+ private int numberOfBucketsInTimeBucketWheel;
/*
* XXX When configuration options are added or removed, they should also be added/removed in the karaf
private static final long MIN_REGISTRATION_VALIDITY_SB = 200000L;
private static final long DEFAULT_SMR_TIMEOUT = 3000L;
private static final int DEFAULT_SMR_RETRY_COUNT = 5;
+ private static final int MIN_NUMBER_OF_BUCKETS_IN_TIME_BUCKET_WHEEL = 2;
+ private static final int TIMEOUT_TOLERANCE_MULTIPLIER_IN_TIME_BUCKET_WHEEL = 2;
private static final ConfigIni INSTANCE = new ConfigIni();
initRegisterValiditySb(context);
initSmrRetryCount(context);
initSmrTimeout(context);
+ initBucketNumber();
}
private void initRegisterValiditySb(BundleContext context) {
}
}
+ //one bucket should contain mapping of approximate 1 min time frame
+ private void initBucketNumber() {
+ numberOfBucketsInTimeBucketWheel = (int) (TimeUnit.MILLISECONDS.toMinutes(getRegistrationValiditySb()) + 1);
+
+ numberOfBucketsInTimeBucketWheel = Math.max(numberOfBucketsInTimeBucketWheel,
+ MIN_NUMBER_OF_BUCKETS_IN_TIME_BUCKET_WHEEL);
+ }
+
public boolean mappingMergeIsSet() {
return mappingMerge;
}
return this.smrTimeout;
}
+ public int getNumberOfBucketsInTimeBucketWheel() {
+ return numberOfBucketsInTimeBucketWheel;
+ }
+
+ public long getMaximumTimeoutTolerance() {
+ return TIMEOUT_TOLERANCE_MULTIPLIER_IN_TIME_BUCKET_WHEEL * getRegistrationValiditySb();
+ }
+
public static ConfigIni getInstance() {
return INSTANCE;
}
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.lispflowmapping.config.ConfigIni;
import org.opendaylight.lispflowmapping.dsbackend.DataStoreBackEnd;
+import org.opendaylight.lispflowmapping.implementation.timebucket.implementation.TimeBucketMappingTimeoutService;
+import org.opendaylight.lispflowmapping.implementation.timebucket.interfaces.ISouthBoundMappingTimeoutService;
import org.opendaylight.lispflowmapping.implementation.util.DSBEInputUtil;
import org.opendaylight.lispflowmapping.implementation.util.MappingMergeUtil;
import org.opendaylight.lispflowmapping.interfaces.dao.ILispDAO;
+import org.opendaylight.lispflowmapping.interfaces.dao.SubKeys;
import org.opendaylight.lispflowmapping.interfaces.mapcache.IAuthKeyDb;
import org.opendaylight.lispflowmapping.interfaces.mapcache.ILispMapCache;
import org.opendaylight.lispflowmapping.interfaces.mapcache.IMapCache;
private DataStoreBackEnd dsbe;
private boolean isMaster = false;
+ private ISouthBoundMappingTimeoutService sbMappingTimeoutService;
+
public MappingSystem(ILispDAO dao, boolean iterateMask, boolean notifications, boolean mappingMerge) {
this.dao = dao;
this.notificationService = notifications;
this.mappingMerge = mappingMerge;
buildMapCaches();
+
+ sbMappingTimeoutService = new TimeBucketMappingTimeoutService(ConfigIni.getInstance()
+ .getNumberOfBucketsInTimeBucketWheel(), ConfigIni.getInstance().getRegistrationValiditySb(),
+ this);
}
public void setDataStoreBackEnd(DataStoreBackEnd dsbe) {
}
public void addMapping(MappingOrigin origin, Eid key, MappingData mappingData) {
+
+ sbMappingTimeoutService.removeExpiredMappings();
+
if (mappingData == null) {
LOG.warn("addMapping() called with null mapping, ignoring");
return;
smc.addMapping(key, xtrId, mappingData);
}
}
+ addOrRefreshMappingInTimeoutService(key, mappingData);
}
tableMap.get(origin).addMapping(key, mappingData);
}
}
+ private void addOrRefreshMappingInTimeoutService(Eid key, MappingData mappingData) {
+ Integer oldBucketId = (Integer) smc.getData(key, SubKeys.TIME_BUCKET_ID);
+ Integer updatedBucketId;
+
+ if (oldBucketId != null) {
+ //refresh mapping
+ updatedBucketId = sbMappingTimeoutService.refreshMapping(key, mappingData, oldBucketId);
+ } else {
+ updatedBucketId = sbMappingTimeoutService.addMapping(key, mappingData);
+ }
+
+ smc.addData(key, SubKeys.TIME_BUCKET_ID, updatedBucketId);
+ }
+
/*
* Since this method is only called when there is a hit in the southbound Map-Register cache, and that cache is
* not used when merge is on, it's OK to ignore the effects of timestamp changes on merging for now.
*/
public void refreshMappingRegistration(Eid key, XtrId xtrId, Long timestamp) {
+
+ sbMappingTimeoutService.removeExpiredMappings();
+
if (timestamp == null) {
timestamp = System.currentTimeMillis();
}
MappingData mappingData = (MappingData) smc.getMapping(null, key);
if (mappingData != null) {
mappingData.setTimestamp(new Date(timestamp));
+ addOrRefreshMappingInTimeoutService(key, mappingData);
} else {
LOG.warn("Could not update timestamp for EID {}, no mapping found", LispAddressStringifier.getString(key));
}
if (mergedMappingData != null) {
smc.addMapping(key, mergedMappingData, sourceRlocs);
dsbe.addMapping(DSBEInputUtil.toMapping(MappingOrigin.Southbound, key, mergedMappingData));
+ addOrRefreshMappingInTimeoutService(key, mergedMappingData);
} else {
removeSbMapping(key, mergedMappingData);
}
}
}
- private MappingData handleSbExpiredMapping(Eid key, XtrId xtrId, MappingData mappingData) {
+ public MappingData handleSbExpiredMapping(Eid key, XtrId xtrId, MappingData mappingData) {
if (mappingMerge && mappingData.isMergeEnabled()) {
return handleMergedMapping(key);
}
} else {
removeSbMapping(key, mappingData);
}
-
return null;
}
if (mappingData != null && mappingData.getXtrId() != null) {
removeSbXtrIdSpecificMapping(key, mappingData.getXtrId(), mappingData);
}
-
+ removeFromSbTimeoutService(key);
smc.removeMapping(key);
dsbe.removeMapping(DSBEInputUtil.toMapping(MappingOrigin.Southbound, key, mappingData));
}
+ private void removeFromSbTimeoutService(Eid key) {
+ Integer bucketId = (Integer) smc.getData(key, SubKeys.TIME_BUCKET_ID);
+ if (bucketId != null) {
+ sbMappingTimeoutService.removeMappingFromTimeoutService(key, bucketId);
+ }
+ }
+
@Override
public Eid getWidestNegativePrefix(Eid key) {
Eid nbPrefix = pmc.getWidestNegativeMapping(key);
@Override
public void removeMapping(MappingOrigin origin, Eid key) {
+ if (origin == MappingOrigin.Southbound) {
+ removeFromSbTimeoutService(key);
+ }
tableMap.get(origin).removeMapping(key);
if (notificationService) {
// TODO
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.lispflowmapping.implementation.timebucket.containers;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.opendaylight.lispflowmapping.implementation.MappingSystem;
+import org.opendaylight.lispflowmapping.lisp.type.MappingData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.eid.container.Eid;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Created by Shakib Ahmed on 12/1/16.
+ */
+public class TimeBucket {
+ private static final Logger LOG = LoggerFactory.getLogger(TimeBucket.class);
+
+ private ConcurrentHashMap<Eid, MappingData> bucketElements;
+
+ private MappingSystem mappingSystem;
+
+ public TimeBucket(MappingSystem mappingSystem) {
+ bucketElements = new ConcurrentHashMap<>();
+ this.mappingSystem = mappingSystem;
+ }
+
+ public void add(Eid key, MappingData mappingData) {
+ bucketElements.put(key, mappingData);
+ }
+
+ public void removeFromBucketOnly(Eid key) {
+ MappingData mappingData = bucketElements.get(key);
+
+ if (mappingData != null) {
+ bucketElements.remove(key);
+ }
+ }
+
+ public void clearBucket() {
+ bucketElements.forEach((key, mappingData) -> {
+ mappingSystem.handleSbExpiredMapping(key, null, mappingData);
+ });
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.lispflowmapping.implementation.timebucket.containers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.opendaylight.lispflowmapping.implementation.MappingSystem;
+import org.opendaylight.lispflowmapping.lisp.type.MappingData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.eid.container.Eid;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Created by Shakib Ahmed on 12/1/16.
+ */
+public class TimeBucketWheel {
+ private static final Logger LOG = LoggerFactory.getLogger(TimeBucketWheel.class);
+
+ private int currentBucketId;
+ private int numberOfBuckets;
+ private long lastRotationTimestamp;
+
+ private List<TimeBucket> bucketList;
+
+ private long timeFrame;
+
+ public TimeBucketWheel(int numberOfBuckets, long mappingRecordValidityInMilis, MappingSystem mappingSystem) {
+
+ if (numberOfBuckets <= 1) {
+ throw new IllegalArgumentException("Expected number of buckets "
+ + "in TimeBucketMappingContainer to be more 1");
+ }
+
+ this.numberOfBuckets = numberOfBuckets;
+
+ initializeBucketList(mappingSystem);
+ timeFrame = (long) Math.ceil(1.0 * mappingRecordValidityInMilis / (numberOfBuckets - 1));
+ lastRotationTimestamp = System.currentTimeMillis();
+ currentBucketId = 0;
+ }
+
+ private void initializeBucketList(MappingSystem mappingSystem) {
+ bucketList = new ArrayList<>();
+ for (int i = 0; i < numberOfBuckets; i++) {
+ bucketList.add(new TimeBucket(mappingSystem));
+ }
+ }
+
+ public int add(Eid key, MappingData mappingData, long timestamp) {
+ clearExpiredMappingAndRotate(timestamp);
+ int timeBucketId = getProperBucketId(timestamp);
+
+ TimeBucket properTimeBucket = getBucket(timeBucketId);
+ properTimeBucket.add(key, mappingData);
+ return timeBucketId;
+ }
+
+ public int refreshMappping(Eid key, MappingData newMappingData, long timestamp, int bucketId) {
+ TimeBucket timeBucket = getBucket(bucketId);
+ timeBucket.removeFromBucketOnly(key);
+ return add(key, newMappingData, timestamp);
+ }
+
+ public void removeMapping(Eid key, int bucketId) {
+ TimeBucket timeBucket = getBucket(bucketId);
+ timeBucket.removeFromBucketOnly(key);
+ }
+
+ private int getLastBucketId() {
+ return (currentBucketId - 1 + numberOfBuckets) % numberOfBuckets;
+ }
+
+ private int getProperBucketId(long timestamp) {
+ if (timestamp > lastRotationTimestamp) {
+ //after rotation we are at current
+ return currentBucketId;
+ }
+
+ int relativeBucketId = (int) ((lastRotationTimestamp - timestamp) / timeFrame);
+ if (relativeBucketId >= numberOfBuckets) {
+ //too old scenario
+ LOG.error("The mapping that is being added is too old! This should not happen.");
+ return getLastBucketId();
+ }
+
+ return (this.currentBucketId + relativeBucketId) % numberOfBuckets;
+ }
+
+ private TimeBucket getBucket(int bucketId) {
+ return bucketList.get(bucketId);
+ }
+
+ public void clearExpiredMappingAndRotate() {
+ clearExpiredMappingAndRotate(System.currentTimeMillis());
+ }
+
+ public void clearExpiredMappingAndRotate(long currentStamp) {
+ int numberOfRotationToPerform = getNumberOfRotationsToPerform(currentStamp);
+
+ long timeForwarded = 0;
+
+ while (numberOfRotationToPerform > 0) {
+ clearExpiredBucket();
+ rotate();
+ numberOfRotationToPerform--;
+ timeForwarded += timeFrame;
+ }
+
+ lastRotationTimestamp = lastRotationTimestamp + timeForwarded;
+ }
+
+ private int getNumberOfRotationsToPerform(long currentStamp) {
+ if (currentStamp < lastRotationTimestamp) {
+ return 0;
+ }
+
+ long durationInMillis = (currentStamp - lastRotationTimestamp);
+
+ int numberOfRotationToPerform = (int) (1.0 * durationInMillis / timeFrame);
+ numberOfRotationToPerform = Math.min(numberOfRotationToPerform, numberOfBuckets);
+
+ return numberOfRotationToPerform;
+ }
+
+ private void clearExpiredBucket() {
+ int timeoutBucketId = getLastBucketId();
+ clearSpecificBucket(timeoutBucketId);
+ }
+
+ private void clearSpecificBucket(int bucketId) {
+ TimeBucket bucket = getBucket(bucketId);
+ bucket.clearBucket();
+ }
+
+ private void rotate() {
+ currentBucketId = getLastBucketId();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.lispflowmapping.implementation.timebucket.implementation;
+
+import org.opendaylight.lispflowmapping.implementation.MappingSystem;
+import org.opendaylight.lispflowmapping.implementation.timebucket.containers.TimeBucketWheel;
+import org.opendaylight.lispflowmapping.implementation.timebucket.interfaces.ISouthBoundMappingTimeoutService;
+import org.opendaylight.lispflowmapping.lisp.type.MappingData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.eid.container.Eid;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Created by Shakib Ahmed on 12/1/16.
+ */
+public class TimeBucketMappingTimeoutService implements ISouthBoundMappingTimeoutService {
+ private static final Logger LOG = LoggerFactory.getLogger(TimeBucketWheel.class);
+
+ TimeBucketWheel timeBucketWheel;
+
+ public TimeBucketMappingTimeoutService(int numberOfBucket, long mappingRecordValidityInMillis,
+ MappingSystem mappingSystem) {
+ timeBucketWheel = new TimeBucketWheel(numberOfBucket, mappingRecordValidityInMillis, mappingSystem);
+ }
+
+ @Override
+ public int addMapping(Eid key, MappingData mappingData) {
+ long timestamp = System.currentTimeMillis();
+ if (mappingData.getTimestamp() != null) {
+ timestamp = mappingData.getTimestamp().getTime();
+ }
+ return timeBucketWheel.add(key, mappingData, timestamp);
+ }
+
+ @Override
+ public int refreshMapping(Eid key, MappingData newMappingData, int presentBucketId) {
+ long timestamp = System.currentTimeMillis();
+ if (newMappingData.getTimestamp() != null) {
+ timestamp = newMappingData.getTimestamp().getTime();
+ }
+ return timeBucketWheel.refreshMappping(key, newMappingData, timestamp, presentBucketId);
+ }
+
+ @Override
+ public void removeMappingFromTimeoutService(Eid key, int presentBucketId) {
+ timeBucketWheel.removeMapping(key, presentBucketId);
+ }
+
+ @Override
+ public void removeExpiredMappings() {
+ timeBucketWheel.clearExpiredMappingAndRotate();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.lispflowmapping.implementation.timebucket.interfaces;
+
+import org.opendaylight.lispflowmapping.lisp.type.MappingData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.eid.container.Eid;
+
+/**
+ * Created by Shakib Ahmed on 12/1/16.
+ */
+public interface ISouthBoundMappingTimeoutService {
+
+ /**
+ * Add mapping in Southbound Mapping Timeout Manager
+ * which is currently Time Bucket Wheel.
+ *
+ * @param key
+ * The key for the mapping
+ * @param mappingData
+ * Mapping to be stored
+ * @return The id of the bucket the mapping was added to
+ */
+ int addMapping(Eid key, MappingData mappingData);
+
+ /**
+ * Refresh mapping in southbound manager. Remove old mapping
+ * from Time Bucket Wheel and add the mapping in proper time bucket.
+ * This is either because mapping re-registration or new merged
+ * mapping and refresh mapping request in MS/MR.
+ *
+ * @param key
+ * The key for the mapping
+ * @param newMappingData
+ * New Mapping Data for the key
+ * @param presentBucketId
+ * The id of the bucket the previous mapping is in
+ * @return The new id of the bucket the mapping was added to
+ */
+ int refreshMapping(Eid key, MappingData newMappingData, int presentBucketId);
+
+ /**
+ * Remove mapping from Southbound manager.
+ *
+ * @param key
+ * The key for the mapping
+ * @param presentBucketId
+ * The id of the present bucket the key is in
+ */
+ void removeMappingFromTimeoutService(Eid key, int presentBucketId);
+
+ /**
+ * Remove the expired mappings from the Time Bucket Wheel. This
+ * should remove mapping from both SimpleMapCache and DataStoreBackEnd.
+ */
+ void removeExpiredMappings();
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.lispflowmapping.implementation.timebucket;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.lispflowmapping.config.ConfigIni;
+import org.opendaylight.lispflowmapping.implementation.MappingSystem;
+import org.opendaylight.lispflowmapping.implementation.timebucket.containers.TimeBucket;
+import org.opendaylight.lispflowmapping.implementation.timebucket.containers.TimeBucketWheel;
+import org.opendaylight.lispflowmapping.interfaces.dao.ILispDAO;
+import org.opendaylight.lispflowmapping.lisp.type.MappingData;
+import org.opendaylight.lispflowmapping.lisp.util.LispAddressUtil;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.inet.binary.types.rev160303.IpAddressBinary;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.inet.binary.types.rev160303.Ipv4AddressBinary;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.SiteId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.XtrId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.eid.container.Eid;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.mapping.record.container.MappingRecord;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.mapping.record.container.MappingRecordBuilder;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.powermock.modules.junit4.PowerMockRunnerDelegate;
+import org.powermock.reflect.Whitebox;
+
+/**
+ * Created by Shakib Ahmed on 12/13/16.
+ */
+
+@RunWith(PowerMockRunner.class)
+@PowerMockRunnerDelegate(MockitoJUnitRunner.class)
+@PrepareForTest(TimeBucketWheel.class)
+public class TimeBucketWheelUnitTest {
+
+ private static final String IPV4_STRING_1 = "1.2.3.0";
+ private static final String IPV4_STRING_2 = "1.2.4.0";
+ private static final String IPV4_STRING_3 = "1.2.5.0";
+ private static final String IPV4_STRING_4 = "1.2.6.0";
+ private static final String IPV4_STRING_5 = "1.2.7.0";
+ private static final Eid IPV4_EID_1 = LispAddressUtil.asIpv4Eid(IPV4_STRING_1);
+ private static final Eid IPV4_EID_2 = LispAddressUtil.asIpv4Eid(IPV4_STRING_2);
+ private static final Eid IPV4_EID_3 = LispAddressUtil.asIpv4Eid(IPV4_STRING_3);
+ private static final Eid IPV4_EID_4 = LispAddressUtil.asIpv4Eid(IPV4_STRING_4);
+ private static final Eid IPV4_EID_5 = LispAddressUtil.asIpv4Eid(IPV4_STRING_5);
+ private static final IpAddressBinary IPV4_SOURCE_RLOC_1 = new IpAddressBinary(
+ new Ipv4AddressBinary(new byte[] {1, 1, 1, 1}));
+
+ private static final int NUMBER_OF_BUCKETS = 4;
+
+ private static final XtrId XTR_ID_1 = new XtrId(new byte[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1});
+
+ private static final SiteId SITE_ID_1 = new SiteId(new byte[]{1, 1, 1, 1, 1, 1, 1, 1});
+
+ private static final long REGISTRATION_VALIDITY = ConfigIni.getInstance().getRegistrationValiditySb();
+
+ private static ILispDAO daoMock = Mockito.mock(ILispDAO.class);
+ private static MappingSystem mappingSystem = Mockito.mock(MappingSystem.class);
+
+ /**
+ * Tests {@link TimeBucketWheel#add(Eid, MappingData, long)} method for general case.
+ */
+ @Test
+ public void mappingAddedInTheProperBucketGeneralTest() {
+ PowerMockito.mockStatic(System.class);
+
+ long frozenTimeStamp = System.currentTimeMillis();
+ PowerMockito.when(System.currentTimeMillis()).thenReturn(frozenTimeStamp);
+
+ TimeBucketWheel timeBucketWheel = getDefaultTimeBucketWheel();
+
+ final int bucketId1 = timeBucketWheel.add(IPV4_EID_1, getDefaultMappingData(IPV4_EID_1),
+ System.currentTimeMillis());
+
+ frozenTimeStamp += 1000;
+ PowerMockito.when(System.currentTimeMillis()).thenReturn(frozenTimeStamp);
+
+ final int bucketId2 = timeBucketWheel.add(IPV4_EID_2, getDefaultMappingData(IPV4_EID_2),
+ System.currentTimeMillis());
+
+ frozenTimeStamp += 1000;
+ PowerMockito.when(System.currentTimeMillis()).thenReturn(frozenTimeStamp);
+
+ final int bucketId3 = timeBucketWheel.add(IPV4_EID_3, getDefaultMappingData(IPV4_EID_3),
+ System.currentTimeMillis());
+
+ frozenTimeStamp += 1000;
+ PowerMockito.when(System.currentTimeMillis()).thenReturn(frozenTimeStamp);
+
+ final int bucketId4 = timeBucketWheel.add(IPV4_EID_4, getDefaultMappingData(IPV4_EID_4),
+ System.currentTimeMillis());
+
+ frozenTimeStamp += 1000;
+ PowerMockito.when(System.currentTimeMillis()).thenReturn(frozenTimeStamp);
+
+ final int bucketId5 = timeBucketWheel.add(IPV4_EID_5, getDefaultMappingData(IPV4_EID_5),
+ System.currentTimeMillis());
+
+ Assert.assertEquals((bucketId1 - 1 + NUMBER_OF_BUCKETS) % NUMBER_OF_BUCKETS, bucketId2);
+ Assert.assertEquals((bucketId2 - 1 + NUMBER_OF_BUCKETS) % NUMBER_OF_BUCKETS, bucketId3);
+ Assert.assertEquals((bucketId3 - 1 + NUMBER_OF_BUCKETS) % NUMBER_OF_BUCKETS, bucketId4);
+ Assert.assertEquals((bucketId4 - 1 + NUMBER_OF_BUCKETS) % NUMBER_OF_BUCKETS, bucketId5);
+ }
+
+ /**
+ * Tests {@link TimeBucketWheel#add(Eid, MappingData, long)} method for add in some bucket in the middle.
+ */
+ @Test
+ public void mappingAddedInTheProperBucketAddInMiddleTest() throws Exception {
+ PowerMockito.mockStatic(System.class);
+
+ long frozenTimeStamp = System.currentTimeMillis();
+ PowerMockito.when(System.currentTimeMillis()).thenReturn(frozenTimeStamp);
+
+ TimeBucketWheel timeBucketWheel = getDefaultTimeBucketWheel();
+
+ checkTooOldMappingCase(timeBucketWheel);
+
+ checkOlderThanCurrentCase(timeBucketWheel);
+
+ }
+
+ private void checkTooOldMappingCase(TimeBucketWheel timeBucketWheel) throws Exception {
+ long frozenTimeStamp = System.currentTimeMillis() - 10000;
+ PowerMockito.when(System.currentTimeMillis()).thenReturn(frozenTimeStamp);
+
+ int bucketId = timeBucketWheel.add(IPV4_EID_1, getDefaultMappingData(IPV4_EID_1),
+ System.currentTimeMillis());
+
+ int idOfLastBucketInBucketWheel = Whitebox.invokeMethod(timeBucketWheel,
+ "getLastBucketId");
+
+ Assert.assertEquals(idOfLastBucketInBucketWheel, bucketId);
+
+ //Time resetting to old frozen time
+ frozenTimeStamp = System.currentTimeMillis() + 10000;
+ PowerMockito.when(System.currentTimeMillis()).thenReturn(frozenTimeStamp);
+ }
+
+ private void checkOlderThanCurrentCase(TimeBucketWheel timeBucketWheel) {
+ long frozenTimeStamp = System.currentTimeMillis();
+
+ int bucketId1 = timeBucketWheel.add(IPV4_EID_1, getDefaultMappingData(IPV4_EID_1),
+ frozenTimeStamp);
+
+ MappingData toBeExpiredMappingData = getDefaultMappingData(IPV4_EID_2);
+
+ int bucketId2 = timeBucketWheel.add(IPV4_EID_2, toBeExpiredMappingData,
+ frozenTimeStamp - 1000);
+
+ Assert.assertEquals((bucketId1 + 1) % NUMBER_OF_BUCKETS, bucketId2);
+
+ //expired at proper time
+ frozenTimeStamp = frozenTimeStamp + 3000;
+ PowerMockito.when(System.currentTimeMillis()).thenReturn(frozenTimeStamp);
+
+ timeBucketWheel.clearExpiredMappingAndRotate();
+
+ Mockito.verify(mappingSystem).handleSbExpiredMapping(IPV4_EID_2, null, toBeExpiredMappingData);
+ }
+
+
+ /**
+ * Tests {@link TimeBucketWheel#refreshMappping(Eid, MappingData, long, int)} method.
+ * {@link ClassCastException} can be thrown.
+ */
+ @Test
+ public void mappingRefreshedProperlyTest() {
+ PowerMockito.mockStatic(System.class);
+
+ long frozenTimeStamp = System.currentTimeMillis();
+ PowerMockito.when(System.currentTimeMillis()).thenReturn(frozenTimeStamp);
+
+ TimeBucketWheel timeBucketWheel = getDefaultTimeBucketWheel();
+
+ final int bucketId1 = timeBucketWheel.add(IPV4_EID_1, getDefaultMappingData(IPV4_EID_1),
+ System.currentTimeMillis());
+
+ frozenTimeStamp += 2000;
+ PowerMockito.when(System.currentTimeMillis()).thenReturn(frozenTimeStamp);
+
+ MappingData newMappingData = getDefaultMappingData(IPV4_EID_1);
+
+ int currentBucketId = timeBucketWheel.refreshMappping(IPV4_EID_1, newMappingData,
+ System.currentTimeMillis(), bucketId1);
+
+ List<TimeBucket> bucketList = extractBucketList(timeBucketWheel);
+
+ TimeBucket pastTimeBucket = bucketList.get(bucketId1);
+
+ MappingData oldStoredMappingData = getMappingDataFromTimeBucket(pastTimeBucket, IPV4_EID_1);
+
+ Assert.assertNull(oldStoredMappingData);
+
+ TimeBucket presentTimeBucket = bucketList.get(currentBucketId);
+
+ MappingData newStoredMappingData = getMappingDataFromTimeBucket(presentTimeBucket, IPV4_EID_1);
+
+ Assert.assertEquals(newMappingData, newStoredMappingData);
+ }
+
+ private List<TimeBucket> extractBucketList(TimeBucketWheel timeBucketWheel) {
+ List<TimeBucket> bucketList;
+ try {
+ bucketList = (List<TimeBucket>) Whitebox.getInternalState(timeBucketWheel, "bucketList");
+ } catch (ClassCastException e) {
+ throw e;
+ }
+ return bucketList;
+ }
+
+ private MappingData getMappingDataFromTimeBucket(TimeBucket timeBucket, Eid eid) {
+ ConcurrentHashMap<Eid, MappingData> bucketElements;
+
+ try {
+ bucketElements = (ConcurrentHashMap<Eid, MappingData>) Whitebox.getInternalState(timeBucket,
+ "bucketElements");
+ } catch (ClassCastException e) {
+ throw e;
+ }
+
+ return bucketElements.get(eid);
+ }
+
+ /**
+ * Tests {@link TimeBucketWheel#clearExpiredMappingAndRotate(long)} method.
+ * {@link ClassCastException} can be thrown.
+ */
+ @Test
+ public void expiredMappingClearedProperlyTest() {
+ PowerMockito.mockStatic(System.class);
+
+ long frozenTimeStamp = System.currentTimeMillis();
+ PowerMockito.when(System.currentTimeMillis()).thenReturn(frozenTimeStamp);
+
+ TimeBucketWheel timeBucketWheel = getDefaultTimeBucketWheel();
+
+ MappingData mappingData = getDefaultMappingData(IPV4_EID_1);
+ timeBucketWheel.add(IPV4_EID_1, mappingData,
+ System.currentTimeMillis());
+
+ frozenTimeStamp = System.currentTimeMillis() + 4000;
+ PowerMockito.when(System.currentTimeMillis()).thenReturn(frozenTimeStamp);
+
+ timeBucketWheel.clearExpiredMappingAndRotate(frozenTimeStamp);
+
+ Mockito.verify(mappingSystem).handleSbExpiredMapping(IPV4_EID_1, null, mappingData);
+ }
+
+
+ private MappingData getDefaultMappingDataWithProperTimestamp(Eid eid, long timeStamp) {
+ MappingData mappingData = getDefaultMappingData(eid);
+ Date date = new Date();
+ date.setTime(timeStamp);
+ mappingData.setTimestamp(date);
+ return mappingData;
+ }
+
+ private static TimeBucketWheel getDefaultTimeBucketWheel() {
+ return new TimeBucketWheel(4, 3000, mappingSystem);
+ }
+
+ private static MappingData getDefaultMappingData(Eid eid) {
+ return getDefaultMappingData(eid,null);
+ }
+
+ private static MappingData getDefaultMappingData(Eid eid, MappingRecord mappingRecord) {
+ if (mappingRecord == null) {
+ mappingRecord = getDefaultMappingRecordBuilder(eid).build();
+ }
+ return new MappingData(mappingRecord, System.currentTimeMillis());
+ }
+
+ private static MappingRecordBuilder getDefaultMappingRecordBuilder(Eid eid) {
+ return new MappingRecordBuilder()
+ .setEid(eid)
+ .setLocatorRecord(new ArrayList<>())
+ .setRecordTtl(2)
+ .setAction(MappingRecord.Action.NativelyForward)
+ .setAuthoritative(true)
+ .setMapVersion((short) 1)
+ .setSiteId(SITE_ID_1)
+ .setSourceRloc(IPV4_SOURCE_RLOC_1)
+ .setTimestamp(new Date().getTime())
+ .setXtrId(XTR_ID_1);
+ }
+}
\ No newline at end of file
table.removeSpecific(key, SubKeys.RECORD);
table.removeSpecific(key, SubKeys.SRC_RLOCS);
table.removeSpecific(key, SubKeys.XTRID_RECORDS);
+ table.removeSpecific(key, SubKeys.TIME_BUCKET_ID);
}
@Override
Mockito.verify(tableMock).removeSpecific(MaskUtil.normalize(EID_IPV4), SubKeys.RECORD);
Mockito.verify(tableMock).removeSpecific(MaskUtil.normalize(EID_IPV4), SubKeys.SRC_RLOCS);
Mockito.verify(tableMock).removeSpecific(MaskUtil.normalize(EID_IPV4), SubKeys.XTRID_RECORDS);
+ Mockito.verify(tableMock).removeSpecific(MaskUtil.normalize(EID_IPV4), SubKeys.TIME_BUCKET_ID);
Mockito.verifyNoMoreInteractions(tableMock);
}