blob: efbffb6d0f2a34ba5989525906f5bce11a2ee5dc [file] [log] [blame]
package net.onrc.onos.core.flowprogrammer;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import net.floodlightcontroller.core.IOFSwitch;
import net.onrc.onos.core.flowprogrammer.IFlowPusherService.MsgPriority;
import net.onrc.onos.core.flowprogrammer.IFlowSyncService.SyncResult;
import net.onrc.onos.core.util.FlowEntry;
import org.easymock.EasyMock;
import org.easymock.IAnswer;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.openflow.protocol.OFFlowMod;
import org.openflow.protocol.OFMatch;
import org.openflow.protocol.OFMessage;
import org.openflow.protocol.OFStatisticsRequest;
import org.openflow.protocol.OFType;
import org.openflow.protocol.statistics.OFFlowStatisticsReply;
import org.openflow.protocol.statistics.OFStatistics;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
// Test should be fixed to fit RAMCloud basis
@Ignore
@RunWith(PowerMockRunner.class)
@PrepareForTest({FlowSynchronizer.class })
public class FlowSynchronizerTest {
private FlowPusher pusher;
private FlowSynchronizer sync;
private List<Long> idAdded;
private List<Long> idRemoved;
@Before
public void setUp() throws Exception {
idAdded = new ArrayList<Long>();
idRemoved = new ArrayList<Long>();
pusher = EasyMock.createMock(FlowPusher.class);
EasyMock.expect(pusher.suspend(EasyMock.anyObject(IOFSwitch.class))).andReturn(true).anyTimes();
EasyMock.expect(pusher.resume(EasyMock.anyObject(IOFSwitch.class))).andReturn(true).anyTimes();
pusher.add(EasyMock.anyObject(IOFSwitch.class), EasyMock.anyObject(OFMessage.class),
EasyMock.eq(MsgPriority.HIGH));
EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
@Override
public Object answer() throws Throwable {
OFMessage msg = (OFMessage) EasyMock.getCurrentArguments()[1];
if (msg.getType().equals(OFType.FLOW_MOD)) {
OFFlowMod fm = (OFFlowMod) msg;
if (fm.getCommand() == OFFlowMod.OFPFC_DELETE_STRICT) {
idRemoved.add(fm.getCookie());
}
}
return null;
}
}).anyTimes();
pusher.pushFlowEntry(EasyMock.anyObject(IOFSwitch.class), EasyMock.anyObject(FlowEntry.class),
EasyMock.eq(MsgPriority.HIGH));
EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
@Override
public Object answer() throws Throwable {
FlowEntry flow = (FlowEntry) EasyMock.getCurrentArguments()[1];
idAdded.add(flow.flowEntryId().value());
return null;
}
}).anyTimes();
EasyMock.replay(pusher);
}
@After
public void tearDown() throws Exception {
}
/**
* Test that synchronization doesn't affect anything in case either DB and
* flow table has the same entries.
*/
@Test
public void testStable() {
// Create mock of flow table : flow 1
IOFSwitch sw = createMockSwitch(new long[]{1});
// Create mock of flow entries : flow 1
initMockGraph(new long[]{1});
// synchronize
doSynchronization(sw);
// check if flow is not changed
assertEquals(0, idAdded.size());
assertEquals(0, idRemoved.size());
}
/**
* Test that an flow is added in case DB has an extra FlowEntry.
*/
@Test
public void testSingleAdd() {
// Create mock of flow table : null
IOFSwitch sw = createMockSwitch(new long[]{});
// Create mock of flow entries : flow 1
initMockGraph(new long[]{1});
// synchronize
doSynchronization(sw);
// check if single flow is installed
assertEquals(1, idAdded.size());
assertTrue(idAdded.contains((long) 1));
assertEquals(0, idRemoved.size());
}
/**
* Test that an flow is deleted in case switch has an extra FlowEntry.
*/
@Test
public void testSingleDelete() {
// Create mock of flow table : flow 1
IOFSwitch sw = createMockSwitch(new long[]{1});
// Create mock of flow entries : null
initMockGraph(new long[]{});
// synchronize
doSynchronization(sw);
// check if single flow is deleted
assertEquals(0, idAdded.size());
assertEquals(1, idRemoved.size());
assertTrue(idRemoved.contains((long) 1));
}
/**
* Test that appropriate flows are added and other appropriate flows are deleted
* in case flows in DB are overlapping flows in switch.
*/
@Test
public void testMixed() {
// Create mock of flow table : flow 1,2,3
IOFSwitch sw = createMockSwitch(new long[]{1, 2, 3});
// Create mock of flow entries : flow 2,3,4,5
initMockGraph(new long[]{2, 3, 4, 5});
// synchronize
doSynchronization(sw);
// check if two flows {4,5} is installed and one flow {1} is deleted
assertEquals(2, idAdded.size());
assertTrue(idAdded.contains((long) 4));
assertTrue(idAdded.contains((long) 5));
assertEquals(1, idRemoved.size());
assertTrue(idRemoved.contains((long) 1));
}
@Test
public void testMassive() {
// Create mock of flow table : flow 0-1999
long[] swIdList = new long[2000];
for (long i = 0; i < 2000; ++i) {
swIdList[(int) i] = i;
}
IOFSwitch sw = createMockSwitch(swIdList);
// Create mock of flow entries : flow 1500-3499
long[] dbIdList = new long[2000];
for (long i = 0; i < 2000; ++i) {
dbIdList[(int) i] = 1500 + i;
}
initMockGraph(dbIdList);
// synchronize
doSynchronization(sw);
// check if 1500 flows {2000-3499} is installed and 1500 flows {0,...,1499} is deleted
assertEquals(1500, idAdded.size());
for (long i = 2000; i < 3500; ++i) {
assertTrue(idAdded.contains(i));
}
assertEquals(1500, idRemoved.size());
for (long i = 0; i < 1500; ++i) {
assertTrue(idRemoved.contains(i));
}
}
/**
* Create mock IOFSwitch with flow table which has arbitrary flows.
*
* @param cookieList List of FlowEntry IDs switch has.
* @return Mock object.
*/
private IOFSwitch createMockSwitch(long[] cookieList) {
IOFSwitch sw = EasyMock.createMock(IOFSwitch.class);
EasyMock.expect(sw.getId()).andReturn((long) 1).anyTimes();
List<OFStatistics> stats = new ArrayList<OFStatistics>();
for (long cookie : cookieList) {
stats.add(createReply(cookie));
}
@SuppressWarnings("unchecked")
Future<List<OFStatistics>> future = EasyMock.createMock(Future.class);
try {
EasyMock.expect(future.get()).andReturn(stats).once();
} catch (InterruptedException e1) {
fail("Failed in Future#get()");
} catch (ExecutionException e1) {
fail("Failed in Future#get()");
}
EasyMock.replay(future);
try {
EasyMock.expect(sw.getStatistics(EasyMock.anyObject(OFStatisticsRequest.class)))
.andReturn(future).once();
} catch (IOException e) {
fail("Failed in IOFSwitch#getStatistics()");
}
EasyMock.replay(sw);
return sw;
}
/**
* Create single OFFlowStatisticsReply object which is actually obtained from switch.
*
* @param cookie Cookie value, which indicates ID of FlowEntry installed to switch.
* @return Created object.
*/
private OFFlowStatisticsReply createReply(long cookie) {
OFFlowStatisticsReply stat = new OFFlowStatisticsReply();
OFMatch match = new OFMatch();
stat.setCookie(cookie);
stat.setMatch(match);
stat.setPriority((short) 1);
return stat;
}
/**
* Create mock FlowDatabaseOperation to mock DB.
*
* @param idList List of FlowEntry IDs stored in DB.
*/
private void initMockGraph(long[] idList) {
/*
* TODO: The old FlowDatabaseOperation class is gone, so the method
* below needs to be rewritten.
*/
/*
List<IFlowEntry> flowEntryList = new ArrayList<IFlowEntry>();
for (long id : idList) {
IFlowEntry entry = EasyMock.createMock(IFlowEntry.class);
EasyMock.expect(entry.getFlowEntryId()).andReturn(String.valueOf(id)).anyTimes();
EasyMock.replay(entry);
flowEntryList.add(entry);
}
ISwitchObject swObj = EasyMock.createMock(ISwitchObject.class);
EasyMock.expect(swObj.getFlowEntries()).andReturn(flowEntryList).once();
EasyMock.replay(swObj);
DBOperation mockOp = PowerMock.createMock(DBOperation.class);
EasyMock.expect(mockOp.searchSwitch(EasyMock.anyObject(String.class))).andReturn(swObj).once();
PowerMock.mockStatic(FlowDatabaseOperation.class);
for (IFlowEntry entry : flowEntryList) {
EasyMock.expect(FlowDatabaseOperation.extractFlowEntry(EasyMock.eq(entry)))
.andAnswer(new IAnswer<FlowEntry>() {
@Override
public FlowEntry answer() throws Throwable {
IFlowEntry iflow = (IFlowEntry)EasyMock.getCurrentArguments()[0];
long flowEntryId = Long.valueOf(iflow.getFlowEntryId());
FlowEntry flow = EasyMock.createMock(FlowEntry.class);
EasyMock.expect(flow.flowEntryId()).andReturn(new FlowEntryId(flowEntryId)).anyTimes();
EasyMock.replay(flow);
return flow;
}
}).anyTimes();
EasyMock.expect(mockOp.searchFlowEntry(EasyMock.eq(new FlowEntryId(entry.getFlowEntryId()))))
.andReturn(entry);
}
PowerMock.replay(FlowDatabaseOperation.class);
EasyMock.replay(mockOp);
try {
PowerMock.expectNew(DBOperation.class).andReturn(mockOp);
} catch (Exception e) {
fail("Failed to create DBOperation");
}
PowerMock.replay(DBOperation.class);
*/
}
/**
* Instantiate FlowSynchronizer and sync flows.
*
* @param sw Target IOFSwitch object
*/
private void doSynchronization(IOFSwitch sw) {
sync = new FlowSynchronizer();
sync.init(pusher);
Future<SyncResult> future = sync.synchronize(sw);
try {
future.get();
} catch (Exception e) {
fail("Failed to Future#get()");
}
}
}