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