blob: 6af50f51fa3ce7ad60a8bdda605a9774b76fa597 [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());
189 DeviceEvent uniAddedDisabledEvent = new DeviceEvent(DeviceEvent.Type.PORT_ADDED, testDevice, uniAddedDisabled);
190 ConnectPoint cp = new ConnectPoint(testDevice.id(), uniPortNumber);
191
192 // if the port does not have default EAPOL we should not generate an event
193 oltDeviceListener.event(uniAddedDisabledEvent);
194
195 // no event == no queue is created
196 assert olt.eventsQueues.isEmpty();
197
198 // if the port has default EAPOL then create an entry in the queue to remove it
199 doReturn(true).when(olt.oltFlowService)
200 .hasDefaultEapol(uniAddedDisabled);
201 // create empty service for testing
202 List<UniTagInformation> uniTagInformationList = new LinkedList<>();
203 UniTagInformation empty = new UniTagInformation.Builder().build();
204 uniTagInformationList.add(empty);
205
206 SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
207 si.setUniTagList(uniTagInformationList);
208 doReturn(si).when(olt.subsService).get("uni-1");
209
210 oltDeviceListener.event(uniAddedDisabledEvent);
211 LinkedBlockingQueue<DiscoveredSubscriber> q = olt.eventsQueues.get(cp);
212 assert !q.isEmpty();
213 sub = q.poll();
214 assert !sub.hasSubscriber; // this is not a provision subscriber call
215 assert sub.device.equals(testDevice);
216 assert sub.port.equals(uniAddedDisabled);
217 assert sub.status.equals(DiscoveredSubscriber.Status.REMOVED); // we need to remove flows for this port (if any)
218 assert q.isEmpty(); // the queue is now empty
219
220 Port uniUpdateEnabled = new OltPort(testDevice, true, PortNumber.portNumber(16),
221 DefaultAnnotations.builder().set(AnnotationKeys.PORT_NAME, "uni-1").build());
222 DeviceEvent uniUpdateEnabledEvent =
223 new DeviceEvent(DeviceEvent.Type.PORT_UPDATED, testDevice, uniUpdateEnabled);
224 oltDeviceListener.event(uniUpdateEnabledEvent);
225
226 assert !q.isEmpty();
227 sub = q.poll();
228 assert !sub.hasSubscriber; // this is not a provision subscriber call
229 assert sub.device.equals(testDevice);
230 assert sub.port.equals(uniUpdateEnabled);
231 assert sub.status.equals(DiscoveredSubscriber.Status.ADDED); // we need to remove flows for this port (if any)
232 assert q.isEmpty(); // the queue is now empty
233 }
234}