blob: a622e6dafdf3b989079dc2b162ebc10dcdd52b8e [file] [log] [blame]
Sonal Kasliwala0bbe6c2020-01-06 10:46:30 +00001/*
2 * Copyright 2016-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Daniele Moro8ea9e102020-03-24 18:56:52 +010016package org.opencord.cordmcast.impl;
Sonal Kasliwala0bbe6c2020-01-06 10:46:30 +000017
Sonal Kasliwala0bbe6c2020-01-06 10:46:30 +000018import static org.easymock.EasyMock.expect;
19import static org.easymock.EasyMock.replay;
Arjun E Kabf9e6e2020-03-02 10:15:21 +000020
Sonal Kasliwala0bbe6c2020-01-06 10:46:30 +000021import org.easymock.EasyMock;
22import org.junit.After;
23import org.junit.Before;
24import org.junit.Test;
Arjun E Kabf9e6e2020-03-02 10:15:21 +000025import org.onlab.junit.TestUtils;
26import org.onlab.packet.IpAddress;
27import org.onlab.packet.VlanId;
28import org.onosproject.cfg.ComponentConfigAdapter;
Sonal Kasliwala0bbe6c2020-01-06 10:46:30 +000029import org.onosproject.cfg.ComponentConfigService;
30import org.onosproject.mcast.api.McastEvent;
Arjun E Kabf9e6e2020-03-02 10:15:21 +000031import org.onosproject.mcast.api.McastRoute;
Sonal Kasliwala0bbe6c2020-01-06 10:46:30 +000032import org.onosproject.mcast.api.McastRouteUpdate;
33import org.onosproject.mcast.api.MulticastRouteService;
34import org.onosproject.net.ConnectPoint;
35import org.onosproject.net.DeviceId;
36import org.onosproject.net.HostId;
Sonal Kasliwala0bbe6c2020-01-06 10:46:30 +000037import org.onosproject.net.flow.TrafficSelector;
38import org.onosproject.net.flow.TrafficTreatment;
39import org.onosproject.net.flow.criteria.IPCriterion;
40import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
41import org.onosproject.net.flowobjective.Objective;
42import org.onosproject.store.service.StorageServiceAdapter;
43import org.onosproject.store.service.TestConsistentMap;
Daniele Moro8ea9e102020-03-24 18:56:52 +010044import org.opencord.cordmcast.CordMcastStatistics;
45import org.opencord.cordmcast.CordMcastStatisticsEvent;
Sonal Kasliwala0bbe6c2020-01-06 10:46:30 +000046import org.osgi.service.component.ComponentContext;
47import com.google.common.collect.ImmutableMap;
48import com.google.common.collect.Sets;
Arjun E Kabf9e6e2020-03-02 10:15:21 +000049
50import java.util.Dictionary;
51import java.util.HashSet;
52import java.util.Hashtable;
53import java.util.Set;
54import java.util.Map;
55import java.util.Arrays;
56import java.util.Collection;
57
58import static org.junit.Assert.*;
Sonal Kasliwala0bbe6c2020-01-06 10:46:30 +000059import static org.onlab.junit.TestTools.assertAfter;
60
61public class McastTest extends McastTestBase {
62
63 private CordMcast cordMcast;
Arjun E Kabf9e6e2020-03-02 10:15:21 +000064 private CordMcastStatisticsManager cordMcastStatisticsManager;
65
66 private MockCordMcastStatisticsEventListener mockListener = new MockCordMcastStatisticsEventListener();
Sonal Kasliwala0bbe6c2020-01-06 10:46:30 +000067
68 private static final int WAIT_TIMEOUT = 1000;
69 private static final int WAIT = 250;
70 McastRouteUpdate previousSubject, currentSubject;
71
72 @Before
73 public void setUp() {
74 cordMcast = new CordMcast();
Arjun E Kabf9e6e2020-03-02 10:15:21 +000075 cordMcastStatisticsManager = new CordMcastStatisticsManager();
Sonal Kasliwala0bbe6c2020-01-06 10:46:30 +000076
77 cordMcast.coreService = new MockCoreService();
Arjun E Kabf9e6e2020-03-02 10:15:21 +000078 cordMcast.networkConfig = new TestNetworkConfigRegistry();
Sonal Kasliwala0bbe6c2020-01-06 10:46:30 +000079 cordMcast.flowObjectiveService = new MockFlowObjectiveService();
80 cordMcast.mastershipService = new TestMastershipService();
Esin Karaman996177c2020-03-05 13:21:09 +000081 cordMcast.deviceService = new MockDeviceService();
Arjun E Kabf9e6e2020-03-02 10:15:21 +000082 cordMcast.componentConfigService = new ComponentConfigAdapter();
83 cordMcastStatisticsManager.componentConfigService = new ComponentConfigAdapter();
84 cordMcastStatisticsManager.addListener(mockListener);
Esin Karaman996177c2020-03-05 13:21:09 +000085 cordMcast.sadisService = new MockSadisService();
Arjun E Kabf9e6e2020-03-02 10:15:21 +000086 cordMcast.cordMcastStatisticsService = cordMcastStatisticsManager;
Sonal Kasliwala0bbe6c2020-01-06 10:46:30 +000087
Arjun E Kabf9e6e2020-03-02 10:15:21 +000088 cordMcast.storageService = EasyMock.createMock(StorageServiceAdapter.class);
Sonal Kasliwala0bbe6c2020-01-06 10:46:30 +000089 expect(cordMcast.storageService.consistentMapBuilder()).andReturn(new TestConsistentMap.Builder<>());
90 replay(cordMcast.storageService);
91
92 Dictionary<String, Object> cfgDict = new Hashtable<String, Object>();
93 cfgDict.put("vlanEnabled", false);
Arjun E Kabf9e6e2020-03-02 10:15:21 +000094 cfgDict.put("eventGenerationPeriodInSeconds", EVENT_GENERATION_PERIOD);
95
96 cordMcast.componentConfigService = EasyMock.createNiceMock(ComponentConfigService.class);
Sonal Kasliwala0bbe6c2020-01-06 10:46:30 +000097 replay(cordMcast.componentConfigService);
98
Arjun E Kabf9e6e2020-03-02 10:15:21 +000099 Set<McastRoute> route1Set = new HashSet<McastRoute>();
100 route1Set.add(route1);
101
Sonal Kasliwala0bbe6c2020-01-06 10:46:30 +0000102 cordMcast.mcastService = EasyMock.createNiceMock(MulticastRouteService.class);
103 expect(cordMcast.mcastService.getRoutes()).andReturn(Sets.newHashSet());
104 replay(cordMcast.mcastService);
105
Arjun E Kabf9e6e2020-03-02 10:15:21 +0000106 cordMcastStatisticsManager.mcastService = EasyMock.createNiceMock(MulticastRouteService.class);
107 expect(cordMcastStatisticsManager.mcastService.getRoutes()).andReturn(route1Set).times(2);
108 replay(cordMcastStatisticsManager.mcastService);
109
110 TestUtils.setField(cordMcastStatisticsManager, "eventDispatcher", new TestEventDispatcher());
111
Sonal Kasliwala0bbe6c2020-01-06 10:46:30 +0000112 ComponentContext componentContext = EasyMock.createMock(ComponentContext.class);
Arjun E Kabf9e6e2020-03-02 10:15:21 +0000113 expect(componentContext.getProperties()).andReturn(cfgDict).times(2);
Sonal Kasliwala0bbe6c2020-01-06 10:46:30 +0000114 replay(componentContext);
Arjun E Kabf9e6e2020-03-02 10:15:21 +0000115 cordMcast.cordMcastStatisticsService = cordMcastStatisticsManager;
116 cordMcastStatisticsManager.activate(componentContext);
Sonal Kasliwala0bbe6c2020-01-06 10:46:30 +0000117
118 cordMcast.activate(componentContext);
Sonal Kasliwala0bbe6c2020-01-06 10:46:30 +0000119 }
120
121 @After
122 public void tearDown() {
123 cordMcast.deactivate();
124 forwardMap.clear();
125 nextMap.clear();
126 }
127
128 @Test
129 public void testAddingSinkEvent() throws InterruptedException {
130
131 Set<ConnectPoint> sinks2Cp = new HashSet<ConnectPoint>(Arrays.asList(CONNECT_POINT_B));
132 Map<HostId, Set<ConnectPoint>> sinks2 = ImmutableMap.of(HOST_ID_NONE, sinks2Cp);
133
134 //Adding the details to create different routes
135 previousSubject = McastRouteUpdate.mcastRouteUpdate(route1, sources, sinks);
136 currentSubject = McastRouteUpdate.mcastRouteUpdate(route1, sources, sinks2);
137 // Creating new mcast event for adding sink
138 McastEvent event = new McastEvent(McastEvent.Type.SINKS_ADDED, previousSubject, currentSubject);
139 cordMcast.listener.event(event);
140 synchronized (forwardMap) {
141 forwardMap.wait(WAIT_TIMEOUT);
142 }
143
144 // ForwardMap will contain the operation "Add" in the flowObjective. None -> CP_B
145 assertNotNull(forwardMap.get(DEVICE_ID_OF_A));
146 assertTrue(forwardMap.get(DEVICE_ID_OF_A).op() == Objective.Operation.ADD);
147
148 // Output port number will be PORT_B i.e. 16
149 Collection<TrafficTreatment> traffictreatMentCollection =
150 nextMap.get(DEVICE_ID_OF_A).next();
151 assertTrue(1 == traffictreatMentCollection.size());
152 OutputInstruction output = null;
153 for (TrafficTreatment trafficTreatment : traffictreatMentCollection) {
154 output = outputPort(trafficTreatment);
155 }
156 assertNotNull(output);
157 assertTrue(PORT_B == output.port());
158 // Checking the group ip address
159 TrafficSelector trafficSelector = forwardMap.get(DEVICE_ID_OF_A).selector();
160 IPCriterion ipCriterion = ipAddress(trafficSelector);
161 assertNotNull(ipCriterion);
162 assertTrue(MULTICAST_IP.equals(ipCriterion.ip().address()));
163
164 }
165
166 @Test
167 public void testAddToExistingSinkEvent() throws InterruptedException {
168
169 // Adding first sink (none --> CP_B)
170 testAddingSinkEvent();
171
172 Set<ConnectPoint> sinksCp = new HashSet<ConnectPoint>(Arrays.asList(CONNECT_POINT_B));
173 Map<HostId, Set<ConnectPoint>> sinks = ImmutableMap.of(HOST_ID_NONE, sinksCp);
174 previousSubject = McastRouteUpdate.mcastRouteUpdate(route1, sources, sinks);
175 sinksCp = new HashSet<ConnectPoint>(Arrays.asList(CONNECT_POINT_B, CONNECT_POINT_C));
176 sinks = ImmutableMap.of(HOST_ID_NONE, sinksCp);
177 currentSubject = McastRouteUpdate.mcastRouteUpdate(route1, sources, sinks);
178 // Again listening the mcast event with different output port ( none --> CP_B, CP_C)
179 McastEvent event = new McastEvent(McastEvent.Type.SINKS_ADDED, previousSubject, currentSubject);
180 cordMcast.listener.event(event);
181
182 // NextMap will contain the operation "ADD_TO_EXISTING" in the DefaultNextObjective.
183 assertAfter(WAIT_TIMEOUT, WAIT_TIMEOUT * 2, () ->
184 assertTrue(nextMap.get(DEVICE_ID_OF_A).op() == Objective.Operation.ADD_TO_EXISTING));
185 // Output port number will be changed to 24 i.e. PORT_C
186 Collection<TrafficTreatment> traffictreatMentCollection = nextMap.get(DEVICE_ID_OF_A).next();
187 assertTrue(1 == traffictreatMentCollection.size());
188 OutputInstruction output = null;
189 for (TrafficTreatment trafficTreatment : traffictreatMentCollection) {
190 output = outputPort(trafficTreatment);
191 }
192 assertNotNull(output);
193 assertTrue(PORT_C == output.port());
194 }
195
196 @Test
197 public void testRemoveSinkEvent() throws InterruptedException {
198
199 testAddToExistingSinkEvent();
200 // Handling the mcast event for removing sink.
201 Set<ConnectPoint> sinksCp = new HashSet<ConnectPoint>(Arrays.asList(CONNECT_POINT_B, CONNECT_POINT_C));
202 Map<HostId, Set<ConnectPoint>> sinks = ImmutableMap.of(HOST_ID_NONE, sinksCp);
203 previousSubject = McastRouteUpdate.mcastRouteUpdate(route1, sources, sinks);
204 sinksCp = new HashSet<ConnectPoint>(Arrays.asList(CONNECT_POINT_C));
205 sinks = ImmutableMap.of(HOST_ID_NONE, sinksCp);
206 currentSubject = McastRouteUpdate.mcastRouteUpdate(route1, sources, sinks);
207 McastEvent event = new McastEvent(McastEvent.Type.SINKS_REMOVED, previousSubject, currentSubject);
208 cordMcast.listener.event(event);
209 // Operation will be REMOVE_FROM_EXISTING and nextMap will be updated. ( None --> CP_C)
210 assertAfter(WAIT_TIMEOUT, WAIT_TIMEOUT * 2, () ->
211 assertTrue(nextMap.get(DEVICE_ID_OF_A).op() == Objective.Operation.REMOVE_FROM_EXISTING));
212
213 // Output port number will be PORT_B i.e. 16
214 // Port_B is removed from the group.
215 Collection<TrafficTreatment> traffictreatMentCollection =
216 nextMap.get(DEVICE_ID_OF_A).next();
217 assertTrue(1 == traffictreatMentCollection.size());
218 OutputInstruction output = null;
219 for (TrafficTreatment trafficTreatment : traffictreatMentCollection) {
220 output = outputPort(trafficTreatment);
221 }
222 assertNotNull(output);
223 assertTrue(PORT_B == output.port());
224
225 }
226
227 @Test
228 public void testRemoveLastSinkEvent() throws InterruptedException {
229
230 testRemoveSinkEvent();
231 // Handling the mcast event for removing sink.
232 Set<ConnectPoint> sinksCp = new HashSet<ConnectPoint>(Arrays.asList(CONNECT_POINT_C));
233 Map<HostId, Set<ConnectPoint>> sinks = ImmutableMap.of(HOST_ID_NONE, sinksCp);
234 previousSubject = McastRouteUpdate.mcastRouteUpdate(route1, sources, sinks);
235 sinksCp = new HashSet<ConnectPoint>(Arrays.asList());
236 sinks = ImmutableMap.of(HOST_ID_NONE, sinksCp);
237 currentSubject = McastRouteUpdate.mcastRouteUpdate(route1, sources, sinks);
238 McastEvent event = new McastEvent(McastEvent.Type.SINKS_REMOVED, previousSubject, currentSubject);
239 cordMcast.listener.event(event);
240
241 // Operation will be REMOVE_FROM_EXISTING and nextMap will be updated. None --> { }
242 assertAfter(WAIT_TIMEOUT, WAIT_TIMEOUT * 2, () ->
243 assertTrue(nextMap.get(DEVICE_ID_OF_A).op() == Objective.Operation.REMOVE_FROM_EXISTING));
244
245 // Output port number will be changed to 24 i.e. PORT_C
246 Collection<TrafficTreatment> traffictreatMentCollection = nextMap.get(DEVICE_ID_OF_A).next();
247 assertTrue(1 == traffictreatMentCollection.size());
248 OutputInstruction output = null;
249 for (TrafficTreatment trafficTreatment : traffictreatMentCollection) {
250 output = outputPort(trafficTreatment);
251 }
252 assertNotNull(output);
253 assertTrue(PORT_C == output.port());
254 }
255
256 @Test
257 public void testUnkownOltDevice() throws InterruptedException {
258
259 // Configuration of mcast event for unknown olt device
260 final DeviceId deviceIdOfB = DeviceId.deviceId("of:1");
261
262 ConnectPoint connectPointA = new ConnectPoint(deviceIdOfB, PORT_A);
263 ConnectPoint connectPointB = new ConnectPoint(deviceIdOfB, PORT_B);
264 Set<ConnectPoint> sourcesCp = new HashSet<ConnectPoint>(Arrays.asList(connectPointA));
265 Set<ConnectPoint> sinksCp = new HashSet<ConnectPoint>(Arrays.asList());
266 Set<ConnectPoint> sinks2Cp = new HashSet<ConnectPoint>(Arrays.asList(connectPointB));
267 Map<HostId, Set<ConnectPoint>> sources = ImmutableMap.of(HOST_ID_NONE, sourcesCp);
268
269 Map<HostId, Set<ConnectPoint>> sinks = ImmutableMap.of(HOST_ID_NONE, sinksCp);
270 Map<HostId, Set<ConnectPoint>> sinks2 = ImmutableMap.of(HOST_ID_NONE, sinks2Cp);
271 //Adding the details to create different routes
272 McastRouteUpdate previousSubject = McastRouteUpdate.mcastRouteUpdate(route1, sources, sinks);
273 McastRouteUpdate currentSubject = McastRouteUpdate.mcastRouteUpdate(route1, sources, sinks2);
274 // Creating new mcast event for adding sink
275 McastEvent event = new McastEvent(McastEvent.Type.SINKS_ADDED, previousSubject, currentSubject);
276 cordMcast.listener.event(event);
277 // OltInfo flag is set to true when olt device is unkown
278 assertAfter(WAIT, WAIT * 2, () -> assertTrue(knownOltFlag));
279 assertTrue(0 == forwardMap.size());
280 assertTrue(0 == nextMap.size());
281
282 }
283
284 @Test
285 public void testRouteAddedEvent() throws InterruptedException {
286
287 //Adding the details to create different routes
288 previousSubject = McastRouteUpdate.mcastRouteUpdate(route1, emptySource, sinks);
289 currentSubject = McastRouteUpdate.mcastRouteUpdate(route1, emptySource, sinks);
290 // Creating new mcast event for route adding
291 McastEvent event = new McastEvent(McastEvent.Type.ROUTE_ADDED, previousSubject, currentSubject);
292 cordMcast.listener.event(event);
293 // There will be no forwarding objective
294 assertAfter(WAIT, WAIT * 2, () -> assertTrue(0 == forwardMap.size()));
295 assertTrue(0 == nextMap.size());
296
297 }
298
299
300 @Test
301 public void testRouteRemovedEvent() throws InterruptedException {
302
303 testRouteAddedEvent();
304
305 //Adding the details to create different routes
306 previousSubject = McastRouteUpdate.mcastRouteUpdate(route1, emptySource, sinks);
307 currentSubject = McastRouteUpdate.mcastRouteUpdate(route1, emptySource, sinks);
308 // Creating new mcast event for route removing
309 McastEvent event = new McastEvent(McastEvent.Type.ROUTE_REMOVED, previousSubject, currentSubject);
310 cordMcast.listener.event(event);
311 // There will be no forwarding objective
312 assertAfter(WAIT, WAIT * 2, () -> assertTrue(0 == forwardMap.size()));
313 assertTrue(0 == nextMap.size());
314
315 }
316
Sonal Kasliwala0bbe6c2020-01-06 10:46:30 +0000317 @Test
318 public void testSourceAddedEvent() throws InterruptedException {
319
320 // Adding route before adding source.
321 testRouteAddedEvent();
322
323 //Adding the details to create different routes
324 previousSubject = McastRouteUpdate.mcastRouteUpdate(route1, emptySource, sinks);
325 currentSubject = McastRouteUpdate.mcastRouteUpdate(route1, sources, sinks);
326 // Creating new mcast event for source adding
327 McastEvent event = new McastEvent(McastEvent.Type.SOURCES_ADDED, previousSubject, currentSubject);
328 cordMcast.listener.event(event);
329 // There will be no forwarding objective
330 assertAfter(WAIT, WAIT * 2, () -> assertTrue(0 == forwardMap.size()));
331 assertTrue(0 == nextMap.size());
332
333 }
334
335 @Test
336 public void testSourcesRemovedEvent() throws InterruptedException {
337
338 testSourceAddedEvent();
339
340 //Adding the details to create different routes
341 previousSubject = McastRouteUpdate.mcastRouteUpdate(route1, sources, sinks);
342 currentSubject = McastRouteUpdate.mcastRouteUpdate(route1, emptySource, sinks);
343 // Creating new mcast event for removing source
344 // Warning message of unknown event will be displayed.
345 McastEvent event = new McastEvent(McastEvent.Type.SOURCES_REMOVED, previousSubject, currentSubject);
346 cordMcast.listener.event(event);
347 assertAfter(WAIT, WAIT * 2, () -> assertTrue(0 == forwardMap.size()));
348 assertTrue(0 == nextMap.size());
349 }
350
Arjun E Kabf9e6e2020-03-02 10:15:21 +0000351 @Test
352 public void mcastTestEventGeneration() throws InterruptedException {
353 //fetching route details used to push CordMcastStatisticsEvent.
354 IpAddress testGroup = route1.group();
355 String testSource = route1.source().isEmpty() ? "*" : route1.source().get().toString();
356 VlanId testVlan = cordMcast.assignedVlan();
357
358 // Thread is scheduled without any delay
359 assertAfter(WAIT, WAIT * 2, () ->
360 assertEquals(1, mockListener.mcastEventList.size()));
361
362 for (CordMcastStatisticsEvent event: mockListener.mcastEventList) {
363 assertEquals(event.type(), CordMcastStatisticsEvent.Type.STATUS_UPDATE);
364 }
365
366 CordMcastStatistics cordMcastStatistics = mockListener.mcastEventList.get(0).subject().get(0);
367 assertEquals(VlanId.NONE, cordMcastStatistics.getVlanId());
368 assertEquals(testVlan, cordMcastStatistics.getVlanId());
369 assertEquals(testSource, cordMcastStatistics.getSourceAddress());
370 assertEquals(testGroup, cordMcastStatistics.getGroupAddress());
371
372 // Test for vlanEnabled
373 Dictionary<String, Object> cfgDict = new Hashtable<>();
374 cfgDict.put("vlanEnabled", true);
375
376 ComponentContext componentContext = EasyMock.createMock(ComponentContext.class);
377 expect(componentContext.getProperties()).andReturn(cfgDict);
378 replay(componentContext);
379 cordMcast.modified(componentContext);
380 testVlan = cordMcast.assignedVlan();
381
382 assertAfter(EVENT_GENERATION_PERIOD, EVENT_GENERATION_PERIOD * 1000, () ->
383 assertEquals(2, mockListener.mcastEventList.size()));
384
385 for (CordMcastStatisticsEvent event: mockListener.mcastEventList) {
386 assertEquals(event.type(), CordMcastStatisticsEvent.Type.STATUS_UPDATE);
387 }
388
389 cordMcastStatistics = mockListener.mcastEventList.get(1).subject().get(0);
390 assertNotEquals(VlanId.NONE, cordMcastStatistics.getVlanId());
391 assertEquals(testVlan, cordMcastStatistics.getVlanId());
392 assertEquals(testSource, cordMcastStatistics.getSourceAddress());
393 assertEquals(testGroup, cordMcastStatistics.getGroupAddress());
394 }
Sonal Kasliwala0bbe6c2020-01-06 10:46:30 +0000395}