blob: 0d5204fff737ebb7a9f2735220a37736757c2b0f [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
158 // when we disable the device we receive a PORT_REMOVED event with status ENABLED
159 // make sure we're removing the flows correctly
160 DeviceEvent nniRemoveEvent = new DeviceEvent(DeviceEvent.Type.PORT_REMOVED, testDevice, enabledNniPort);
161 oltDeviceListener.event(nniRemoveEvent);
162
163 assert olt.eventsQueues.isEmpty();
164 verify(olt.oltFlowService, times(1))
165 .handleNniFlows(testDevice, enabledNniPort, OltFlowService.FlowOperation.REMOVE);
166 }
167
168 @Test
169 public void testUniEvents() {
170 DiscoveredSubscriber sub;
171 // there are few cases we need to test in the UNI port case:
172 // - [X] UNI port added in disabled state
173 // - [X] UNI port added in disabled state (with default EAPOL installed)
174 // - UNI port added in enabled state
175 // - [X] UNI port updated to enabled state
176 // - UNI port updated to disabled state
177 // - UNI port removed (assumes it's disabled state)
178
179 // make sure the device is recognized as an OLT, the port is not an NNI,
180 // and we're local masters
181 doReturn(true).when(olt.oltDeviceService).isOlt(testDevice);
182 doReturn(false).when(olt.oltDeviceService).isNniPort(eq(testDevice), any());
183 doReturn(true).when(olt.oltDeviceService).isLocalLeader(any());
184
185 PortNumber uniPortNumber = PortNumber.portNumber(16);
186 Port uniAddedDisabled = new OltPort(testDevice, false, uniPortNumber,
187 DefaultAnnotations.builder().set(AnnotationKeys.PORT_NAME, "uni-1").build());
188 DeviceEvent uniAddedDisabledEvent = new DeviceEvent(DeviceEvent.Type.PORT_ADDED, testDevice, uniAddedDisabled);
189 ConnectPoint cp = new ConnectPoint(testDevice.id(), uniPortNumber);
190
191 // if the port does not have default EAPOL we should not generate an event
192 oltDeviceListener.event(uniAddedDisabledEvent);
193
194 // no event == no queue is created
195 assert olt.eventsQueues.isEmpty();
196
197 // if the port has default EAPOL then create an entry in the queue to remove it
198 doReturn(true).when(olt.oltFlowService)
199 .hasDefaultEapol(uniAddedDisabled);
200 // create empty service for testing
201 List<UniTagInformation> uniTagInformationList = new LinkedList<>();
202 UniTagInformation empty = new UniTagInformation.Builder().build();
203 uniTagInformationList.add(empty);
204
205 SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
206 si.setUniTagList(uniTagInformationList);
207 doReturn(si).when(olt.subsService).get("uni-1");
208
209 oltDeviceListener.event(uniAddedDisabledEvent);
210 LinkedBlockingQueue<DiscoveredSubscriber> q = olt.eventsQueues.get(cp);
211 assert !q.isEmpty();
212 sub = q.poll();
213 assert !sub.hasSubscriber; // this is not a provision subscriber call
214 assert sub.device.equals(testDevice);
215 assert sub.port.equals(uniAddedDisabled);
216 assert sub.status.equals(DiscoveredSubscriber.Status.REMOVED); // we need to remove flows for this port (if any)
217 assert q.isEmpty(); // the queue is now empty
218
219 Port uniUpdateEnabled = new OltPort(testDevice, true, PortNumber.portNumber(16),
220 DefaultAnnotations.builder().set(AnnotationKeys.PORT_NAME, "uni-1").build());
221 DeviceEvent uniUpdateEnabledEvent =
222 new DeviceEvent(DeviceEvent.Type.PORT_UPDATED, testDevice, uniUpdateEnabled);
223 oltDeviceListener.event(uniUpdateEnabledEvent);
224
225 assert !q.isEmpty();
226 sub = q.poll();
227 assert !sub.hasSubscriber; // this is not a provision subscriber call
228 assert sub.device.equals(testDevice);
229 assert sub.port.equals(uniUpdateEnabled);
230 assert sub.status.equals(DiscoveredSubscriber.Status.ADDED); // we need to remove flows for this port (if any)
231 assert q.isEmpty(); // the queue is now empty
232 }
233}