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