blob: efbffb6d0f2a34ba5989525906f5bce11a2ee5dc [file] [log] [blame]
Jonathan Hart23701d12014-04-03 10:45:48 -07001package net.onrc.onos.core.flowprogrammer;
Naoki Shiota8df97bc2014-03-13 18:42:23 -07002
Jonathan Harta88fd242014-04-03 11:24:54 -07003import static org.junit.Assert.assertEquals;
4import static org.junit.Assert.assertTrue;
5import static org.junit.Assert.fail;
Naoki Shiota8df97bc2014-03-13 18:42:23 -07006
7import java.io.IOException;
8import java.util.ArrayList;
9import java.util.List;
10import java.util.concurrent.ExecutionException;
11import java.util.concurrent.Future;
12
13import net.floodlightcontroller.core.IOFSwitch;
Jonathan Hart23701d12014-04-03 10:45:48 -070014import net.onrc.onos.core.flowprogrammer.IFlowPusherService.MsgPriority;
15import net.onrc.onos.core.flowprogrammer.IFlowSyncService.SyncResult;
16import net.onrc.onos.core.util.FlowEntry;
Naoki Shiota8df97bc2014-03-13 18:42:23 -070017
18import org.easymock.EasyMock;
19import org.easymock.IAnswer;
20import org.junit.After;
21import org.junit.Before;
22import org.junit.Ignore;
23import org.junit.Test;
24import org.junit.runner.RunWith;
25import org.openflow.protocol.OFFlowMod;
26import org.openflow.protocol.OFMatch;
27import org.openflow.protocol.OFMessage;
28import org.openflow.protocol.OFStatisticsRequest;
29import org.openflow.protocol.OFType;
30import org.openflow.protocol.statistics.OFFlowStatisticsReply;
31import org.openflow.protocol.statistics.OFStatistics;
Naoki Shiota8df97bc2014-03-13 18:42:23 -070032import org.powermock.core.classloader.annotations.PrepareForTest;
33import org.powermock.modules.junit4.PowerMockRunner;
34
35// Test should be fixed to fit RAMCloud basis
36@Ignore
37@RunWith(PowerMockRunner.class)
Yuta HIGUCHI44a0b352014-05-14 21:32:48 -070038@PrepareForTest({FlowSynchronizer.class })
Naoki Shiota8df97bc2014-03-13 18:42:23 -070039public class FlowSynchronizerTest {
Ray Milkey269ffb92014-04-03 14:43:30 -070040 private FlowPusher pusher;
41 private FlowSynchronizer sync;
42 private List<Long> idAdded;
43 private List<Long> idRemoved;
Naoki Shiota8df97bc2014-03-13 18:42:23 -070044
Ray Milkey269ffb92014-04-03 14:43:30 -070045 @Before
46 public void setUp() throws Exception {
47 idAdded = new ArrayList<Long>();
48 idRemoved = new ArrayList<Long>();
Naoki Shiota8df97bc2014-03-13 18:42:23 -070049
Ray Milkey269ffb92014-04-03 14:43:30 -070050 pusher = EasyMock.createMock(FlowPusher.class);
51 EasyMock.expect(pusher.suspend(EasyMock.anyObject(IOFSwitch.class))).andReturn(true).anyTimes();
52 EasyMock.expect(pusher.resume(EasyMock.anyObject(IOFSwitch.class))).andReturn(true).anyTimes();
53 pusher.add(EasyMock.anyObject(IOFSwitch.class), EasyMock.anyObject(OFMessage.class),
54 EasyMock.eq(MsgPriority.HIGH));
55 EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
56 @Override
57 public Object answer() throws Throwable {
58 OFMessage msg = (OFMessage) EasyMock.getCurrentArguments()[1];
59 if (msg.getType().equals(OFType.FLOW_MOD)) {
60 OFFlowMod fm = (OFFlowMod) msg;
61 if (fm.getCommand() == OFFlowMod.OFPFC_DELETE_STRICT) {
62 idRemoved.add(fm.getCookie());
63 }
64 }
65 return null;
66 }
67 }).anyTimes();
68 pusher.pushFlowEntry(EasyMock.anyObject(IOFSwitch.class), EasyMock.anyObject(FlowEntry.class),
69 EasyMock.eq(MsgPriority.HIGH));
70 EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
71 @Override
72 public Object answer() throws Throwable {
73 FlowEntry flow = (FlowEntry) EasyMock.getCurrentArguments()[1];
74 idAdded.add(flow.flowEntryId().value());
75 return null;
76 }
77 }).anyTimes();
78 EasyMock.replay(pusher);
79 }
Naoki Shiota8df97bc2014-03-13 18:42:23 -070080
Ray Milkey269ffb92014-04-03 14:43:30 -070081 @After
82 public void tearDown() throws Exception {
83 }
Naoki Shiota8df97bc2014-03-13 18:42:23 -070084
Ray Milkey269ffb92014-04-03 14:43:30 -070085 /**
86 * Test that synchronization doesn't affect anything in case either DB and
87 * flow table has the same entries.
88 */
89 @Test
90 public void testStable() {
91 // Create mock of flow table : flow 1
92 IOFSwitch sw = createMockSwitch(new long[]{1});
Naoki Shiota8df97bc2014-03-13 18:42:23 -070093
Ray Milkey269ffb92014-04-03 14:43:30 -070094 // Create mock of flow entries : flow 1
95 initMockGraph(new long[]{1});
Naoki Shiota8df97bc2014-03-13 18:42:23 -070096
Ray Milkey269ffb92014-04-03 14:43:30 -070097 // synchronize
98 doSynchronization(sw);
Naoki Shiota8df97bc2014-03-13 18:42:23 -070099
Ray Milkey269ffb92014-04-03 14:43:30 -0700100 // check if flow is not changed
101 assertEquals(0, idAdded.size());
102 assertEquals(0, idRemoved.size());
103 }
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700104
Ray Milkey269ffb92014-04-03 14:43:30 -0700105 /**
106 * Test that an flow is added in case DB has an extra FlowEntry.
107 */
108 @Test
109 public void testSingleAdd() {
110 // Create mock of flow table : null
111 IOFSwitch sw = createMockSwitch(new long[]{});
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700112
Ray Milkey269ffb92014-04-03 14:43:30 -0700113 // Create mock of flow entries : flow 1
114 initMockGraph(new long[]{1});
115
116 // synchronize
117 doSynchronization(sw);
118
119 // check if single flow is installed
120 assertEquals(1, idAdded.size());
121 assertTrue(idAdded.contains((long) 1));
122 assertEquals(0, idRemoved.size());
123 }
124
125 /**
126 * Test that an flow is deleted in case switch has an extra FlowEntry.
127 */
128 @Test
129 public void testSingleDelete() {
130 // Create mock of flow table : flow 1
131 IOFSwitch sw = createMockSwitch(new long[]{1});
132
133 // Create mock of flow entries : null
134 initMockGraph(new long[]{});
135
136 // synchronize
137 doSynchronization(sw);
138
139 // check if single flow is deleted
140 assertEquals(0, idAdded.size());
141 assertEquals(1, idRemoved.size());
142 assertTrue(idRemoved.contains((long) 1));
143 }
144
145 /**
146 * Test that appropriate flows are added and other appropriate flows are deleted
147 * in case flows in DB are overlapping flows in switch.
148 */
149 @Test
150 public void testMixed() {
151 // Create mock of flow table : flow 1,2,3
152 IOFSwitch sw = createMockSwitch(new long[]{1, 2, 3});
153
154 // Create mock of flow entries : flow 2,3,4,5
155 initMockGraph(new long[]{2, 3, 4, 5});
156
157 // synchronize
158 doSynchronization(sw);
159
160 // check if two flows {4,5} is installed and one flow {1} is deleted
161 assertEquals(2, idAdded.size());
162 assertTrue(idAdded.contains((long) 4));
163 assertTrue(idAdded.contains((long) 5));
164 assertEquals(1, idRemoved.size());
165 assertTrue(idRemoved.contains((long) 1));
166 }
167
168
169 @Test
170 public void testMassive() {
171 // Create mock of flow table : flow 0-1999
172 long[] swIdList = new long[2000];
173 for (long i = 0; i < 2000; ++i) {
174 swIdList[(int) i] = i;
175 }
176 IOFSwitch sw = createMockSwitch(swIdList);
177
178 // Create mock of flow entries : flow 1500-3499
179 long[] dbIdList = new long[2000];
180 for (long i = 0; i < 2000; ++i) {
181 dbIdList[(int) i] = 1500 + i;
182 }
183 initMockGraph(dbIdList);
184
185 // synchronize
186 doSynchronization(sw);
187
188 // check if 1500 flows {2000-3499} is installed and 1500 flows {0,...,1499} is deleted
189 assertEquals(1500, idAdded.size());
190 for (long i = 2000; i < 3500; ++i) {
191 assertTrue(idAdded.contains(i));
192 }
193 assertEquals(1500, idRemoved.size());
194 for (long i = 0; i < 1500; ++i) {
195 assertTrue(idRemoved.contains(i));
196 }
197 }
198
199 /**
200 * Create mock IOFSwitch with flow table which has arbitrary flows.
201 *
202 * @param cookieList List of FlowEntry IDs switch has.
203 * @return Mock object.
204 */
205 private IOFSwitch createMockSwitch(long[] cookieList) {
206 IOFSwitch sw = EasyMock.createMock(IOFSwitch.class);
207 EasyMock.expect(sw.getId()).andReturn((long) 1).anyTimes();
208
209 List<OFStatistics> stats = new ArrayList<OFStatistics>();
210 for (long cookie : cookieList) {
211 stats.add(createReply(cookie));
212 }
213
214 @SuppressWarnings("unchecked")
215 Future<List<OFStatistics>> future = EasyMock.createMock(Future.class);
216 try {
217 EasyMock.expect(future.get()).andReturn(stats).once();
218 } catch (InterruptedException e1) {
219 fail("Failed in Future#get()");
220 } catch (ExecutionException e1) {
221 fail("Failed in Future#get()");
222 }
223 EasyMock.replay(future);
224
225 try {
226 EasyMock.expect(sw.getStatistics(EasyMock.anyObject(OFStatisticsRequest.class)))
227 .andReturn(future).once();
228 } catch (IOException e) {
229 fail("Failed in IOFSwitch#getStatistics()");
230 }
231
232 EasyMock.replay(sw);
233 return sw;
234 }
235
236 /**
237 * Create single OFFlowStatisticsReply object which is actually obtained from switch.
238 *
239 * @param cookie Cookie value, which indicates ID of FlowEntry installed to switch.
240 * @return Created object.
241 */
242 private OFFlowStatisticsReply createReply(long cookie) {
243 OFFlowStatisticsReply stat = new OFFlowStatisticsReply();
244 OFMatch match = new OFMatch();
245
246 stat.setCookie(cookie);
247 stat.setMatch(match);
248 stat.setPriority((short) 1);
249
250 return stat;
251 }
252
253 /**
254 * Create mock FlowDatabaseOperation to mock DB.
255 *
256 * @param idList List of FlowEntry IDs stored in DB.
257 */
258 private void initMockGraph(long[] idList) {
259 /*
260 * TODO: The old FlowDatabaseOperation class is gone, so the method
261 * below needs to be rewritten.
262 */
263 /*
264 List<IFlowEntry> flowEntryList = new ArrayList<IFlowEntry>();
Yuta HIGUCHI44a0b352014-05-14 21:32:48 -0700265
Ray Milkey269ffb92014-04-03 14:43:30 -0700266 for (long id : idList) {
267 IFlowEntry entry = EasyMock.createMock(IFlowEntry.class);
268 EasyMock.expect(entry.getFlowEntryId()).andReturn(String.valueOf(id)).anyTimes();
269 EasyMock.replay(entry);
270 flowEntryList.add(entry);
271 }
Yuta HIGUCHI44a0b352014-05-14 21:32:48 -0700272
Ray Milkey269ffb92014-04-03 14:43:30 -0700273 ISwitchObject swObj = EasyMock.createMock(ISwitchObject.class);
274 EasyMock.expect(swObj.getFlowEntries()).andReturn(flowEntryList).once();
275 EasyMock.replay(swObj);
Yuta HIGUCHI44a0b352014-05-14 21:32:48 -0700276
Ray Milkey269ffb92014-04-03 14:43:30 -0700277 DBOperation mockOp = PowerMock.createMock(DBOperation.class);
278 EasyMock.expect(mockOp.searchSwitch(EasyMock.anyObject(String.class))).andReturn(swObj).once();
Yuta HIGUCHI44a0b352014-05-14 21:32:48 -0700279
Ray Milkey269ffb92014-04-03 14:43:30 -0700280 PowerMock.mockStatic(FlowDatabaseOperation.class);
281 for (IFlowEntry entry : flowEntryList) {
282 EasyMock.expect(FlowDatabaseOperation.extractFlowEntry(EasyMock.eq(entry)))
283 .andAnswer(new IAnswer<FlowEntry>() {
284 @Override
285 public FlowEntry answer() throws Throwable {
286 IFlowEntry iflow = (IFlowEntry)EasyMock.getCurrentArguments()[0];
287 long flowEntryId = Long.valueOf(iflow.getFlowEntryId());
Yuta HIGUCHI44a0b352014-05-14 21:32:48 -0700288
Ray Milkey269ffb92014-04-03 14:43:30 -0700289 FlowEntry flow = EasyMock.createMock(FlowEntry.class);
290 EasyMock.expect(flow.flowEntryId()).andReturn(new FlowEntryId(flowEntryId)).anyTimes();
291 EasyMock.replay(flow);
292 return flow;
293 }
Yuta HIGUCHI44a0b352014-05-14 21:32:48 -0700294
Ray Milkey269ffb92014-04-03 14:43:30 -0700295 }).anyTimes();
296 EasyMock.expect(mockOp.searchFlowEntry(EasyMock.eq(new FlowEntryId(entry.getFlowEntryId()))))
297 .andReturn(entry);
298 }
299 PowerMock.replay(FlowDatabaseOperation.class);
300 EasyMock.replay(mockOp);
Yuta HIGUCHI44a0b352014-05-14 21:32:48 -0700301
Ray Milkey269ffb92014-04-03 14:43:30 -0700302 try {
303 PowerMock.expectNew(DBOperation.class).andReturn(mockOp);
304 } catch (Exception e) {
305 fail("Failed to create DBOperation");
306 }
307 PowerMock.replay(DBOperation.class);
308 */
309 }
310
311 /**
312 * Instantiate FlowSynchronizer and sync flows.
313 *
314 * @param sw Target IOFSwitch object
315 */
316 private void doSynchronization(IOFSwitch sw) {
317 sync = new FlowSynchronizer();
318 sync.init(pusher);
319 Future<SyncResult> future = sync.synchronize(sw);
320 try {
321 future.get();
322 } catch (Exception e) {
323 fail("Failed to Future#get()");
324 }
325 }
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700326}