blob: 7be7d11dd4013471c9020c17860ef2514808a93e [file] [log] [blame]
Pingpingf5d90932014-10-27 10:50:04 -07001package org.onlab.onos.sdnip;
2
3import static org.easymock.EasyMock.anyObject;
4import static org.easymock.EasyMock.createMock;
5import static org.easymock.EasyMock.expect;
6import static org.easymock.EasyMock.expectLastCall;
7import static org.easymock.EasyMock.replay;
8import static org.easymock.EasyMock.reset;
9import static org.easymock.EasyMock.verify;
10import static org.junit.Assert.assertEquals;
Jonathan Hartec2df012014-10-23 16:40:24 -070011import static org.junit.Assert.assertFalse;
Pingpingf5d90932014-10-27 10:50:04 -070012import static org.junit.Assert.assertTrue;
13
14import java.util.HashSet;
15import java.util.Set;
16import java.util.concurrent.ConcurrentHashMap;
17
18import org.junit.Before;
19import org.junit.Test;
20import org.onlab.junit.TestUtils;
21import org.onlab.junit.TestUtils.TestUtilsException;
22import org.onlab.onos.core.ApplicationId;
23import org.onlab.onos.net.ConnectPoint;
24import org.onlab.onos.net.DefaultHost;
25import org.onlab.onos.net.DeviceId;
26import org.onlab.onos.net.Host;
27import org.onlab.onos.net.HostId;
28import org.onlab.onos.net.HostLocation;
29import org.onlab.onos.net.PortNumber;
30import org.onlab.onos.net.flow.DefaultTrafficSelector;
31import org.onlab.onos.net.flow.DefaultTrafficTreatment;
32import org.onlab.onos.net.flow.TrafficSelector;
33import org.onlab.onos.net.flow.TrafficTreatment;
34import org.onlab.onos.net.host.HostListener;
35import org.onlab.onos.net.host.HostService;
36import org.onlab.onos.net.host.InterfaceIpAddress;
37import org.onlab.onos.net.intent.Intent;
38import org.onlab.onos.net.intent.IntentService;
Jonathan Hartec2df012014-10-23 16:40:24 -070039import org.onlab.onos.net.intent.IntentState;
Pingpingf5d90932014-10-27 10:50:04 -070040import org.onlab.onos.net.intent.MultiPointToSinglePointIntent;
41import org.onlab.onos.net.provider.ProviderId;
42import org.onlab.onos.sdnip.config.Interface;
43import org.onlab.packet.Ethernet;
44import org.onlab.packet.IpAddress;
Pavlin Radoslavov6b570732014-11-06 13:16:45 -080045import org.onlab.packet.Ip4Address;
Pingpingf5d90932014-10-27 10:50:04 -070046import org.onlab.packet.IpPrefix;
Pavlin Radoslavov6b570732014-11-06 13:16:45 -080047import org.onlab.packet.Ip4Prefix;
Pingpingf5d90932014-10-27 10:50:04 -070048import org.onlab.packet.MacAddress;
49import org.onlab.packet.VlanId;
50
51import com.google.common.collect.Sets;
52import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
53import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
54import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
55
56/**
57 * This class tests the intent synchronization function in Router class.
58 */
59public class IntentSyncTest {
60
61 private InterfaceService interfaceService;
62 private IntentService intentService;
63 private HostService hostService;
64
65 private static final ConnectPoint SW1_ETH1 = new ConnectPoint(
66 DeviceId.deviceId("of:0000000000000001"),
67 PortNumber.portNumber(1));
68
69 private static final ConnectPoint SW2_ETH1 = new ConnectPoint(
70 DeviceId.deviceId("of:0000000000000002"),
71 PortNumber.portNumber(1));
72
73 private static final ConnectPoint SW3_ETH1 = new ConnectPoint(
74 DeviceId.deviceId("of:0000000000000003"),
75 PortNumber.portNumber(1));
76
77 private Router router;
78
79 private static final ApplicationId APPID = new ApplicationId() {
80 @Override
81 public short id() {
82 return 1;
83 }
84
85 @Override
86 public String name() {
87 return "SDNIP";
88 }
89 };
90
91 @Before
92 public void setUp() throws Exception {
93 setUpInterfaceService();
94 setUpHostService();
95 intentService = createMock(IntentService.class);
96
97 router = new Router(APPID, intentService,
98 hostService, null, interfaceService);
99 }
100
101 /**
102 * Sets up InterfaceService.
103 */
104 private void setUpInterfaceService() {
105
106 interfaceService = createMock(InterfaceService.class);
107
108 Set<Interface> interfaces = Sets.newHashSet();
109
110 Set<InterfaceIpAddress> interfaceIpAddresses1 = Sets.newHashSet();
111 interfaceIpAddresses1.add(new InterfaceIpAddress(
112 IpAddress.valueOf("192.168.10.101"),
113 IpPrefix.valueOf("192.168.10.0/24")));
114 Interface sw1Eth1 = new Interface(SW1_ETH1,
115 interfaceIpAddresses1, MacAddress.valueOf("00:00:00:00:00:01"));
116 interfaces.add(sw1Eth1);
117
118 Set<InterfaceIpAddress> interfaceIpAddresses2 = Sets.newHashSet();
119 interfaceIpAddresses2.add(new InterfaceIpAddress(
120 IpAddress.valueOf("192.168.20.101"),
121 IpPrefix.valueOf("192.168.20.0/24")));
122 Interface sw2Eth1 = new Interface(SW2_ETH1,
123 interfaceIpAddresses2, MacAddress.valueOf("00:00:00:00:00:02"));
124 interfaces.add(sw2Eth1);
125
126 Set<InterfaceIpAddress> interfaceIpAddresses3 = Sets.newHashSet();
127 interfaceIpAddresses3.add(new InterfaceIpAddress(
128 IpAddress.valueOf("192.168.30.101"),
129 IpPrefix.valueOf("192.168.30.0/24")));
130 Interface sw3Eth1 = new Interface(SW3_ETH1,
131 interfaceIpAddresses3, MacAddress.valueOf("00:00:00:00:00:03"));
132 interfaces.add(sw3Eth1);
133
134 expect(interfaceService.getInterface(SW1_ETH1)).andReturn(
135 sw1Eth1).anyTimes();
136 expect(interfaceService.getInterface(SW2_ETH1)).andReturn(
137 sw2Eth1).anyTimes();
138 expect(interfaceService.getInterface(SW3_ETH1)).andReturn(
139 sw3Eth1).anyTimes();
140 expect(interfaceService.getInterfaces()).andReturn(
141 interfaces).anyTimes();
142 replay(interfaceService);
143 }
144
145 /**
146 * Sets up the host service with details of hosts.
147 */
148 private void setUpHostService() {
149 hostService = createMock(HostService.class);
150
151 hostService.addListener(anyObject(HostListener.class));
152 expectLastCall().anyTimes();
153
154 IpAddress host1Address = IpAddress.valueOf("192.168.10.1");
155 Host host1 = new DefaultHost(ProviderId.NONE, HostId.NONE,
156 MacAddress.valueOf("00:00:00:00:00:01"), VlanId.NONE,
157 new HostLocation(SW1_ETH1, 1),
158 Sets.newHashSet(host1Address));
159
160 expect(hostService.getHostsByIp(host1Address))
161 .andReturn(Sets.newHashSet(host1)).anyTimes();
162 hostService.startMonitoringIp(host1Address);
163 expectLastCall().anyTimes();
164
165
166 IpAddress host2Address = IpAddress.valueOf("192.168.20.1");
167 Host host2 = new DefaultHost(ProviderId.NONE, HostId.NONE,
168 MacAddress.valueOf("00:00:00:00:00:02"), VlanId.NONE,
169 new HostLocation(SW2_ETH1, 1),
170 Sets.newHashSet(host2Address));
171
172 expect(hostService.getHostsByIp(host2Address))
173 .andReturn(Sets.newHashSet(host2)).anyTimes();
174 hostService.startMonitoringIp(host2Address);
175 expectLastCall().anyTimes();
176
177
178 IpAddress host3Address = IpAddress.valueOf("192.168.30.1");
179 Host host3 = new DefaultHost(ProviderId.NONE, HostId.NONE,
180 MacAddress.valueOf("00:00:00:00:00:03"), VlanId.NONE,
181 new HostLocation(SW3_ETH1, 1),
182 Sets.newHashSet(host3Address));
183
184 expect(hostService.getHostsByIp(host3Address))
185 .andReturn(Sets.newHashSet(host3)).anyTimes();
186 hostService.startMonitoringIp(host3Address);
187 expectLastCall().anyTimes();
188
189
190 replay(hostService);
191 }
192
193 /**
194 * This method tests the behavior of intent Synchronizer.
195 *
196 * @throws TestUtilsException
197 */
198 @Test
199 public void testIntentSync() throws TestUtilsException {
200
201 //
202 // Construct routes and intents.
203 // This test simulates the following cases during the master change
204 // time interval:
205 // 1. RouteEntry1 did not change and the intent also did not change.
206 // 2. RouteEntry2 was deleted, but the intent was not deleted.
207 // 3. RouteEntry3 was newly added, and the intent was also submitted.
208 // 4. RouteEntry4 was updated to RouteEntry4Update, and the intent was
209 // also updated to a new one.
210 // 5. RouteEntry5 did not change, but its intent id changed.
211 // 6. RouteEntry6 was newly added, but the intent was not submitted.
212 //
213 RouteEntry routeEntry1 = new RouteEntry(
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800214 Ip4Prefix.valueOf("1.1.1.0/24"),
215 Ip4Address.valueOf("192.168.10.1"));
Pingpingf5d90932014-10-27 10:50:04 -0700216
217 RouteEntry routeEntry2 = new RouteEntry(
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800218 Ip4Prefix.valueOf("2.2.2.0/24"),
219 Ip4Address.valueOf("192.168.20.1"));
Pingpingf5d90932014-10-27 10:50:04 -0700220
221 RouteEntry routeEntry3 = new RouteEntry(
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800222 Ip4Prefix.valueOf("3.3.3.0/24"),
223 Ip4Address.valueOf("192.168.30.1"));
Pingpingf5d90932014-10-27 10:50:04 -0700224
225 RouteEntry routeEntry4 = new RouteEntry(
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800226 Ip4Prefix.valueOf("4.4.4.0/24"),
227 Ip4Address.valueOf("192.168.30.1"));
Pingpingf5d90932014-10-27 10:50:04 -0700228
229 RouteEntry routeEntry4Update = new RouteEntry(
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800230 Ip4Prefix.valueOf("4.4.4.0/24"),
231 Ip4Address.valueOf("192.168.20.1"));
Pingpingf5d90932014-10-27 10:50:04 -0700232
233 RouteEntry routeEntry5 = new RouteEntry(
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800234 Ip4Prefix.valueOf("5.5.5.0/24"),
235 Ip4Address.valueOf("192.168.10.1"));
Pingpingf5d90932014-10-27 10:50:04 -0700236
237 RouteEntry routeEntry6 = new RouteEntry(
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800238 Ip4Prefix.valueOf("6.6.6.0/24"),
239 Ip4Address.valueOf("192.168.10.1"));
Pingpingf5d90932014-10-27 10:50:04 -0700240
Jonathan Hartec2df012014-10-23 16:40:24 -0700241 RouteEntry routeEntry7 = new RouteEntry(
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800242 Ip4Prefix.valueOf("7.7.7.0/24"),
243 Ip4Address.valueOf("192.168.10.1"));
Jonathan Hartec2df012014-10-23 16:40:24 -0700244
Pingpingf5d90932014-10-27 10:50:04 -0700245 MultiPointToSinglePointIntent intent1 = intentBuilder(
246 routeEntry1.prefix(), "00:00:00:00:00:01", SW1_ETH1);
247 MultiPointToSinglePointIntent intent2 = intentBuilder(
248 routeEntry2.prefix(), "00:00:00:00:00:02", SW2_ETH1);
249 MultiPointToSinglePointIntent intent3 = intentBuilder(
250 routeEntry3.prefix(), "00:00:00:00:00:03", SW3_ETH1);
251 MultiPointToSinglePointIntent intent4 = intentBuilder(
252 routeEntry4.prefix(), "00:00:00:00:00:03", SW3_ETH1);
253 MultiPointToSinglePointIntent intent4Update = intentBuilder(
254 routeEntry4Update.prefix(), "00:00:00:00:00:02", SW2_ETH1);
255 MultiPointToSinglePointIntent intent5 = intentBuilder(
256 routeEntry5.prefix(), "00:00:00:00:00:01", SW1_ETH1);
Jonathan Hartec2df012014-10-23 16:40:24 -0700257 MultiPointToSinglePointIntent intent7 = intentBuilder(
258 routeEntry7.prefix(), "00:00:00:00:00:01", SW1_ETH1);
Pingpingf5d90932014-10-27 10:50:04 -0700259
260 // Compose a intent, which is equal to intent5 but the id is different.
261 MultiPointToSinglePointIntent intent5New =
262 staticIntentBuilder(intent5, routeEntry5, "00:00:00:00:00:01");
263 assertTrue(TestUtils.callMethod(router,
264 "compareMultiPointToSinglePointIntents",
265 new Class<?>[] {MultiPointToSinglePointIntent.class,
266 MultiPointToSinglePointIntent.class},
267 intent5, intent5New).equals(true));
Jonathan Hartec2df012014-10-23 16:40:24 -0700268 assertFalse(intent5.equals(intent5New));
Pingpingf5d90932014-10-27 10:50:04 -0700269
270 MultiPointToSinglePointIntent intent6 = intentBuilder(
271 routeEntry6.prefix(), "00:00:00:00:00:01", SW1_ETH1);
272
273 // Set up the bgpRoutes and pushedRouteIntents fields in Router class
274 InvertedRadixTree<RouteEntry> bgpRoutes =
275 new ConcurrentInvertedRadixTree<>(
276 new DefaultByteArrayNodeFactory());
277 bgpRoutes.put(RouteEntry.createBinaryString(routeEntry1.prefix()),
278 routeEntry1);
279 bgpRoutes.put(RouteEntry.createBinaryString(routeEntry3.prefix()),
280 routeEntry3);
281 bgpRoutes.put(RouteEntry.createBinaryString(routeEntry4Update.prefix()),
282 routeEntry4Update);
283 bgpRoutes.put(RouteEntry.createBinaryString(routeEntry5.prefix()),
284 routeEntry5);
285 bgpRoutes.put(RouteEntry.createBinaryString(routeEntry6.prefix()),
286 routeEntry6);
Jonathan Hartec2df012014-10-23 16:40:24 -0700287 bgpRoutes.put(RouteEntry.createBinaryString(routeEntry7.prefix()),
288 routeEntry7);
Pingpingf5d90932014-10-27 10:50:04 -0700289 TestUtils.setField(router, "bgpRoutes", bgpRoutes);
290
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800291 ConcurrentHashMap<Ip4Prefix, MultiPointToSinglePointIntent>
Pingpingf5d90932014-10-27 10:50:04 -0700292 pushedRouteIntents = new ConcurrentHashMap<>();
293 pushedRouteIntents.put(routeEntry1.prefix(), intent1);
294 pushedRouteIntents.put(routeEntry3.prefix(), intent3);
295 pushedRouteIntents.put(routeEntry4Update.prefix(), intent4Update);
296 pushedRouteIntents.put(routeEntry5.prefix(), intent5New);
297 pushedRouteIntents.put(routeEntry6.prefix(), intent6);
Jonathan Hartec2df012014-10-23 16:40:24 -0700298 pushedRouteIntents.put(routeEntry7.prefix(), intent7);
Pingpingf5d90932014-10-27 10:50:04 -0700299 TestUtils.setField(router, "pushedRouteIntents", pushedRouteIntents);
300
301 // Set up expectation
302 reset(intentService);
303 Set<Intent> intents = new HashSet<Intent>();
304 intents.add(intent1);
Jonathan Hartec2df012014-10-23 16:40:24 -0700305 expect(intentService.getIntentState(intent1.id()))
306 .andReturn(IntentState.INSTALLED).anyTimes();
Pingpingf5d90932014-10-27 10:50:04 -0700307 intents.add(intent2);
Jonathan Hartec2df012014-10-23 16:40:24 -0700308 expect(intentService.getIntentState(intent2.id()))
309 .andReturn(IntentState.INSTALLED).anyTimes();
Pingpingf5d90932014-10-27 10:50:04 -0700310 intents.add(intent4);
Jonathan Hartec2df012014-10-23 16:40:24 -0700311 expect(intentService.getIntentState(intent4.id()))
312 .andReturn(IntentState.INSTALLED).anyTimes();
Pingpingf5d90932014-10-27 10:50:04 -0700313 intents.add(intent5);
Jonathan Hartec2df012014-10-23 16:40:24 -0700314 expect(intentService.getIntentState(intent5.id()))
315 .andReturn(IntentState.INSTALLED).anyTimes();
316 intents.add(intent7);
317 expect(intentService.getIntentState(intent7.id()))
318 .andReturn(IntentState.WITHDRAWING).anyTimes();
Pingpingf5d90932014-10-27 10:50:04 -0700319 expect(intentService.getIntents()).andReturn(intents).anyTimes();
320
321 intentService.withdraw(intent2);
322 intentService.submit(intent3);
323 intentService.withdraw(intent4);
324 intentService.submit(intent4Update);
325 intentService.submit(intent6);
Jonathan Hartec2df012014-10-23 16:40:24 -0700326 intentService.submit(intent7);
Pingpingf5d90932014-10-27 10:50:04 -0700327 replay(intentService);
328
329 // Start the test
330 router.leaderChanged(true);
331 TestUtils.callMethod(router, "syncIntents", new Class<?>[] {});
332
333 // Verify
Jonathan Hartec2df012014-10-23 16:40:24 -0700334 assertEquals(router.getRoutes().size(), 6);
Pingpingf5d90932014-10-27 10:50:04 -0700335 assertTrue(router.getRoutes().contains(routeEntry1));
336 assertTrue(router.getRoutes().contains(routeEntry3));
337 assertTrue(router.getRoutes().contains(routeEntry4Update));
338 assertTrue(router.getRoutes().contains(routeEntry5));
339 assertTrue(router.getRoutes().contains(routeEntry6));
340
Jonathan Hartec2df012014-10-23 16:40:24 -0700341 assertEquals(router.getPushedRouteIntents().size(), 6);
Pingpingf5d90932014-10-27 10:50:04 -0700342 assertTrue(router.getPushedRouteIntents().contains(intent1));
343 assertTrue(router.getPushedRouteIntents().contains(intent3));
344 assertTrue(router.getPushedRouteIntents().contains(intent4Update));
345 assertTrue(router.getPushedRouteIntents().contains(intent5));
346 assertTrue(router.getPushedRouteIntents().contains(intent6));
347
348 verify(intentService);
349 }
350
351 /**
352 * MultiPointToSinglePointIntent builder.
353 *
354 * @param ipPrefix the ipPrefix to match
355 * @param nextHopMacAddress to which the destination MAC address in packet
356 * should be rewritten
357 * @param egressPoint to which packets should be sent
358 * @return the constructed MultiPointToSinglePointIntent
359 */
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800360 private MultiPointToSinglePointIntent intentBuilder(Ip4Prefix ipPrefix,
Pingpingf5d90932014-10-27 10:50:04 -0700361 String nextHopMacAddress, ConnectPoint egressPoint) {
362
363 TrafficSelector.Builder selectorBuilder =
364 DefaultTrafficSelector.builder();
365 selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipPrefix);
366
367 TrafficTreatment.Builder treatmentBuilder =
368 DefaultTrafficTreatment.builder();
369 treatmentBuilder.setEthDst(MacAddress.valueOf(nextHopMacAddress));
370
371 Set<ConnectPoint> ingressPoints = new HashSet<ConnectPoint>();
372 for (Interface intf : interfaceService.getInterfaces()) {
373 if (!intf.equals(interfaceService.getInterface(egressPoint))) {
374 ConnectPoint srcPort = intf.connectPoint();
375 ingressPoints.add(srcPort);
376 }
377 }
378 MultiPointToSinglePointIntent intent =
379 new MultiPointToSinglePointIntent(APPID,
380 selectorBuilder.build(), treatmentBuilder.build(),
381 ingressPoints, egressPoint);
382 return intent;
383 }
384
385 /**
386 * A static MultiPointToSinglePointIntent builder, the returned intent is
387 * equal to the input intent except that the id is different.
388 *
389 *
390 * @param intent the intent to be used for building a new intent
391 * @param routeEntry the relative routeEntry of the intent
392 * @return the newly constructed MultiPointToSinglePointIntent
393 * @throws TestUtilsException
394 */
395 private MultiPointToSinglePointIntent staticIntentBuilder(
396 MultiPointToSinglePointIntent intent, RouteEntry routeEntry,
397 String nextHopMacAddress) throws TestUtilsException {
398
399 // Use a different egress ConnectPoint with that in intent
400 // to generate a different id
401 MultiPointToSinglePointIntent intentNew = intentBuilder(
402 routeEntry.prefix(), nextHopMacAddress, SW2_ETH1);
403 TestUtils.setField(intentNew, "egressPoint", intent.egressPoint());
404 TestUtils.setField(intentNew,
405 "ingressPoints", intent.ingressPoints());
406 return intentNew;
407 }
408}