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