blob: 86bfcd863b233a4b6743e182ceace48a4008e8b7 [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.Before;
20import org.junit.Test;
21import org.mockito.Mockito;
22import org.mockito.invocation.InvocationOnMock;
23import org.mockito.stubbing.Answer;
24import org.onlab.packet.ChassisId;
25import org.onosproject.cluster.ClusterService;
26import org.onosproject.cluster.LeadershipService;
27import org.onosproject.mastership.MastershipService;
28import org.onosproject.net.AnnotationKeys;
29import org.onosproject.net.ConnectPoint;
30import org.onosproject.net.DefaultAnnotations;
31import org.onosproject.net.DefaultDevice;
32import org.onosproject.net.Device;
33import org.onosproject.net.DeviceId;
34import org.onosproject.net.Port;
35import org.onosproject.net.PortNumber;
36import org.onosproject.net.device.DeviceEvent;
37import org.onosproject.net.device.DeviceService;
38import org.onosproject.net.provider.ProviderId;
Gustavo Silva29fb20e2022-05-26 09:59:54 -030039import org.opencord.olt.DiscoveredSubscriber;
40import org.opencord.olt.FlowOperation;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070041import org.opencord.sadis.BaseInformationService;
42import org.opencord.sadis.SubscriberAndDeviceInformation;
43import org.opencord.sadis.UniTagInformation;
44
45import java.util.HashMap;
46import java.util.LinkedList;
47import java.util.List;
48import java.util.concurrent.ExecutorService;
49import java.util.concurrent.LinkedBlockingQueue;
50
51import static org.mockito.Matchers.any;
52import static org.mockito.Matchers.eq;
53import static org.mockito.Mockito.doAnswer;
54import static org.mockito.Mockito.doReturn;
Matteo Scandolob6981dc2021-12-02 16:31:44 -080055import static org.mockito.Mockito.never;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070056import static org.mockito.Mockito.times;
57import static org.mockito.Mockito.verify;
58
59public class OltDeviceListenerTest extends OltTestHelpers {
60 private Olt olt;
61 private Olt.OltDeviceListener oltDeviceListener;
62
63 private final DeviceId deviceId = DeviceId.deviceId("test-device");
64 private final Device testDevice = new DefaultDevice(ProviderId.NONE, deviceId, Device.Type.OLT,
65 "testManufacturer", "1.0", "1.0", "SN", new ChassisId(1));
66
67 @Before
68 public void setUp() {
69 olt = new Olt();
70 olt.eventsQueues = new HashMap<>();
71 olt.mastershipService = Mockito.mock(MastershipService.class);
72 olt.oltDeviceService = Mockito.mock(OltDeviceService.class);
73 olt.oltFlowService = Mockito.mock(OltFlowService.class);
74 olt.oltMeterService = Mockito.mock(OltMeterService.class);
75 olt.deviceService = Mockito.mock(DeviceService.class);
76 olt.leadershipService = Mockito.mock(LeadershipService.class);
77 olt.clusterService = Mockito.mock(ClusterService.class);
78 olt.subsService = Mockito.mock(BaseInformationService.class);
79
80 Olt.OltDeviceListener baseClass = olt.deviceListener;
81 baseClass.eventExecutor = Mockito.mock(ExecutorService.class);
82 oltDeviceListener = Mockito.spy(baseClass);
83
84 // mock the executor so it immediately invokes the method
85 doAnswer(new Answer<Object>() {
86 public Object answer(InvocationOnMock invocation) throws Exception {
87 ((Runnable) invocation.getArguments()[0]).run();
88 return null;
89 }
90 }).when(baseClass.eventExecutor).execute(any(Runnable.class));
91
92 olt.eventsQueues.forEach((cp, q) -> q.clear());
93 }
94
95 @Test
96 public void testDeviceDisconnection() {
97 doReturn(true).when(olt.oltDeviceService).isOlt(testDevice);
98 doReturn(false).when(olt.deviceService).isAvailable(any());
99 doReturn(new LinkedList<Port>()).when(olt.deviceService).getPorts(any());
100 doReturn(true).when(olt.oltDeviceService).isLocalLeader(any());
101
102 DeviceEvent disconnect = new DeviceEvent(DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED, testDevice, null);
103 oltDeviceListener.event(disconnect);
104
105 verify(olt.oltFlowService, times(1)).purgeDeviceFlows(testDevice.id());
106 verify(olt.oltMeterService, times(1)).purgeDeviceMeters(testDevice.id());
107 }
108
109 @Test
110 public void testPortEventOwnership() {
111 // make sure that we ignore events for devices that are not local to this node
112
113 // make sure the device is recognized as an OLT and the port is not an NNI
114 doReturn(true).when(olt.oltDeviceService).isOlt(testDevice);
115 doReturn(false).when(olt.oltDeviceService).isNniPort(eq(testDevice), any());
116
117 // make sure we're not leaders of the device
118 doReturn(false).when(olt.oltDeviceService).isLocalLeader(any());
119
120 // this is a new port, should not create an entry in the queue
121 // we're not owners of the device
122 Port uniUpdateEnabled = new OltPort(testDevice, true, PortNumber.portNumber(16),
123 DefaultAnnotations.builder().set(AnnotationKeys.PORT_NAME, "uni-1").build());
124 DeviceEvent uniUpdateEnabledEvent =
125 new DeviceEvent(DeviceEvent.Type.PORT_UPDATED, testDevice, uniUpdateEnabled);
126 oltDeviceListener.event(uniUpdateEnabledEvent);
127
128 // the queue won't even be created
129 assert olt.eventsQueues.isEmpty();
130 }
131
132 @Test
133 public void testNniEvent() throws InterruptedException {
134 // make sure the device is recognized as an OLT and the port is recognized as an NNI,
135 // and we're local leaders
136 doReturn(true).when(olt.oltDeviceService).isOlt(testDevice);
137 doReturn(true).when(olt.oltDeviceService).isNniPort(eq(testDevice), any());
138 doReturn(true).when(olt.oltDeviceService).isLocalLeader(any());
139
140 Port enabledNniPort = new OltPort(testDevice, true, PortNumber.portNumber(1048576),
141 DefaultAnnotations.builder().set(AnnotationKeys.PORT_NAME, "nni-1").build());
142 DeviceEvent nniEnabledEvent = new DeviceEvent(DeviceEvent.Type.PORT_ADDED, testDevice, enabledNniPort);
143 oltDeviceListener.event(nniEnabledEvent);
144
145 // NNI events are straight forward, we can provision the flows directly
146 assert olt.eventsQueues.isEmpty();
147 verify(olt.oltFlowService, times(1))
Gustavo Silva29fb20e2022-05-26 09:59:54 -0300148 .handleNniFlows(testDevice, enabledNniPort, FlowOperation.ADD);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700149
150 Port disabledNniPort = new OltPort(testDevice, false, PortNumber.portNumber(1048576),
151 DefaultAnnotations.builder().set(AnnotationKeys.PORT_NAME, "nni-1").build());
152 DeviceEvent nniDisabledEvent = new DeviceEvent(DeviceEvent.Type.PORT_UPDATED, testDevice, disabledNniPort);
153 oltDeviceListener.event(nniDisabledEvent);
154
Matteo Scandolob6981dc2021-12-02 16:31:44 -0800155 // when the NNI goes down we ignore the event
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700156 assert olt.eventsQueues.isEmpty();
Matteo Scandolob6981dc2021-12-02 16:31:44 -0800157 verify(olt.oltFlowService, never())
Gustavo Silva29fb20e2022-05-26 09:59:54 -0300158 .handleNniFlows(testDevice, disabledNniPort, FlowOperation.REMOVE);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700159
Matteo Scandolo1f8de332021-12-06 12:18:24 -0800160 // if the NNI is removed we ignore the event
161 Port removedNniPort = new OltPort(testDevice, true, PortNumber.portNumber(1048576),
162 DefaultAnnotations.builder().set(AnnotationKeys.PORT_NAME, "nni-1").build());
163 DeviceEvent nniRemovedEvent = new DeviceEvent(DeviceEvent.Type.PORT_UPDATED, testDevice, removedNniPort);
164 oltDeviceListener.event(nniRemovedEvent);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700165
166 assert olt.eventsQueues.isEmpty();
Matteo Scandolo1f8de332021-12-06 12:18:24 -0800167 verify(olt.oltFlowService, never())
Gustavo Silva29fb20e2022-05-26 09:59:54 -0300168 .handleNniFlows(testDevice, removedNniPort, FlowOperation.REMOVE);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700169 }
170
171 @Test
172 public void testUniEvents() {
173 DiscoveredSubscriber sub;
174 // there are few cases we need to test in the UNI port case:
175 // - [X] UNI port added in disabled state
176 // - [X] UNI port added in disabled state (with default EAPOL installed)
177 // - UNI port added in enabled state
178 // - [X] UNI port updated to enabled state
179 // - UNI port updated to disabled state
180 // - UNI port removed (assumes it's disabled state)
181
182 // make sure the device is recognized as an OLT, the port is not an NNI,
183 // and we're local masters
184 doReturn(true).when(olt.oltDeviceService).isOlt(testDevice);
185 doReturn(false).when(olt.oltDeviceService).isNniPort(eq(testDevice), any());
186 doReturn(true).when(olt.oltDeviceService).isLocalLeader(any());
187
188 PortNumber uniPortNumber = PortNumber.portNumber(16);
189 Port uniAddedDisabled = new OltPort(testDevice, false, uniPortNumber,
190 DefaultAnnotations.builder().set(AnnotationKeys.PORT_NAME, "uni-1").build());
Andrea Campanella61650a12022-01-24 18:09:44 -0800191 DeviceEvent uniAddedDisabledEvent =
192 new DeviceEvent(DeviceEvent.Type.PORT_UPDATED, testDevice, uniAddedDisabled);
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700193 ConnectPoint cp = new ConnectPoint(testDevice.id(), uniPortNumber);
194
195 // if the port does not have default EAPOL we should not generate an event
196 oltDeviceListener.event(uniAddedDisabledEvent);
197
198 // no event == no queue is created
199 assert olt.eventsQueues.isEmpty();
200
201 // if the port has default EAPOL then create an entry in the queue to remove it
202 doReturn(true).when(olt.oltFlowService)
203 .hasDefaultEapol(uniAddedDisabled);
204 // create empty service for testing
205 List<UniTagInformation> uniTagInformationList = new LinkedList<>();
206 UniTagInformation empty = new UniTagInformation.Builder().build();
207 uniTagInformationList.add(empty);
208
209 SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
210 si.setUniTagList(uniTagInformationList);
211 doReturn(si).when(olt.subsService).get("uni-1");
212
Andrea Campanellacc6dc7e2022-03-22 10:38:43 +0100213 doReturn(true).when(olt.deviceService).isAvailable(any());
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700214 oltDeviceListener.event(uniAddedDisabledEvent);
215 LinkedBlockingQueue<DiscoveredSubscriber> q = olt.eventsQueues.get(cp);
216 assert !q.isEmpty();
217 sub = q.poll();
218 assert !sub.hasSubscriber; // this is not a provision subscriber call
219 assert sub.device.equals(testDevice);
220 assert sub.port.equals(uniAddedDisabled);
221 assert sub.status.equals(DiscoveredSubscriber.Status.REMOVED); // we need to remove flows for this port (if any)
222 assert q.isEmpty(); // the queue is now empty
223
224 Port uniUpdateEnabled = new OltPort(testDevice, true, PortNumber.portNumber(16),
225 DefaultAnnotations.builder().set(AnnotationKeys.PORT_NAME, "uni-1").build());
226 DeviceEvent uniUpdateEnabledEvent =
227 new DeviceEvent(DeviceEvent.Type.PORT_UPDATED, testDevice, uniUpdateEnabled);
228 oltDeviceListener.event(uniUpdateEnabledEvent);
229
230 assert !q.isEmpty();
231 sub = q.poll();
232 assert !sub.hasSubscriber; // this is not a provision subscriber call
233 assert sub.device.equals(testDevice);
234 assert sub.port.equals(uniUpdateEnabled);
235 assert sub.status.equals(DiscoveredSubscriber.Status.ADDED); // we need to remove flows for this port (if any)
236 assert q.isEmpty(); // the queue is now empty
237 }
238}