blob: 654cb7a010a4ec4160826e945b85e0e98c7389cc [file] [log] [blame]
Jonathan Hart23701d12014-04-03 10:45:48 -07001package net.onrc.onos.core.flowprogrammer;
Naoki Shiota8df97bc2014-03-13 18:42:23 -07002
Sho SHIMIZU107344e2014-08-13 16:11:53 -07003import static org.easymock.EasyMock.anyObject;
4import static org.easymock.EasyMock.createMock;
5import static org.easymock.EasyMock.eq;
6import static org.easymock.EasyMock.expect;
7import static org.easymock.EasyMock.expectLastCall;
8import static org.easymock.EasyMock.getCurrentArguments;
9import static org.easymock.EasyMock.replay;
Jonathan Harta88fd242014-04-03 11:24:54 -070010import static org.junit.Assert.assertEquals;
11import static org.junit.Assert.assertTrue;
12import static org.junit.Assert.fail;
Naoki Shiota8df97bc2014-03-13 18:42:23 -070013
14import java.io.IOException;
15import java.util.ArrayList;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070016import java.util.Collections;
Naoki Shiota8df97bc2014-03-13 18:42:23 -070017import java.util.List;
18import java.util.concurrent.ExecutionException;
19import java.util.concurrent.Future;
20
21import net.floodlightcontroller.core.IOFSwitch;
Jonathan Hart23701d12014-04-03 10:45:48 -070022import net.onrc.onos.core.flowprogrammer.IFlowPusherService.MsgPriority;
23import net.onrc.onos.core.flowprogrammer.IFlowSyncService.SyncResult;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070024import net.onrc.onos.core.intent.FlowEntry;
Naoki Shiota8df97bc2014-03-13 18:42:23 -070025
Naoki Shiota8df97bc2014-03-13 18:42:23 -070026import org.easymock.IAnswer;
27import org.junit.After;
28import org.junit.Before;
29import org.junit.Ignore;
30import org.junit.Test;
31import org.junit.runner.RunWith;
Naoki Shiota8df97bc2014-03-13 18:42:23 -070032import org.powermock.core.classloader.annotations.PrepareForTest;
33import org.powermock.modules.junit4.PowerMockRunner;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070034import org.projectfloodlight.openflow.protocol.OFFactories;
35import org.projectfloodlight.openflow.protocol.OFFactory;
36import org.projectfloodlight.openflow.protocol.OFFlowMod;
37import org.projectfloodlight.openflow.protocol.OFFlowModCommand;
38import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
39import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
40import org.projectfloodlight.openflow.protocol.OFFlowStatsRequest;
41import org.projectfloodlight.openflow.protocol.OFMessage;
42import org.projectfloodlight.openflow.protocol.OFStatsReply;
43import org.projectfloodlight.openflow.protocol.OFType;
44import org.projectfloodlight.openflow.protocol.OFVersion;
45import org.projectfloodlight.openflow.types.U64;
Naoki Shiota8df97bc2014-03-13 18:42:23 -070046
47// Test should be fixed to fit RAMCloud basis
48@Ignore
49@RunWith(PowerMockRunner.class)
Yuta HIGUCHI44a0b352014-05-14 21:32:48 -070050@PrepareForTest({FlowSynchronizer.class })
Naoki Shiota8df97bc2014-03-13 18:42:23 -070051public class FlowSynchronizerTest {
Ray Milkey269ffb92014-04-03 14:43:30 -070052 private FlowPusher pusher;
53 private FlowSynchronizer sync;
54 private List<Long> idAdded;
55 private List<Long> idRemoved;
Naoki Shiota8df97bc2014-03-13 18:42:23 -070056
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070057 /*
58 * OF1.0 Factory for now. Change when we move to
59 * OF 1.3.
60 */
61 private static OFFactory factory10;
62
Ray Milkey269ffb92014-04-03 14:43:30 -070063 @Before
64 public void setUp() throws Exception {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070065 factory10 = OFFactories.getFactory(OFVersion.OF_10);
Ray Milkey269ffb92014-04-03 14:43:30 -070066 idAdded = new ArrayList<Long>();
67 idRemoved = new ArrayList<Long>();
Naoki Shiota8df97bc2014-03-13 18:42:23 -070068
Sho SHIMIZU107344e2014-08-13 16:11:53 -070069 pusher = createMock(FlowPusher.class);
70 expect(pusher.suspend(anyObject(IOFSwitch.class))).andReturn(true).anyTimes();
71 expect(pusher.resume(anyObject(IOFSwitch.class))).andReturn(true).anyTimes();
72 pusher.add(anyObject(IOFSwitch.class), anyObject(OFMessage.class),
73 eq(MsgPriority.HIGH));
74 expectLastCall().andAnswer(new IAnswer<Object>() {
Ray Milkey269ffb92014-04-03 14:43:30 -070075 @Override
76 public Object answer() throws Throwable {
Sho SHIMIZU107344e2014-08-13 16:11:53 -070077 OFMessage msg = (OFMessage) getCurrentArguments()[1];
Ray Milkey269ffb92014-04-03 14:43:30 -070078 if (msg.getType().equals(OFType.FLOW_MOD)) {
79 OFFlowMod fm = (OFFlowMod) msg;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070080 if (fm.getCommand() == OFFlowModCommand.DELETE_STRICT) {
81 idRemoved.add(fm.getCookie().getValue());
Ray Milkey269ffb92014-04-03 14:43:30 -070082 }
83 }
84 return null;
85 }
86 }).anyTimes();
Sho SHIMIZU107344e2014-08-13 16:11:53 -070087 pusher.pushFlowEntry(anyObject(IOFSwitch.class), anyObject(FlowEntry.class),
88 eq(MsgPriority.HIGH));
89 expectLastCall().andAnswer(new IAnswer<Object>() {
Ray Milkey269ffb92014-04-03 14:43:30 -070090 @Override
91 public Object answer() throws Throwable {
Sho SHIMIZU107344e2014-08-13 16:11:53 -070092 FlowEntry flow = (FlowEntry) getCurrentArguments()[1];
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070093 idAdded.add(flow.getFlowEntryId());
Ray Milkey269ffb92014-04-03 14:43:30 -070094 return null;
95 }
96 }).anyTimes();
Sho SHIMIZU107344e2014-08-13 16:11:53 -070097 replay(pusher);
Ray Milkey269ffb92014-04-03 14:43:30 -070098 }
Naoki Shiota8df97bc2014-03-13 18:42:23 -070099
Ray Milkey269ffb92014-04-03 14:43:30 -0700100 @After
101 public void tearDown() throws Exception {
102 }
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700103
Ray Milkey269ffb92014-04-03 14:43:30 -0700104 /**
105 * Test that synchronization doesn't affect anything in case either DB and
106 * flow table has the same entries.
107 */
108 @Test
109 public void testStable() {
110 // Create mock of flow table : flow 1
111 IOFSwitch sw = createMockSwitch(new long[]{1});
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});
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700115
Ray Milkey269ffb92014-04-03 14:43:30 -0700116 // synchronize
117 doSynchronization(sw);
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700118
Ray Milkey269ffb92014-04-03 14:43:30 -0700119 // check if flow is not changed
120 assertEquals(0, idAdded.size());
121 assertEquals(0, idRemoved.size());
122 }
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700123
Ray Milkey269ffb92014-04-03 14:43:30 -0700124 /**
125 * Test that an flow is added in case DB has an extra FlowEntry.
126 */
127 @Test
128 public void testSingleAdd() {
129 // Create mock of flow table : null
130 IOFSwitch sw = createMockSwitch(new long[]{});
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700131
Ray Milkey269ffb92014-04-03 14:43:30 -0700132 // Create mock of flow entries : flow 1
133 initMockGraph(new long[]{1});
134
135 // synchronize
136 doSynchronization(sw);
137
138 // check if single flow is installed
139 assertEquals(1, idAdded.size());
140 assertTrue(idAdded.contains((long) 1));
141 assertEquals(0, idRemoved.size());
142 }
143
144 /**
145 * Test that an flow is deleted in case switch has an extra FlowEntry.
146 */
147 @Test
148 public void testSingleDelete() {
149 // Create mock of flow table : flow 1
150 IOFSwitch sw = createMockSwitch(new long[]{1});
151
152 // Create mock of flow entries : null
153 initMockGraph(new long[]{});
154
155 // synchronize
156 doSynchronization(sw);
157
158 // check if single flow is deleted
159 assertEquals(0, idAdded.size());
160 assertEquals(1, idRemoved.size());
161 assertTrue(idRemoved.contains((long) 1));
162 }
163
164 /**
165 * Test that appropriate flows are added and other appropriate flows are deleted
166 * in case flows in DB are overlapping flows in switch.
167 */
168 @Test
169 public void testMixed() {
170 // Create mock of flow table : flow 1,2,3
171 IOFSwitch sw = createMockSwitch(new long[]{1, 2, 3});
172
173 // Create mock of flow entries : flow 2,3,4,5
174 initMockGraph(new long[]{2, 3, 4, 5});
175
176 // synchronize
177 doSynchronization(sw);
178
179 // check if two flows {4,5} is installed and one flow {1} is deleted
180 assertEquals(2, idAdded.size());
181 assertTrue(idAdded.contains((long) 4));
182 assertTrue(idAdded.contains((long) 5));
183 assertEquals(1, idRemoved.size());
184 assertTrue(idRemoved.contains((long) 1));
185 }
186
187
188 @Test
189 public void testMassive() {
190 // Create mock of flow table : flow 0-1999
191 long[] swIdList = new long[2000];
192 for (long i = 0; i < 2000; ++i) {
193 swIdList[(int) i] = i;
194 }
195 IOFSwitch sw = createMockSwitch(swIdList);
196
197 // Create mock of flow entries : flow 1500-3499
198 long[] dbIdList = new long[2000];
199 for (long i = 0; i < 2000; ++i) {
200 dbIdList[(int) i] = 1500 + i;
201 }
202 initMockGraph(dbIdList);
203
204 // synchronize
205 doSynchronization(sw);
206
207 // check if 1500 flows {2000-3499} is installed and 1500 flows {0,...,1499} is deleted
208 assertEquals(1500, idAdded.size());
209 for (long i = 2000; i < 3500; ++i) {
210 assertTrue(idAdded.contains(i));
211 }
212 assertEquals(1500, idRemoved.size());
213 for (long i = 0; i < 1500; ++i) {
214 assertTrue(idRemoved.contains(i));
215 }
216 }
217
218 /**
219 * Create mock IOFSwitch with flow table which has arbitrary flows.
220 *
221 * @param cookieList List of FlowEntry IDs switch has.
222 * @return Mock object.
223 */
224 private IOFSwitch createMockSwitch(long[] cookieList) {
Sho SHIMIZU107344e2014-08-13 16:11:53 -0700225 IOFSwitch sw = createMock(IOFSwitch.class);
226 expect(sw.getId()).andReturn((long) 1).anyTimes();
Ray Milkey269ffb92014-04-03 14:43:30 -0700227
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700228 List<OFStatsReply> stats = new ArrayList<OFStatsReply>();
Ray Milkey269ffb92014-04-03 14:43:30 -0700229 for (long cookie : cookieList) {
230 stats.add(createReply(cookie));
231 }
232
233 @SuppressWarnings("unchecked")
Sho SHIMIZU107344e2014-08-13 16:11:53 -0700234 Future<List<OFStatsReply>> future = createMock(Future.class);
Ray Milkey269ffb92014-04-03 14:43:30 -0700235 try {
Sho SHIMIZU107344e2014-08-13 16:11:53 -0700236 expect(future.get()).andReturn(stats).once();
Ray Milkey269ffb92014-04-03 14:43:30 -0700237 } catch (InterruptedException e1) {
238 fail("Failed in Future#get()");
239 } catch (ExecutionException e1) {
240 fail("Failed in Future#get()");
241 }
Sho SHIMIZU107344e2014-08-13 16:11:53 -0700242 replay(future);
Ray Milkey269ffb92014-04-03 14:43:30 -0700243
244 try {
Sho SHIMIZU107344e2014-08-13 16:11:53 -0700245 expect(sw.getStatistics(anyObject(OFFlowStatsRequest.class)))
Ray Milkey269ffb92014-04-03 14:43:30 -0700246 .andReturn(future).once();
247 } catch (IOException e) {
248 fail("Failed in IOFSwitch#getStatistics()");
249 }
250
Sho SHIMIZU107344e2014-08-13 16:11:53 -0700251 replay(sw);
Ray Milkey269ffb92014-04-03 14:43:30 -0700252 return sw;
253 }
254
255 /**
256 * Create single OFFlowStatisticsReply object which is actually obtained from switch.
257 *
258 * @param cookie Cookie value, which indicates ID of FlowEntry installed to switch.
259 * @return Created object.
260 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700261 private OFFlowStatsReply createReply(long cookie) {
262 OFFlowStatsEntry entry = factory10.buildFlowStatsEntry()
263 .setCookie(U64.of(cookie))
264 .setPriority(1)
265 .setMatch(factory10.buildMatch().build())
266 .build();
267 OFFlowStatsReply stat = factory10.buildFlowStatsReply()
268 .setEntries(Collections.singletonList(entry)).build();
Ray Milkey269ffb92014-04-03 14:43:30 -0700269
270 return stat;
271 }
272
273 /**
274 * Create mock FlowDatabaseOperation to mock DB.
275 *
276 * @param idList List of FlowEntry IDs stored in DB.
277 */
278 private void initMockGraph(long[] idList) {
279 /*
Yuta HIGUCHI91a8f502014-06-17 10:15:29 -0700280 * TODO: The old FlowDatabaseOperation class is gone, so the method
281 * below needs to be rewritten.
282 */
283 /*
284 List<IFlowEntry> flowEntryList = new ArrayList<IFlowEntry>();
Yuta HIGUCHI44a0b352014-05-14 21:32:48 -0700285
Yuta HIGUCHI91a8f502014-06-17 10:15:29 -0700286 for (long id : idList) {
287 IFlowEntry entry = EasyMock.createMock(IFlowEntry.class);
288 EasyMock.expect(entry.getFlowEntryId()).andReturn(String.valueOf(id)).anyTimes();
289 EasyMock.replay(entry);
290 flowEntryList.add(entry);
291 }
292
293 ISwitchObject swObj = EasyMock.createMock(ISwitchObject.class);
294 EasyMock.expect(swObj.getFlowEntries()).andReturn(flowEntryList).once();
295 EasyMock.replay(swObj);
296
297 DBOperation mockOp = PowerMock.createMock(DBOperation.class);
298 EasyMock.expect(mockOp.searchSwitch(EasyMock.anyObject(String.class))).andReturn(swObj).once();
299
300 PowerMock.mockStatic(FlowDatabaseOperation.class);
301 for (IFlowEntry entry : flowEntryList) {
302 EasyMock.expect(FlowDatabaseOperation.extractFlowEntry(EasyMock.eq(entry)))
303 .andAnswer(new IAnswer<FlowEntry>() {
304 @Override
305 public FlowEntry answer() throws Throwable {
306 IFlowEntry iflow = (IFlowEntry)EasyMock.getCurrentArguments()[0];
307 long flowEntryId = Long.valueOf(iflow.getFlowEntryId());
308
309 FlowEntry flow = EasyMock.createMock(FlowEntry.class);
310 EasyMock.expect(flow.flowEntryId()).andReturn(new FlowEntryId(flowEntryId)).anyTimes();
311 EasyMock.replay(flow);
312 return flow;
Ray Milkey269ffb92014-04-03 14:43:30 -0700313 }
Yuta HIGUCHI44a0b352014-05-14 21:32:48 -0700314
Yuta HIGUCHI91a8f502014-06-17 10:15:29 -0700315 }).anyTimes();
316 EasyMock.expect(mockOp.searchFlowEntry(EasyMock.eq(new FlowEntryId(entry.getFlowEntryId()))))
317 .andReturn(entry);
318 }
319 PowerMock.replay(FlowDatabaseOperation.class);
320 EasyMock.replay(mockOp);
Yuta HIGUCHI44a0b352014-05-14 21:32:48 -0700321
Yuta HIGUCHI91a8f502014-06-17 10:15:29 -0700322 try {
323 PowerMock.expectNew(DBOperation.class).andReturn(mockOp);
324 } catch (Exception e) {
325 fail("Failed to create DBOperation");
326 }
327 PowerMock.replay(DBOperation.class);
328 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700329 }
330
331 /**
332 * Instantiate FlowSynchronizer and sync flows.
333 *
334 * @param sw Target IOFSwitch object
335 */
336 private void doSynchronization(IOFSwitch sw) {
337 sync = new FlowSynchronizer();
338 sync.init(pusher);
339 Future<SyncResult> future = sync.synchronize(sw);
340 try {
341 future.get();
342 } catch (Exception e) {
343 fail("Failed to Future#get()");
344 }
345 }
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700346}