blob: 326311d7e5f4579a0ab84c86a6fe5f4655fceb40 [file] [log] [blame]
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07001/*
2 * Copyright 2021-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 */
16
17package org.opencord.olt.impl;
18
19import org.junit.After;
20import org.junit.Assert;
21import org.junit.Before;
22import org.junit.Test;
23import org.mockito.Mockito;
24import org.onlab.packet.ChassisId;
25import org.onlab.packet.EthType;
26import org.onlab.packet.IPv4;
27import org.onlab.packet.IPv6;
28import org.onlab.packet.MacAddress;
29import org.onlab.packet.TpPort;
30import org.onlab.packet.VlanId;
31import org.onosproject.cfg.ComponentConfigAdapter;
32import org.onosproject.core.ApplicationId;
33import org.onosproject.core.CoreServiceAdapter;
34import org.onosproject.core.DefaultApplicationId;
35import org.onosproject.net.ConnectPoint;
36import org.onosproject.net.DefaultAnnotations;
37import org.onosproject.net.DefaultDevice;
38import org.onosproject.net.DefaultHost;
39import org.onosproject.net.DefaultPort;
40import org.onosproject.net.Device;
41import org.onosproject.net.DeviceId;
42import org.onosproject.net.Host;
43import org.onosproject.net.HostId;
44import org.onosproject.net.HostLocation;
45import org.onosproject.net.Port;
46import org.onosproject.net.PortNumber;
Matteo Scandolo97449bb2021-12-09 15:33:46 -080047import org.onosproject.net.device.DeviceService;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070048import org.onosproject.net.flow.DefaultFlowRule;
49import org.onosproject.net.flow.DefaultTrafficTreatment;
50import org.onosproject.net.flow.FlowRule;
51import org.onosproject.net.flow.FlowRuleEvent;
52import org.onosproject.net.flow.FlowRuleService;
53import org.onosproject.net.flow.criteria.Criteria;
54import org.onosproject.net.flowobjective.DefaultFilteringObjective;
55import org.onosproject.net.flowobjective.FilteringObjective;
56import org.onosproject.net.flowobjective.FlowObjectiveService;
57import org.onosproject.net.host.HostService;
58import org.onosproject.net.meter.MeterId;
59import org.onosproject.net.provider.ProviderId;
60import org.onosproject.store.service.TestStorageService;
61import org.opencord.sadis.BaseInformationService;
62import org.opencord.sadis.SadisService;
63import org.opencord.sadis.SubscriberAndDeviceInformation;
64import org.opencord.sadis.UniTagInformation;
65
66import java.util.Arrays;
67import java.util.HashSet;
68import java.util.LinkedList;
69import java.util.List;
70import java.util.Set;
71
72import static org.mockito.Matchers.any;
73import static org.mockito.Matchers.argThat;
74import static org.mockito.Matchers.eq;
75import static org.mockito.Mockito.doReturn;
76import static org.mockito.Mockito.never;
77import static org.mockito.Mockito.spy;
78import static org.mockito.Mockito.times;
79import static org.mockito.Mockito.verify;
80import static org.onosproject.net.AnnotationKeys.PORT_NAME;
Matteo Scandolo2542e5d2021-12-01 16:53:41 -080081import static org.opencord.olt.impl.OltFlowService.OltFlowsStatus.ERROR;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070082import static org.opencord.olt.impl.OltFlowService.OltFlowsStatus.NONE;
83import static org.opencord.olt.impl.OltFlowService.OltFlowsStatus.ADDED;
84import static org.opencord.olt.impl.OltFlowService.OltFlowsStatus.PENDING_ADD;
Matteo Scandolo2542e5d2021-12-01 16:53:41 -080085import static org.opencord.olt.impl.OltFlowService.OltFlowsStatus.PENDING_REMOVE;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070086import static org.opencord.olt.impl.OltFlowService.OltFlowsStatus.REMOVED;
87import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_BP_ID_DEFAULT;
Matteo Scandolo97449bb2021-12-09 15:33:46 -080088import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_MCAST_SERVICE_NAME;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070089
90public class OltFlowServiceTest extends OltTestHelpers {
91
Matteo Scandolo97449bb2021-12-09 15:33:46 -080092 private OltFlowService component;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070093 private OltFlowService oltFlowService;
94 OltFlowService.InternalFlowListener internalFlowListener;
95 private final ApplicationId testAppId = new DefaultApplicationId(1, "org.opencord.olt.test");
96 private final short eapolDefaultVlan = 4091;
97
98 private final DeviceId deviceId = DeviceId.deviceId("test-device");
99 private final Device testDevice = new DefaultDevice(ProviderId.NONE, deviceId, Device.Type.OLT,
100 "testManufacturer", "1.0", "1.0", "SN", new ChassisId(1));
101 Port nniPort = new OltPort(testDevice, true, PortNumber.portNumber(1048576),
102 DefaultAnnotations.builder().set(PORT_NAME, "nni-1").build());
103 Port nniPortDisabled = new OltPort(testDevice, false, PortNumber.portNumber(1048576),
104 DefaultAnnotations.builder().set(PORT_NAME, "nni-1").build());
105 Port uniUpdateEnabled = new OltPort(testDevice, true, PortNumber.portNumber(16),
106 DefaultAnnotations.builder().set(PORT_NAME, "uni-1").build());
107
108 @Before
109 public void setUp() {
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800110 component = new OltFlowService();
111 component.cfgService = new ComponentConfigAdapter();
112 component.sadisService = Mockito.mock(SadisService.class);
113 component.coreService = Mockito.spy(new CoreServiceAdapter());
114 component.oltMeterService = Mockito.mock(OltMeterService.class);
115 component.flowObjectiveService = Mockito.mock(FlowObjectiveService.class);
116 component.hostService = Mockito.mock(HostService.class);
117 component.flowRuleService = Mockito.mock(FlowRuleService.class);
118 component.storageService = new TestStorageService();
119 component.oltDeviceService = Mockito.mock(OltDeviceService.class);
120 component.appId = testAppId;
121 component.deviceService = Mockito.mock(DeviceService.class);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700122
123 doReturn(Mockito.mock(BaseInformationService.class))
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800124 .when(component.sadisService).getSubscriberInfoService();
125 doReturn(testAppId).when(component.coreService).registerApplication("org.opencord.olt");
126 component.activate(null);
127 component.bindSadisService(component.sadisService);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700128
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800129 internalFlowListener = spy(component.internalFlowListener);
130
131 oltFlowService = spy(component);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700132 }
133
134 @After
135 public void tearDown() {
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800136 component.deactivate(null);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700137 }
138
139 @Test
140 public void testUpdateConnectPointStatus() {
141
142 DeviceId deviceId = DeviceId.deviceId("test-device");
143 ProviderId pid = new ProviderId("of", "foo");
144 Device device =
145 new DefaultDevice(pid, deviceId, Device.Type.OLT, "", "", "", "", null);
146 Port port1 = new DefaultPort(device, PortNumber.portNumber(1), true,
147 DefaultAnnotations.builder().set(PORT_NAME, "port-1").build());
148 Port port2 = new DefaultPort(device, PortNumber.portNumber(2), true,
149 DefaultAnnotations.builder().set(PORT_NAME, "port-2").build());
150 Port port3 = new DefaultPort(device, PortNumber.portNumber(3), true,
151 DefaultAnnotations.builder().set(PORT_NAME, "port-3").build());
152
153 ServiceKey sk1 = new ServiceKey(new AccessDevicePort(port1), new UniTagInformation());
154 ServiceKey sk2 = new ServiceKey(new AccessDevicePort(port2), new UniTagInformation());
155 ServiceKey sk3 = new ServiceKey(new AccessDevicePort(port3), new UniTagInformation());
156
157 // cpStatus map for the test
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800158 component.cpStatus = component.storageService.
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700159 <ServiceKey, OltPortStatus>consistentMapBuilder().build().asJavaMap();
160 OltPortStatus cp1Status = new OltPortStatus(PENDING_ADD, NONE, NONE);
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800161 component.cpStatus.put(sk1, cp1Status);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700162
163 //check that we only update the provided value
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800164 component.updateConnectPointStatus(sk1, ADDED, null, null);
165 OltPortStatus updated = component.cpStatus.get(sk1);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700166 Assert.assertEquals(ADDED, updated.defaultEapolStatus);
167 Assert.assertEquals(NONE, updated.subscriberFlowsStatus);
168 Assert.assertEquals(NONE, updated.dhcpStatus);
169
170 // check that it creates an entry if it does not exist
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800171 component.updateConnectPointStatus(sk2, PENDING_ADD, NONE, NONE);
172 Assert.assertNotNull(component.cpStatus.get(sk2));
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700173
174 // check that if we create a new entry with null values they're converted to NONE
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800175 component.updateConnectPointStatus(sk3, null, null, null);
176 updated = component.cpStatus.get(sk3);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700177 Assert.assertEquals(NONE, updated.defaultEapolStatus);
178 Assert.assertEquals(NONE, updated.subscriberFlowsStatus);
179 Assert.assertEquals(NONE, updated.dhcpStatus);
180 }
181
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800182 /**
183 * If the flow status is PENDING_REMOVE or ERROR and there is no
184 * previous state in the map that don't update it.
185 * In case of a device disconnection we immediately wipe out the status,
186 * but then flows might update the cpStatus map. That result
187 */
188 @Test
189 public void doNotUpdateConnectPointStatus() {
190 DeviceId deviceId = DeviceId.deviceId("test-device");
191 ProviderId pid = new ProviderId("of", "foo");
192 Device device =
193 new DefaultDevice(pid, deviceId, Device.Type.OLT, "", "", "", "", null);
194 Port port1 = new DefaultPort(device, PortNumber.portNumber(1), true,
195 DefaultAnnotations.builder().set(PORT_NAME, "port-1").build());
196
197 ServiceKey sk1 = new ServiceKey(new AccessDevicePort(port1), new UniTagInformation());
198
199 // cpStatus map for the test
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800200 component.cpStatus = component.storageService.
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800201 <ServiceKey, OltPortStatus>consistentMapBuilder().build().asJavaMap();
202
203 // check that an entry is not created if the only status is pending remove
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800204 component.updateConnectPointStatus(sk1, null, null, PENDING_REMOVE);
205 OltPortStatus entry = component.cpStatus.get(sk1);
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800206 Assert.assertNull(entry);
207
208 // check that an entry is not created if the only status is ERROR
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800209 component.updateConnectPointStatus(sk1, null, null, ERROR);
210 entry = component.cpStatus.get(sk1);
Matteo Scandolo2542e5d2021-12-01 16:53:41 -0800211 Assert.assertNull(entry);
212 }
213
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700214 @Test
215 public void testHasDefaultEapol() {
216 DeviceId deviceId = DeviceId.deviceId("test-device");
217 ProviderId pid = new ProviderId("of", "foo");
218
219 Device device =
220 new DefaultDevice(pid, deviceId, Device.Type.OLT, "", "", "", "", null);
221
222 Port port = new DefaultPort(device, PortNumber.portNumber(16), true,
223 DefaultAnnotations.builder().set(PORT_NAME, "name-1").build());
224 ServiceKey skWithStatus = new ServiceKey(new AccessDevicePort(port),
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800225 component.defaultEapolUniTag);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700226
227 Port port17 = new DefaultPort(device, PortNumber.portNumber(17), true,
228 DefaultAnnotations.builder().set(PORT_NAME, "name-1").build());
229
230 OltPortStatus portStatusAdded = new OltPortStatus(
231 OltFlowService.OltFlowsStatus.ADDED,
232 NONE,
233 null
234 );
235
236 OltPortStatus portStatusRemoved = new OltPortStatus(
237 REMOVED,
238 NONE,
239 null
240 );
241
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800242 component.cpStatus.put(skWithStatus, portStatusAdded);
243 Assert.assertTrue(component.hasDefaultEapol(port));
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700244
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800245 component.cpStatus.put(skWithStatus, portStatusRemoved);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700246
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800247 Assert.assertFalse(component.hasDefaultEapol(port17));
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700248 }
249
250 @Test
251 public void testHasSubscriberFlows() {
252 // TODO test with multiple services
253 DeviceId deviceId = DeviceId.deviceId("test-device");
254 ProviderId pid = new ProviderId("of", "foo");
255
256 Device device =
257 new DefaultDevice(pid, deviceId, Device.Type.OLT, "", "", "", "", null);
258
259 Port port = new DefaultPort(device, PortNumber.portNumber(16), true,
260 DefaultAnnotations.builder().set(PORT_NAME, "name-1").build());
261
262 UniTagInformation uti = new UniTagInformation.Builder().setServiceName("test").build();
263 ServiceKey skWithStatus = new ServiceKey(new AccessDevicePort(port),
264 uti);
265
266 OltPortStatus withDefaultEapol = new OltPortStatus(
267 ADDED,
268 NONE,
269 NONE
270 );
271
272 OltPortStatus withDhcp = new OltPortStatus(
273 REMOVED,
274 NONE,
275 ADDED
276 );
277
278 OltPortStatus withSubFlow = new OltPortStatus(
279 REMOVED,
280 ADDED,
281 ADDED
282 );
283
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800284 component.cpStatus.put(skWithStatus, withDefaultEapol);
285 Assert.assertFalse(component.hasSubscriberFlows(port, uti));
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700286
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800287 component.cpStatus.put(skWithStatus, withDhcp);
288 Assert.assertTrue(component.hasDhcpFlows(port, uti));
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700289
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800290 component.cpStatus.put(skWithStatus, withSubFlow);
291 Assert.assertTrue(component.hasSubscriberFlows(port, uti));
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700292 }
293
294 @Test
295 public void testHandleBasicPortFlowsNoEapol() throws Exception {
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800296 component.enableEapol = false;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700297 // create empty service for testing
298 List<UniTagInformation> uniTagInformationList = new LinkedList<>();
299 UniTagInformation empty = new UniTagInformation.Builder().build();
300 uniTagInformationList.add(empty);
301
302 SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
303 si.setUniTagList(uniTagInformationList);
304
305 final DiscoveredSubscriber addedSub =
306 new DiscoveredSubscriber(testDevice,
307 uniUpdateEnabled, DiscoveredSubscriber.Status.ADDED,
308 false, si);
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800309 component.handleBasicPortFlows(addedSub, DEFAULT_BP_ID_DEFAULT, DEFAULT_BP_ID_DEFAULT);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700310 // if eapol is not enabled there's nothing we need to do,
311 // so make sure we don't even call sadis
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800312 verify(component.subsService, never()).get(any());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700313 }
314
315 @Test
316 public void testHandleBasicPortFlowsWithEapolNoMeter() throws Exception {
317 // create empty service for testing
318 List<UniTagInformation> uniTagInformationList = new LinkedList<>();
319 UniTagInformation empty = new UniTagInformation.Builder().build();
320 uniTagInformationList.add(empty);
321 SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
322 si.setUniTagList(uniTagInformationList);
323 final DiscoveredSubscriber addedSub =
324 new DiscoveredSubscriber(testDevice,
325 uniUpdateEnabled, DiscoveredSubscriber.Status.ADDED,
326 false, si);
327 // whether the meter is pending or not is up to the createMeter method to handle
328 // we just don't proceed with the subscriber till it's ready
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800329 doReturn(false).when(component.oltMeterService)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700330 .createMeter(addedSub.device.id(), DEFAULT_BP_ID_DEFAULT);
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800331 boolean res = component.handleBasicPortFlows(addedSub, DEFAULT_BP_ID_DEFAULT, DEFAULT_BP_ID_DEFAULT);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700332
333 Assert.assertFalse(res);
334
335 // we do not create flows
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800336 verify(component.flowObjectiveService, never())
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700337 .filter(eq(addedSub.device.id()), any());
338 }
339
340 @Test
341 public void testHandleBasicPortFlowsWithEapolAddedMeter() throws Exception {
342 // create empty service for testing
343 List<UniTagInformation> uniTagInformationList = new LinkedList<>();
344 UniTagInformation empty = new UniTagInformation.Builder().build();
345 uniTagInformationList.add(empty);
346 SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
347 si.setUniTagList(uniTagInformationList);
348 final DiscoveredSubscriber addedSub =
349 new DiscoveredSubscriber(testDevice,
350 uniUpdateEnabled, DiscoveredSubscriber.Status.ADDED,
351 false, si);
352 // this is the happy case, we have the meter so we check that the default EAPOL flow
353 // is installed
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800354 doReturn(true).when(component.oltMeterService)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700355 .createMeter(deviceId, DEFAULT_BP_ID_DEFAULT);
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800356 doReturn(true).when(component.oltMeterService)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700357 .hasMeterByBandwidthProfile(deviceId, DEFAULT_BP_ID_DEFAULT);
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800358 doReturn(MeterId.meterId(1)).when(component.oltMeterService)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700359 .getMeterIdForBandwidthProfile(deviceId, DEFAULT_BP_ID_DEFAULT);
360
361 FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
362 .permit()
363 .withKey(Criteria.matchInPort(uniUpdateEnabled.number()))
364 .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()))
365 .fromApp(testAppId)
366 .withPriority(10000)
367 .withMeta(
368 DefaultTrafficTreatment.builder()
369 .meter(MeterId.meterId(1))
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800370 .writeMetadata(component.createTechProfValueForWriteMetadata(
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700371 VlanId.vlanId(eapolDefaultVlan),
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800372 component.defaultTechProfileId, MeterId.meterId(1)), 0)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700373 .setOutput(PortNumber.CONTROLLER)
374 .pushVlan()
375 .setVlanId(VlanId.vlanId(eapolDefaultVlan)).build()
376 )
377 .add();
378
379
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800380 component.handleBasicPortFlows(addedSub, DEFAULT_BP_ID_DEFAULT, DEFAULT_BP_ID_DEFAULT);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700381
382 // we check for an existing meter (present)
383 // FIXME understand why the above test invokes this call and this one doesn't
384// verify(oltFlowService.oltMeterService, times(1))
385// .hasMeterByBandwidthProfile(eq(addedSub.device.id()), eq(DEFAULT_BP_ID_DEFAULT));
386
387 // the meter exist, no need to check for PENDING or to create it
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800388 verify(component.oltMeterService, never())
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700389 .hasPendingMeterByBandwidthProfile(eq(deviceId), eq(DEFAULT_BP_ID_DEFAULT));
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800390 verify(component.oltMeterService, never())
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700391 .createMeterForBp(eq(deviceId), eq(DEFAULT_BP_ID_DEFAULT));
392
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800393 verify(component.flowObjectiveService, times(1))
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700394 .filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
395 }
396
397 @Test
398 public void testHandleBasicPortFlowsRemovedSub() throws Exception {
399 // create empty service for testing
400 List<UniTagInformation> uniTagInformationList = new LinkedList<>();
401 UniTagInformation empty = new UniTagInformation.Builder().build();
402 uniTagInformationList.add(empty);
403 SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
404 si.setUniTagList(uniTagInformationList);
405 final DiscoveredSubscriber removedSub =
406 new DiscoveredSubscriber(testDevice,
407 uniUpdateEnabled, DiscoveredSubscriber.Status.REMOVED,
408 false, si);
409 // we are testing that when a port goes down we remove the default EAPOL flow
410
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800411 doReturn(MeterId.meterId(1)).when(component.oltMeterService)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700412 .getMeterIdForBandwidthProfile(deviceId, DEFAULT_BP_ID_DEFAULT);
413
414 FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
415 .deny()
416 .withKey(Criteria.matchInPort(uniUpdateEnabled.number()))
417 .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()))
418 .fromApp(testAppId)
419 .withPriority(10000)
420 .withMeta(
421 DefaultTrafficTreatment.builder()
422 .meter(MeterId.meterId(1))
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800423 .writeMetadata(component.createTechProfValueForWriteMetadata(
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700424 VlanId.vlanId(eapolDefaultVlan),
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800425 component.defaultTechProfileId, MeterId.meterId(1)), 0)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700426 .setOutput(PortNumber.CONTROLLER)
427 .pushVlan()
428 .setVlanId(VlanId.vlanId(eapolDefaultVlan)).build()
429 )
430 .add();
431
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800432 component.handleBasicPortFlows(removedSub, DEFAULT_BP_ID_DEFAULT, DEFAULT_BP_ID_DEFAULT);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700433
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800434 verify(component.flowObjectiveService, times(1))
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700435 .filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
436 }
437
438 @Test
439 public void testHandleNniFlowsOnlyLldp() {
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800440 component.enableDhcpOnNni = false;
441 component.handleNniFlows(testDevice, nniPort, OltFlowService.FlowOperation.ADD);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700442
443 FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
444 .permit()
445 .withKey(Criteria.matchInPort(nniPort.number()))
446 .addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
447 .fromApp(testAppId)
448 .withPriority(10000)
449 .withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
450 .add();
451
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800452 verify(component.flowObjectiveService, times(1))
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700453 .filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800454 verify(component.flowObjectiveService, times(1))
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700455 .filter(eq(deviceId), any());
456 }
457
458 @Test
459 public void testHandleNniFlowsDhcpV4() {
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800460 component.enableDhcpOnNni = true;
461 component.enableDhcpV4 = true;
462 component.handleNniFlows(testDevice, nniPort, OltFlowService.FlowOperation.ADD);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700463
464 FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
465 .permit()
466 .withKey(Criteria.matchInPort(nniPort.number()))
467 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
468 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_UDP))
469 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(67)))
470 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(68)))
471 .fromApp(testAppId)
472 .withPriority(10000)
473 .withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
474 .add();
475
476 // invoked with the correct DHCP filtering objective
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800477 verify(component.flowObjectiveService, times(1))
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700478 .filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
479 // invoked only twice, LLDP and DHCP
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800480 verify(component.flowObjectiveService, times(2))
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700481 .filter(eq(deviceId), any());
482 }
483
484 @Test
485 public void testRemoveNniFlowsDhcpV4() {
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800486 component.enableDhcpOnNni = true;
487 component.enableDhcpV4 = true;
488 component.handleNniFlows(testDevice, nniPortDisabled, OltFlowService.FlowOperation.REMOVE);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700489
490 FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
491 .deny()
492 .withKey(Criteria.matchInPort(nniPort.number()))
493 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
494 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_UDP))
495 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(67)))
496 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(68)))
497 .fromApp(testAppId)
498 .withPriority(10000)
499 .withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
500 .add();
501
502 // invoked with the correct DHCP filtering objective
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800503 verify(component.flowObjectiveService, times(1))
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700504 .filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
505 // invoked only twice, LLDP and DHCP
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800506 verify(component.flowObjectiveService, times(2))
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700507 .filter(eq(deviceId), any());
508 }
509
510 @Test
511 public void testHandleNniFlowsDhcpV6() {
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800512 component.enableDhcpOnNni = true;
513 component.enableDhcpV4 = false;
514 component.enableDhcpV6 = true;
515 component.handleNniFlows(testDevice, nniPort, OltFlowService.FlowOperation.ADD);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700516
517 FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
518 .permit()
519 .withKey(Criteria.matchInPort(nniPort.number()))
520 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV6.ethType()))
521 .addCondition(Criteria.matchIPProtocol(IPv6.PROTOCOL_UDP))
522 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(546)))
523 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(547)))
524 .fromApp(testAppId)
525 .withPriority(10000)
526 .withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
527 .add();
528
529 // invoked with the correct DHCP filtering objective
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800530 verify(component.flowObjectiveService, times(1))
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700531 .filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
532 // invoked only twice, LLDP and DHCP
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800533 verify(component.flowObjectiveService, times(2))
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700534 .filter(eq(deviceId), any());
535 }
536
537 @Test
538 public void testHandleNniFlowsIgmp() {
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800539 component.enableDhcpOnNni = false;
540 component.enableIgmpOnNni = true;
541 component.handleNniFlows(testDevice, nniPort, OltFlowService.FlowOperation.ADD);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700542
543 FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
544 .permit()
545 .withKey(Criteria.matchInPort(nniPort.number()))
546 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
547 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
548 .fromApp(testAppId)
549 .withPriority(10000)
550 .add();
551
552 // invoked with the correct DHCP filtering objective
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800553 verify(component.flowObjectiveService, times(1))
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700554 .filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
555 // invoked only twice, LLDP and DHCP
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800556 verify(component.flowObjectiveService, times(2))
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700557 .filter(eq(deviceId), any());
558 }
559
560 @Test
561 public void testMacAddressNotRequired() {
562 // create a single service that doesn't require mac address
563 List<UniTagInformation> uniTagInformationList = new LinkedList<>();
564 UniTagInformation hsia = new UniTagInformation.Builder()
565 .setEnableMacLearning(false)
566 .build();
567 uniTagInformationList.add(hsia);
568 SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
569 si.setUniTagList(uniTagInformationList);
570
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800571 boolean isMacAvailable = component.isMacAddressAvailable(testDevice.id(), uniUpdateEnabled, si);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700572 // we return true as we don't care wether it's available or not
573 Assert.assertTrue(isMacAvailable);
574 }
575
576 @Test
577 public void testIsMacAddressAvailableViaMacLearning() {
578
579 // create a single service that requires macLearning to be enabled
580 List<UniTagInformation> uniTagInformationList = new LinkedList<>();
581 VlanId hsiaCtag = VlanId.vlanId((short) 11);
582 UniTagInformation hsia = new UniTagInformation.Builder()
583 .setPonCTag(hsiaCtag)
584 .setEnableMacLearning(true).build();
585 uniTagInformationList.add(hsia);
586
587 SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
588 si.setUniTagList(uniTagInformationList);
589
590 // with no hosts discovered, return false
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800591 boolean isMacAvailable = component.isMacAddressAvailable(testDevice.id(), uniUpdateEnabled, si);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700592 Assert.assertFalse(isMacAvailable);
593
594 // with a discovered host, return true
595 Host fakeHost = new DefaultHost(ProviderId.NONE, HostId.hostId(MacAddress.NONE), MacAddress.ZERO,
596 hsiaCtag, HostLocation.NONE, new HashSet<>(), DefaultAnnotations.builder().build());
597 Set<Host> hosts = new HashSet<>(Arrays.asList(fakeHost));
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800598 doReturn(hosts).when(component.hostService).getConnectedHosts((ConnectPoint) any());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700599
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800600 isMacAvailable = component.isMacAddressAvailable(testDevice.id(), uniUpdateEnabled, si);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700601 Assert.assertTrue(isMacAvailable);
602 }
603
604 @Test
605 public void testIsMacAddressAvailableViaConfiguration() {
606 // create a single service that has a macAddress configured
607 List<UniTagInformation> uniTagInformationList = new LinkedList<>();
608 UniTagInformation hsia = new UniTagInformation.Builder()
609 .setConfiguredMacAddress("2e:0a:00:01:00:00")
610 .build();
611 uniTagInformationList.add(hsia);
612 SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
613 si.setUniTagList(uniTagInformationList);
614
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800615 boolean isMacAvailable = component.isMacAddressAvailable(testDevice.id(), uniUpdateEnabled, si);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700616 Assert.assertTrue(isMacAvailable);
617 }
618
619 @Test
620 public void testHandleSubscriberDhcpFlowsAdd() {
621
622 String usBp = "usBp";
623 String usOltBp = "usOltBp";
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800624 component.enableDhcpV4 = true;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700625
626 // create two services, one requires DHCP the other doesn't
627 List<UniTagInformation> uniTagInformationList = new LinkedList<>();
628 VlanId hsiaCtag = VlanId.vlanId((short) 11);
629 UniTagInformation hsia = new UniTagInformation.Builder()
630 .setPonCTag(hsiaCtag)
631 .setTechnologyProfileId(64)
632 .setUniTagMatch(VlanId.vlanId(VlanId.NO_VID))
633 .setUpstreamBandwidthProfile(usBp)
634 .setUpstreamOltBandwidthProfile(usOltBp)
635 .setIsDhcpRequired(true).build();
636 UniTagInformation mc = new UniTagInformation.Builder()
637 .setIsDhcpRequired(false).build();
638 uniTagInformationList.add(hsia);
639 uniTagInformationList.add(mc);
640
641 SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
642 si.setUniTagList(uniTagInformationList);
643
644 final DiscoveredSubscriber addedSub =
645 new DiscoveredSubscriber(testDevice,
646 uniUpdateEnabled, DiscoveredSubscriber.Status.ADDED,
647 false, si);
648
649 // return meter IDs
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800650 doReturn(MeterId.meterId(2)).when(component.oltMeterService)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700651 .getMeterIdForBandwidthProfile(addedSub.device.id(), usBp);
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800652 doReturn(MeterId.meterId(3)).when(component.oltMeterService)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700653 .getMeterIdForBandwidthProfile(addedSub.device.id(), usOltBp);
654
655 // TODO improve the matches on the filter
656 FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
657 .permit()
658 .withKey(Criteria.matchInPort(addedSub.port.number()))
659 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
660 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_UDP))
661 .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(68)))
662 .addCondition(Criteria.matchUdpDst(TpPort.tpPort(67)))
663 .fromApp(testAppId)
664 .withPriority(10000)
665 .add();
666
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800667 component.handleSubscriberDhcpFlows(addedSub.device.id(), addedSub.port,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700668 OltFlowService.FlowOperation.ADD, si);
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800669 verify(component.flowObjectiveService, times(1))
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700670 .filter(eq(addedSub.device.id()), argThat(new FilteringObjectiveMatcher(expectedFilter)));
671 }
672
673 @Test
674 public void testInternalFlowListenerNotMaster() {
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800675 doReturn(false).when(component.oltDeviceService).isLocalLeader(any());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700676
677 FlowRule flowRule = DefaultFlowRule.builder()
678 .forDevice(DeviceId.deviceId("foo"))
679 .fromApp(testAppId)
680 .makePermanent()
681 .withPriority(1000)
682 .build();
683 FlowRuleEvent event = new FlowRuleEvent(FlowRuleEvent.Type.RULE_ADDED,
684 flowRule);
685
686 internalFlowListener.event(event);
687
688 // if we're not master of the device, we should not update
689 verify(internalFlowListener, never()).updateCpStatus(any(), any(), any());
690 }
691
692 @Test
693 public void testInternalFlowListenerDifferentApp() {
694 ApplicationId someAppId = new DefaultApplicationId(1, "org.opencord.olt.not-test");
695 FlowRule flowRule = DefaultFlowRule.builder()
696 .forDevice(DeviceId.deviceId("foo"))
697 .fromApp(someAppId)
698 .makePermanent()
699 .withPriority(1000)
700 .build();
701 FlowRuleEvent event = new FlowRuleEvent(FlowRuleEvent.Type.RULE_ADDED,
702 flowRule);
703
704 internalFlowListener.event(event);
705
706 // if we're not master of the device, we should not update
707 verify(internalFlowListener, never()).updateCpStatus(any(), any(), any());
708 }
Matteo Scandolo97449bb2021-12-09 15:33:46 -0800709
710 @Test
711 public void testRemoveSubscriberFlows() {
712 // test that if we have EAPOL we wait till the tagged flow is removed
713 // before installing the default one
714
715 // setup
716 component.enableEapol = true;
717
718 // mock data
719 DeviceId deviceId = DeviceId.deviceId("test-device");
720 ProviderId pid = new ProviderId("of", "foo");
721 Device device =
722 new DefaultDevice(pid, deviceId, Device.Type.OLT, "", "", "", "", null);
723 Port port = new DefaultPort(device, PortNumber.portNumber(1), true,
724 DefaultAnnotations.builder().set(PORT_NAME, "port-1").build());
725
726 List<UniTagInformation> uniTagInformationList = new LinkedList<>();
727 UniTagInformation hsia = new UniTagInformation.Builder()
728 .setUpstreamBandwidthProfile("usbp")
729 .setDownstreamBandwidthProfile("dsbp")
730 .setPonCTag(VlanId.vlanId((short) 900)).build();
731 uniTagInformationList.add(hsia);
732 SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
733 si.setUniTagList(uniTagInformationList);
734
735 DiscoveredSubscriber sub = new DiscoveredSubscriber(
736 device, port, DiscoveredSubscriber.Status.REMOVED, true, si);
737
738 // first test that when we remove the EAPOL flow we return false so that the
739 // subscriber is not removed from the queue
740 doReturn(true).when(oltFlowService).areSubscriberFlowsPendingRemoval(any(), any());
741 boolean res = oltFlowService.removeSubscriberFlows(sub, DEFAULT_BP_ID_DEFAULT, DEFAULT_MCAST_SERVICE_NAME);
742 verify(oltFlowService, times(1))
743 .handleSubscriberDhcpFlows(deviceId, port, OltFlowService.FlowOperation.REMOVE, si);
744 verify(oltFlowService, times(1))
745 .handleSubscriberEapolFlows(sub, OltFlowService.FlowOperation.REMOVE, si);
746 verify(oltFlowService, times(1))
747 .handleSubscriberDataFlows(device, port, OltFlowService.FlowOperation.REMOVE,
748 si, DEFAULT_MCAST_SERVICE_NAME);
749 verify(oltFlowService, times(1))
750 .handleSubscriberIgmpFlows(sub, OltFlowService.FlowOperation.REMOVE);
751 verify(oltFlowService, never())
752 .handleEapolFlow(any(), any(), any(),
753 eq(OltFlowService.FlowOperation.ADD), eq(VlanId.vlanId(OltFlowService.EAPOL_DEFAULT_VLAN)));
754 Assert.assertFalse(res);
755
756 // then test that if the tagged EAPOL is not there we install the default EAPOL
757 // and return true so we remove the subscriber from the queue
758 doReturn(false).when(oltFlowService).areSubscriberFlowsPendingRemoval(any(), any());
759 doReturn(port).when(oltFlowService.deviceService).getPort(deviceId, port.number());
760 res = oltFlowService.removeSubscriberFlows(sub, DEFAULT_BP_ID_DEFAULT, DEFAULT_MCAST_SERVICE_NAME);
761 verify(oltFlowService, times(1))
762 .handleEapolFlow(any(), any(), any(),
763 eq(OltFlowService.FlowOperation.ADD), eq(VlanId.vlanId(OltFlowService.EAPOL_DEFAULT_VLAN)));
764 Assert.assertTrue(res);
765 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700766}