blob: 112620ee236f2468a4ec068ef4952fd83b0d6b3a [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;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07009import java.util.Collections;
Naoki Shiota8df97bc2014-03-13 18:42:23 -070010import java.util.List;
11import java.util.concurrent.ExecutionException;
12import java.util.concurrent.Future;
13
14import net.floodlightcontroller.core.IOFSwitch;
Jonathan Hart23701d12014-04-03 10:45:48 -070015import net.onrc.onos.core.flowprogrammer.IFlowPusherService.MsgPriority;
16import net.onrc.onos.core.flowprogrammer.IFlowSyncService.SyncResult;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070017import net.onrc.onos.core.intent.FlowEntry;
Naoki Shiota8df97bc2014-03-13 18:42:23 -070018
19import org.easymock.EasyMock;
20import org.easymock.IAnswer;
21import org.junit.After;
22import org.junit.Before;
23import org.junit.Ignore;
24import org.junit.Test;
25import org.junit.runner.RunWith;
Naoki Shiota8df97bc2014-03-13 18:42:23 -070026import org.powermock.core.classloader.annotations.PrepareForTest;
27import org.powermock.modules.junit4.PowerMockRunner;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070028import org.projectfloodlight.openflow.protocol.OFFactories;
29import org.projectfloodlight.openflow.protocol.OFFactory;
30import org.projectfloodlight.openflow.protocol.OFFlowMod;
31import org.projectfloodlight.openflow.protocol.OFFlowModCommand;
32import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
33import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
34import org.projectfloodlight.openflow.protocol.OFFlowStatsRequest;
35import org.projectfloodlight.openflow.protocol.OFMessage;
36import org.projectfloodlight.openflow.protocol.OFStatsReply;
37import org.projectfloodlight.openflow.protocol.OFType;
38import org.projectfloodlight.openflow.protocol.OFVersion;
39import org.projectfloodlight.openflow.types.U64;
Naoki Shiota8df97bc2014-03-13 18:42:23 -070040
41// Test should be fixed to fit RAMCloud basis
42@Ignore
43@RunWith(PowerMockRunner.class)
Yuta HIGUCHI44a0b352014-05-14 21:32:48 -070044@PrepareForTest({FlowSynchronizer.class })
Naoki Shiota8df97bc2014-03-13 18:42:23 -070045public class FlowSynchronizerTest {
Ray Milkey269ffb92014-04-03 14:43:30 -070046 private FlowPusher pusher;
47 private FlowSynchronizer sync;
48 private List<Long> idAdded;
49 private List<Long> idRemoved;
Naoki Shiota8df97bc2014-03-13 18:42:23 -070050
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070051 /*
52 * OF1.0 Factory for now. Change when we move to
53 * OF 1.3.
54 */
55 private static OFFactory factory10;
56
Ray Milkey269ffb92014-04-03 14:43:30 -070057 @Before
58 public void setUp() throws Exception {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070059 factory10 = OFFactories.getFactory(OFVersion.OF_10);
Ray Milkey269ffb92014-04-03 14:43:30 -070060 idAdded = new ArrayList<Long>();
61 idRemoved = new ArrayList<Long>();
Naoki Shiota8df97bc2014-03-13 18:42:23 -070062
Ray Milkey269ffb92014-04-03 14:43:30 -070063 pusher = EasyMock.createMock(FlowPusher.class);
64 EasyMock.expect(pusher.suspend(EasyMock.anyObject(IOFSwitch.class))).andReturn(true).anyTimes();
65 EasyMock.expect(pusher.resume(EasyMock.anyObject(IOFSwitch.class))).andReturn(true).anyTimes();
66 pusher.add(EasyMock.anyObject(IOFSwitch.class), EasyMock.anyObject(OFMessage.class),
67 EasyMock.eq(MsgPriority.HIGH));
68 EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
69 @Override
70 public Object answer() throws Throwable {
71 OFMessage msg = (OFMessage) EasyMock.getCurrentArguments()[1];
72 if (msg.getType().equals(OFType.FLOW_MOD)) {
73 OFFlowMod fm = (OFFlowMod) msg;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070074 if (fm.getCommand() == OFFlowModCommand.DELETE_STRICT) {
75 idRemoved.add(fm.getCookie().getValue());
Ray Milkey269ffb92014-04-03 14:43:30 -070076 }
77 }
78 return null;
79 }
80 }).anyTimes();
81 pusher.pushFlowEntry(EasyMock.anyObject(IOFSwitch.class), EasyMock.anyObject(FlowEntry.class),
82 EasyMock.eq(MsgPriority.HIGH));
83 EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
84 @Override
85 public Object answer() throws Throwable {
86 FlowEntry flow = (FlowEntry) EasyMock.getCurrentArguments()[1];
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070087 idAdded.add(flow.getFlowEntryId());
Ray Milkey269ffb92014-04-03 14:43:30 -070088 return null;
89 }
90 }).anyTimes();
91 EasyMock.replay(pusher);
92 }
Naoki Shiota8df97bc2014-03-13 18:42:23 -070093
Ray Milkey269ffb92014-04-03 14:43:30 -070094 @After
95 public void tearDown() throws Exception {
96 }
Naoki Shiota8df97bc2014-03-13 18:42:23 -070097
Ray Milkey269ffb92014-04-03 14:43:30 -070098 /**
99 * Test that synchronization doesn't affect anything in case either DB and
100 * flow table has the same entries.
101 */
102 @Test
103 public void testStable() {
104 // Create mock of flow table : flow 1
105 IOFSwitch sw = createMockSwitch(new long[]{1});
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700106
Ray Milkey269ffb92014-04-03 14:43:30 -0700107 // Create mock of flow entries : flow 1
108 initMockGraph(new long[]{1});
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700109
Ray Milkey269ffb92014-04-03 14:43:30 -0700110 // synchronize
111 doSynchronization(sw);
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700112
Ray Milkey269ffb92014-04-03 14:43:30 -0700113 // check if flow is not changed
114 assertEquals(0, idAdded.size());
115 assertEquals(0, idRemoved.size());
116 }
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700117
Ray Milkey269ffb92014-04-03 14:43:30 -0700118 /**
119 * Test that an flow is added in case DB has an extra FlowEntry.
120 */
121 @Test
122 public void testSingleAdd() {
123 // Create mock of flow table : null
124 IOFSwitch sw = createMockSwitch(new long[]{});
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700125
Ray Milkey269ffb92014-04-03 14:43:30 -0700126 // Create mock of flow entries : flow 1
127 initMockGraph(new long[]{1});
128
129 // synchronize
130 doSynchronization(sw);
131
132 // check if single flow is installed
133 assertEquals(1, idAdded.size());
134 assertTrue(idAdded.contains((long) 1));
135 assertEquals(0, idRemoved.size());
136 }
137
138 /**
139 * Test that an flow is deleted in case switch has an extra FlowEntry.
140 */
141 @Test
142 public void testSingleDelete() {
143 // Create mock of flow table : flow 1
144 IOFSwitch sw = createMockSwitch(new long[]{1});
145
146 // Create mock of flow entries : null
147 initMockGraph(new long[]{});
148
149 // synchronize
150 doSynchronization(sw);
151
152 // check if single flow is deleted
153 assertEquals(0, idAdded.size());
154 assertEquals(1, idRemoved.size());
155 assertTrue(idRemoved.contains((long) 1));
156 }
157
158 /**
159 * Test that appropriate flows are added and other appropriate flows are deleted
160 * in case flows in DB are overlapping flows in switch.
161 */
162 @Test
163 public void testMixed() {
164 // Create mock of flow table : flow 1,2,3
165 IOFSwitch sw = createMockSwitch(new long[]{1, 2, 3});
166
167 // Create mock of flow entries : flow 2,3,4,5
168 initMockGraph(new long[]{2, 3, 4, 5});
169
170 // synchronize
171 doSynchronization(sw);
172
173 // check if two flows {4,5} is installed and one flow {1} is deleted
174 assertEquals(2, idAdded.size());
175 assertTrue(idAdded.contains((long) 4));
176 assertTrue(idAdded.contains((long) 5));
177 assertEquals(1, idRemoved.size());
178 assertTrue(idRemoved.contains((long) 1));
179 }
180
181
182 @Test
183 public void testMassive() {
184 // Create mock of flow table : flow 0-1999
185 long[] swIdList = new long[2000];
186 for (long i = 0; i < 2000; ++i) {
187 swIdList[(int) i] = i;
188 }
189 IOFSwitch sw = createMockSwitch(swIdList);
190
191 // Create mock of flow entries : flow 1500-3499
192 long[] dbIdList = new long[2000];
193 for (long i = 0; i < 2000; ++i) {
194 dbIdList[(int) i] = 1500 + i;
195 }
196 initMockGraph(dbIdList);
197
198 // synchronize
199 doSynchronization(sw);
200
201 // check if 1500 flows {2000-3499} is installed and 1500 flows {0,...,1499} is deleted
202 assertEquals(1500, idAdded.size());
203 for (long i = 2000; i < 3500; ++i) {
204 assertTrue(idAdded.contains(i));
205 }
206 assertEquals(1500, idRemoved.size());
207 for (long i = 0; i < 1500; ++i) {
208 assertTrue(idRemoved.contains(i));
209 }
210 }
211
212 /**
213 * Create mock IOFSwitch with flow table which has arbitrary flows.
214 *
215 * @param cookieList List of FlowEntry IDs switch has.
216 * @return Mock object.
217 */
218 private IOFSwitch createMockSwitch(long[] cookieList) {
219 IOFSwitch sw = EasyMock.createMock(IOFSwitch.class);
220 EasyMock.expect(sw.getId()).andReturn((long) 1).anyTimes();
221
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700222 List<OFStatsReply> stats = new ArrayList<OFStatsReply>();
Ray Milkey269ffb92014-04-03 14:43:30 -0700223 for (long cookie : cookieList) {
224 stats.add(createReply(cookie));
225 }
226
227 @SuppressWarnings("unchecked")
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700228 Future<List<OFStatsReply>> future = EasyMock.createMock(Future.class);
Ray Milkey269ffb92014-04-03 14:43:30 -0700229 try {
230 EasyMock.expect(future.get()).andReturn(stats).once();
231 } catch (InterruptedException e1) {
232 fail("Failed in Future#get()");
233 } catch (ExecutionException e1) {
234 fail("Failed in Future#get()");
235 }
236 EasyMock.replay(future);
237
238 try {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700239 EasyMock.expect(sw.getStatistics(EasyMock.anyObject(OFFlowStatsRequest.class)))
Ray Milkey269ffb92014-04-03 14:43:30 -0700240 .andReturn(future).once();
241 } catch (IOException e) {
242 fail("Failed in IOFSwitch#getStatistics()");
243 }
244
245 EasyMock.replay(sw);
246 return sw;
247 }
248
249 /**
250 * Create single OFFlowStatisticsReply object which is actually obtained from switch.
251 *
252 * @param cookie Cookie value, which indicates ID of FlowEntry installed to switch.
253 * @return Created object.
254 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700255 private OFFlowStatsReply createReply(long cookie) {
256 OFFlowStatsEntry entry = factory10.buildFlowStatsEntry()
257 .setCookie(U64.of(cookie))
258 .setPriority(1)
259 .setMatch(factory10.buildMatch().build())
260 .build();
261 OFFlowStatsReply stat = factory10.buildFlowStatsReply()
262 .setEntries(Collections.singletonList(entry)).build();
Ray Milkey269ffb92014-04-03 14:43:30 -0700263
264 return stat;
265 }
266
267 /**
268 * Create mock FlowDatabaseOperation to mock DB.
269 *
270 * @param idList List of FlowEntry IDs stored in DB.
271 */
272 private void initMockGraph(long[] idList) {
273 /*
Yuta HIGUCHI91a8f502014-06-17 10:15:29 -0700274 * TODO: The old FlowDatabaseOperation class is gone, so the method
275 * below needs to be rewritten.
276 */
277 /*
278 List<IFlowEntry> flowEntryList = new ArrayList<IFlowEntry>();
Yuta HIGUCHI44a0b352014-05-14 21:32:48 -0700279
Yuta HIGUCHI91a8f502014-06-17 10:15:29 -0700280 for (long id : idList) {
281 IFlowEntry entry = EasyMock.createMock(IFlowEntry.class);
282 EasyMock.expect(entry.getFlowEntryId()).andReturn(String.valueOf(id)).anyTimes();
283 EasyMock.replay(entry);
284 flowEntryList.add(entry);
285 }
286
287 ISwitchObject swObj = EasyMock.createMock(ISwitchObject.class);
288 EasyMock.expect(swObj.getFlowEntries()).andReturn(flowEntryList).once();
289 EasyMock.replay(swObj);
290
291 DBOperation mockOp = PowerMock.createMock(DBOperation.class);
292 EasyMock.expect(mockOp.searchSwitch(EasyMock.anyObject(String.class))).andReturn(swObj).once();
293
294 PowerMock.mockStatic(FlowDatabaseOperation.class);
295 for (IFlowEntry entry : flowEntryList) {
296 EasyMock.expect(FlowDatabaseOperation.extractFlowEntry(EasyMock.eq(entry)))
297 .andAnswer(new IAnswer<FlowEntry>() {
298 @Override
299 public FlowEntry answer() throws Throwable {
300 IFlowEntry iflow = (IFlowEntry)EasyMock.getCurrentArguments()[0];
301 long flowEntryId = Long.valueOf(iflow.getFlowEntryId());
302
303 FlowEntry flow = EasyMock.createMock(FlowEntry.class);
304 EasyMock.expect(flow.flowEntryId()).andReturn(new FlowEntryId(flowEntryId)).anyTimes();
305 EasyMock.replay(flow);
306 return flow;
Ray Milkey269ffb92014-04-03 14:43:30 -0700307 }
Yuta HIGUCHI44a0b352014-05-14 21:32:48 -0700308
Yuta HIGUCHI91a8f502014-06-17 10:15:29 -0700309 }).anyTimes();
310 EasyMock.expect(mockOp.searchFlowEntry(EasyMock.eq(new FlowEntryId(entry.getFlowEntryId()))))
311 .andReturn(entry);
312 }
313 PowerMock.replay(FlowDatabaseOperation.class);
314 EasyMock.replay(mockOp);
Yuta HIGUCHI44a0b352014-05-14 21:32:48 -0700315
Yuta HIGUCHI91a8f502014-06-17 10:15:29 -0700316 try {
317 PowerMock.expectNew(DBOperation.class).andReturn(mockOp);
318 } catch (Exception e) {
319 fail("Failed to create DBOperation");
320 }
321 PowerMock.replay(DBOperation.class);
322 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700323 }
324
325 /**
326 * Instantiate FlowSynchronizer and sync flows.
327 *
328 * @param sw Target IOFSwitch object
329 */
330 private void doSynchronization(IOFSwitch sw) {
331 sync = new FlowSynchronizer();
332 sync.init(pusher);
333 Future<SyncResult> future = sync.synchronize(sw);
334 try {
335 future.get();
336 } catch (Exception e) {
337 fail("Failed to Future#get()");
338 }
339 }
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700340}