blob: 9222a8ae43d07ee8dc9c49af6b048f27a972afce [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)
Pavlin Radoslavov697f6982014-03-26 15:55:19 -070038@PrepareForTest({FlowSynchronizer.class})
Naoki Shiota8df97bc2014-03-13 18:42:23 -070039public class FlowSynchronizerTest {
40 private FlowPusher pusher;
41 private FlowSynchronizer sync;
42 private List<Long> idAdded;
43 private List<Long> idRemoved;
44
45 @Before
46 public void setUp() throws Exception {
47 idAdded = new ArrayList<Long>();
48 idRemoved = new ArrayList<Long>();
49
50 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 }
80
81 @After
82 public void tearDown() throws Exception {
83 }
84
85 /**
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});
93
94 // Create mock of flow entries : flow 1
95 initMockGraph(new long[] {1});
96
97 // synchronize
98 doSynchronization(sw);
99
100 // check if flow is not changed
101 assertEquals(0, idAdded.size());
102 assertEquals(0, idRemoved.size());
103 }
104
105 /**
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[] {});
112
113 // 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 * @param cookieList List of FlowEntry IDs switch has.
202 * @return Mock object.
203 */
204 private IOFSwitch createMockSwitch(long[] cookieList) {
205 IOFSwitch sw = EasyMock.createMock(IOFSwitch.class);
206 EasyMock.expect(sw.getId()).andReturn((long)1).anyTimes();
207
208 List<OFStatistics> stats = new ArrayList<OFStatistics>();
209 for (long cookie : cookieList) {
210 stats.add(createReply(cookie));
211 }
212
213 @SuppressWarnings("unchecked")
214 Future<List<OFStatistics>> future = EasyMock.createMock(Future.class);
215 try {
216 EasyMock.expect(future.get()).andReturn(stats).once();
217 } catch (InterruptedException e1) {
218 fail("Failed in Future#get()");
219 } catch (ExecutionException e1) {
220 fail("Failed in Future#get()");
221 }
222 EasyMock.replay(future);
223
224 try {
225 EasyMock.expect(sw.getStatistics(EasyMock.anyObject(OFStatisticsRequest.class)))
226 .andReturn(future).once();
227 } catch (IOException e) {
228 fail("Failed in IOFSwitch#getStatistics()");
229 }
230
231 EasyMock.replay(sw);
232 return sw;
233 }
234
235 /**
236 * Create single OFFlowStatisticsReply object which is actually obtained from switch.
237 * @param cookie Cookie value, which indicates ID of FlowEntry installed to switch.
238 * @return Created object.
239 */
240 private OFFlowStatisticsReply createReply(long cookie) {
241 OFFlowStatisticsReply stat = new OFFlowStatisticsReply();
242 OFMatch match = new OFMatch();
243
244 stat.setCookie(cookie);
245 stat.setMatch(match);
246 stat.setPriority((short)1);
247
248 return stat;
249 }
250
251 /**
Pavlin Radoslavov697f6982014-03-26 15:55:19 -0700252 * Create mock FlowDatabaseOperation to mock DB.
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700253 * @param idList List of FlowEntry IDs stored in DB.
254 */
255 private void initMockGraph(long[] idList) {
Pavlin Radoslavov39f0f2e2014-03-20 12:04:57 -0700256 /*
257 * TODO: The old FlowDatabaseOperation class is gone, so the method
258 * below needs to be rewritten.
259 */
260 /*
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700261 List<IFlowEntry> flowEntryList = new ArrayList<IFlowEntry>();
262
263 for (long id : idList) {
264 IFlowEntry entry = EasyMock.createMock(IFlowEntry.class);
265 EasyMock.expect(entry.getFlowEntryId()).andReturn(String.valueOf(id)).anyTimes();
266 EasyMock.replay(entry);
267 flowEntryList.add(entry);
268 }
269
270 ISwitchObject swObj = EasyMock.createMock(ISwitchObject.class);
271 EasyMock.expect(swObj.getFlowEntries()).andReturn(flowEntryList).once();
272 EasyMock.replay(swObj);
273
274 DBOperation mockOp = PowerMock.createMock(DBOperation.class);
275 EasyMock.expect(mockOp.searchSwitch(EasyMock.anyObject(String.class))).andReturn(swObj).once();
276
277 PowerMock.mockStatic(FlowDatabaseOperation.class);
278 for (IFlowEntry entry : flowEntryList) {
279 EasyMock.expect(FlowDatabaseOperation.extractFlowEntry(EasyMock.eq(entry)))
280 .andAnswer(new IAnswer<FlowEntry>() {
281 @Override
282 public FlowEntry answer() throws Throwable {
283 IFlowEntry iflow = (IFlowEntry)EasyMock.getCurrentArguments()[0];
284 long flowEntryId = Long.valueOf(iflow.getFlowEntryId());
285
286 FlowEntry flow = EasyMock.createMock(FlowEntry.class);
287 EasyMock.expect(flow.flowEntryId()).andReturn(new FlowEntryId(flowEntryId)).anyTimes();
288 EasyMock.replay(flow);
289 return flow;
290 }
291
292 }).anyTimes();
293 EasyMock.expect(mockOp.searchFlowEntry(EasyMock.eq(new FlowEntryId(entry.getFlowEntryId()))))
294 .andReturn(entry);
295 }
296 PowerMock.replay(FlowDatabaseOperation.class);
297 EasyMock.replay(mockOp);
298
299 try {
300 PowerMock.expectNew(DBOperation.class).andReturn(mockOp);
301 } catch (Exception e) {
302 fail("Failed to create DBOperation");
303 }
304 PowerMock.replay(DBOperation.class);
Pavlin Radoslavov39f0f2e2014-03-20 12:04:57 -0700305 */
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700306 }
307
308 /**
309 * Instantiate FlowSynchronizer and sync flows.
310 * @param sw Target IOFSwitch object
311 */
312 private void doSynchronization(IOFSwitch sw) {
313 sync = new FlowSynchronizer();
314 sync.init(pusher);
315 Future<SyncResult> future = sync.synchronize(sw);
316 try {
317 future.get();
318 } catch (Exception e) {
319 fail("Failed to Future#get()");
320 }
321 }
322}