blob: 13d5a87dc26a7a32fc1ea2f4b7ec6dbdc39ae390 [file] [log] [blame]
Jonathan Hart23701d12014-04-03 10:45:48 -07001package net.onrc.onos.core.flowprogrammer;
Naoki Shiota8df97bc2014-03-13 18:42:23 -07002
3import static org.junit.Assert.*;
4
5import java.io.IOException;
6import java.util.ArrayList;
7import java.util.List;
8import java.util.concurrent.ExecutionException;
9import java.util.concurrent.Future;
10
11import net.floodlightcontroller.core.IOFSwitch;
Jonathan Hart23701d12014-04-03 10:45:48 -070012import net.onrc.onos.core.flowprogrammer.FlowPusher;
13import net.onrc.onos.core.flowprogrammer.FlowSynchronizer;
14import net.onrc.onos.core.flowprogrammer.IFlowPusherService.MsgPriority;
15import net.onrc.onos.core.flowprogrammer.IFlowSyncService.SyncResult;
16import net.onrc.onos.core.util.FlowEntry;
17import net.onrc.onos.core.util.FlowEntryId;
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;
26import org.openflow.protocol.OFFlowMod;
27import org.openflow.protocol.OFMatch;
28import org.openflow.protocol.OFMessage;
29import org.openflow.protocol.OFStatisticsRequest;
30import org.openflow.protocol.OFType;
31import org.openflow.protocol.statistics.OFFlowStatisticsReply;
32import org.openflow.protocol.statistics.OFStatistics;
33import org.powermock.api.easymock.PowerMock;
34import org.powermock.core.classloader.annotations.PrepareForTest;
35import org.powermock.modules.junit4.PowerMockRunner;
36
37// Test should be fixed to fit RAMCloud basis
38@Ignore
39@RunWith(PowerMockRunner.class)
Pavlin Radoslavov697f6982014-03-26 15:55:19 -070040@PrepareForTest({FlowSynchronizer.class})
Naoki Shiota8df97bc2014-03-13 18:42:23 -070041public class FlowSynchronizerTest {
42 private FlowPusher pusher;
43 private FlowSynchronizer sync;
44 private List<Long> idAdded;
45 private List<Long> idRemoved;
46
47 @Before
48 public void setUp() throws Exception {
49 idAdded = new ArrayList<Long>();
50 idRemoved = new ArrayList<Long>();
51
52 pusher = EasyMock.createMock(FlowPusher.class);
53 EasyMock.expect(pusher.suspend(EasyMock.anyObject(IOFSwitch.class))).andReturn(true).anyTimes();
54 EasyMock.expect(pusher.resume(EasyMock.anyObject(IOFSwitch.class))).andReturn(true).anyTimes();
55 pusher.add(EasyMock.anyObject(IOFSwitch.class), EasyMock.anyObject(OFMessage.class),
56 EasyMock.eq(MsgPriority.HIGH));
57 EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
58 @Override
59 public Object answer() throws Throwable {
60 OFMessage msg = (OFMessage)EasyMock.getCurrentArguments()[1];
61 if (msg.getType().equals(OFType.FLOW_MOD)) {
62 OFFlowMod fm = (OFFlowMod)msg;
63 if (fm.getCommand() == OFFlowMod.OFPFC_DELETE_STRICT) {
64 idRemoved.add(fm.getCookie());
65 }
66 }
67 return null;
68 }
69 }).anyTimes();
70 pusher.pushFlowEntry(EasyMock.anyObject(IOFSwitch.class), EasyMock.anyObject(FlowEntry.class),
71 EasyMock.eq(MsgPriority.HIGH));
72 EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
73 @Override
74 public Object answer() throws Throwable {
75 FlowEntry flow = (FlowEntry)EasyMock.getCurrentArguments()[1];
76 idAdded.add(flow.flowEntryId().value());
77 return null;
78 }
79 }).anyTimes();
80 EasyMock.replay(pusher);
81 }
82
83 @After
84 public void tearDown() throws Exception {
85 }
86
87 /**
88 * Test that synchronization doesn't affect anything in case either DB and
89 * flow table has the same entries.
90 */
91 @Test
92 public void testStable() {
93 // Create mock of flow table : flow 1
94 IOFSwitch sw = createMockSwitch(new long[] {1});
95
96 // Create mock of flow entries : flow 1
97 initMockGraph(new long[] {1});
98
99 // synchronize
100 doSynchronization(sw);
101
102 // check if flow is not changed
103 assertEquals(0, idAdded.size());
104 assertEquals(0, idRemoved.size());
105 }
106
107 /**
108 * Test that an flow is added in case DB has an extra FlowEntry.
109 */
110 @Test
111 public void testSingleAdd() {
112 // Create mock of flow table : null
113 IOFSwitch sw = createMockSwitch(new long[] {});
114
115 // Create mock of flow entries : flow 1
116 initMockGraph(new long[] {1});
117
118 // synchronize
119 doSynchronization(sw);
120
121 // check if single flow is installed
122 assertEquals(1, idAdded.size());
123 assertTrue(idAdded.contains((long)1));
124 assertEquals(0, idRemoved.size());
125 }
126
127 /**
128 * Test that an flow is deleted in case switch has an extra FlowEntry.
129 */
130 @Test
131 public void testSingleDelete() {
132 // Create mock of flow table : flow 1
133 IOFSwitch sw = createMockSwitch(new long[] {1});
134
135 // Create mock of flow entries : null
136 initMockGraph(new long[] {});
137
138 // synchronize
139 doSynchronization(sw);
140
141 // check if single flow is deleted
142 assertEquals(0, idAdded.size());
143 assertEquals(1, idRemoved.size());
144 assertTrue(idRemoved.contains((long)1));
145 }
146
147 /**
148 * Test that appropriate flows are added and other appropriate flows are deleted
149 * in case flows in DB are overlapping flows in switch.
150 */
151 @Test
152 public void testMixed() {
153 // Create mock of flow table : flow 1,2,3
154 IOFSwitch sw = createMockSwitch(new long[] {1,2,3});
155
156 // Create mock of flow entries : flow 2,3,4,5
157 initMockGraph(new long[] {2,3,4,5});
158
159 // synchronize
160 doSynchronization(sw);
161
162 // check if two flows {4,5} is installed and one flow {1} is deleted
163 assertEquals(2, idAdded.size());
164 assertTrue(idAdded.contains((long)4));
165 assertTrue(idAdded.contains((long)5));
166 assertEquals(1, idRemoved.size());
167 assertTrue(idRemoved.contains((long)1));
168 }
169
170
171 @Test
172 public void testMassive() {
173 // Create mock of flow table : flow 0-1999
174 long [] swIdList = new long [2000];
175 for (long i = 0; i < 2000; ++i) {
176 swIdList[(int)i] = i;
177 }
178 IOFSwitch sw = createMockSwitch(swIdList);
179
180 // Create mock of flow entries : flow 1500-3499
181 long [] dbIdList = new long [2000];
182 for (long i = 0; i < 2000; ++i) {
183 dbIdList[(int)i] = 1500 + i;
184 }
185 initMockGraph(dbIdList);
186
187 // synchronize
188 doSynchronization(sw);
189
190 // check if 1500 flows {2000-3499} is installed and 1500 flows {0,...,1499} is deleted
191 assertEquals(1500, idAdded.size());
192 for (long i = 2000; i < 3500; ++i) {
193 assertTrue(idAdded.contains(i));
194 }
195 assertEquals(1500, idRemoved.size());
196 for (long i = 0; i < 1500; ++i) {
197 assertTrue(idRemoved.contains(i));
198 }
199 }
200
201 /**
202 * Create mock IOFSwitch with flow table which has arbitrary flows.
203 * @param cookieList List of FlowEntry IDs switch has.
204 * @return Mock object.
205 */
206 private IOFSwitch createMockSwitch(long[] cookieList) {
207 IOFSwitch sw = EasyMock.createMock(IOFSwitch.class);
208 EasyMock.expect(sw.getId()).andReturn((long)1).anyTimes();
209
210 List<OFStatistics> stats = new ArrayList<OFStatistics>();
211 for (long cookie : cookieList) {
212 stats.add(createReply(cookie));
213 }
214
215 @SuppressWarnings("unchecked")
216 Future<List<OFStatistics>> future = EasyMock.createMock(Future.class);
217 try {
218 EasyMock.expect(future.get()).andReturn(stats).once();
219 } catch (InterruptedException e1) {
220 fail("Failed in Future#get()");
221 } catch (ExecutionException e1) {
222 fail("Failed in Future#get()");
223 }
224 EasyMock.replay(future);
225
226 try {
227 EasyMock.expect(sw.getStatistics(EasyMock.anyObject(OFStatisticsRequest.class)))
228 .andReturn(future).once();
229 } catch (IOException e) {
230 fail("Failed in IOFSwitch#getStatistics()");
231 }
232
233 EasyMock.replay(sw);
234 return sw;
235 }
236
237 /**
238 * Create single OFFlowStatisticsReply object which is actually obtained from switch.
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 /**
Pavlin Radoslavov697f6982014-03-26 15:55:19 -0700254 * Create mock FlowDatabaseOperation to mock DB.
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700255 * @param idList List of FlowEntry IDs stored in DB.
256 */
257 private void initMockGraph(long[] idList) {
Pavlin Radoslavov39f0f2e2014-03-20 12:04:57 -0700258 /*
259 * TODO: The old FlowDatabaseOperation class is gone, so the method
260 * below needs to be rewritten.
261 */
262 /*
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700263 List<IFlowEntry> flowEntryList = new ArrayList<IFlowEntry>();
264
265 for (long id : idList) {
266 IFlowEntry entry = EasyMock.createMock(IFlowEntry.class);
267 EasyMock.expect(entry.getFlowEntryId()).andReturn(String.valueOf(id)).anyTimes();
268 EasyMock.replay(entry);
269 flowEntryList.add(entry);
270 }
271
272 ISwitchObject swObj = EasyMock.createMock(ISwitchObject.class);
273 EasyMock.expect(swObj.getFlowEntries()).andReturn(flowEntryList).once();
274 EasyMock.replay(swObj);
275
276 DBOperation mockOp = PowerMock.createMock(DBOperation.class);
277 EasyMock.expect(mockOp.searchSwitch(EasyMock.anyObject(String.class))).andReturn(swObj).once();
278
279 PowerMock.mockStatic(FlowDatabaseOperation.class);
280 for (IFlowEntry entry : flowEntryList) {
281 EasyMock.expect(FlowDatabaseOperation.extractFlowEntry(EasyMock.eq(entry)))
282 .andAnswer(new IAnswer<FlowEntry>() {
283 @Override
284 public FlowEntry answer() throws Throwable {
285 IFlowEntry iflow = (IFlowEntry)EasyMock.getCurrentArguments()[0];
286 long flowEntryId = Long.valueOf(iflow.getFlowEntryId());
287
288 FlowEntry flow = EasyMock.createMock(FlowEntry.class);
289 EasyMock.expect(flow.flowEntryId()).andReturn(new FlowEntryId(flowEntryId)).anyTimes();
290 EasyMock.replay(flow);
291 return flow;
292 }
293
294 }).anyTimes();
295 EasyMock.expect(mockOp.searchFlowEntry(EasyMock.eq(new FlowEntryId(entry.getFlowEntryId()))))
296 .andReturn(entry);
297 }
298 PowerMock.replay(FlowDatabaseOperation.class);
299 EasyMock.replay(mockOp);
300
301 try {
302 PowerMock.expectNew(DBOperation.class).andReturn(mockOp);
303 } catch (Exception e) {
304 fail("Failed to create DBOperation");
305 }
306 PowerMock.replay(DBOperation.class);
Pavlin Radoslavov39f0f2e2014-03-20 12:04:57 -0700307 */
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700308 }
309
310 /**
311 * Instantiate FlowSynchronizer and sync flows.
312 * @param sw Target IOFSwitch object
313 */
314 private void doSynchronization(IOFSwitch sw) {
315 sync = new FlowSynchronizer();
316 sync.init(pusher);
317 Future<SyncResult> future = sync.synchronize(sw);
318 try {
319 future.get();
320 } catch (Exception e) {
321 fail("Failed to Future#get()");
322 }
323 }
324}