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