2 * Copyright © 2021 Orange. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.transportpce.servicehandler.listeners;
10 import static org.junit.jupiter.api.Assertions.fail;
11 import static org.mockito.ArgumentMatchers.any;
12 import static org.mockito.ArgumentMatchers.anyString;
13 import static org.mockito.Mockito.mock;
14 import static org.mockito.Mockito.never;
15 import static org.mockito.Mockito.times;
16 import static org.mockito.Mockito.verify;
17 import static org.mockito.Mockito.when;
19 import java.util.ArrayList;
20 import java.util.List;
22 import java.util.Optional;
23 import org.junit.jupiter.api.Test;
24 import org.junit.jupiter.api.extension.ExtendWith;
25 import org.mockito.Mock;
26 import org.mockito.junit.jupiter.MockitoExtension;
27 import org.opendaylight.mdsal.binding.api.DataObjectModification;
28 import org.opendaylight.mdsal.binding.api.DataTreeModification;
29 import org.opendaylight.mdsal.binding.api.NotificationPublishService;
30 import org.opendaylight.mdsal.binding.api.RpcService;
31 import org.opendaylight.transportpce.common.ResponseCodes;
32 import org.opendaylight.transportpce.pce.service.PathComputationService;
33 import org.opendaylight.transportpce.servicehandler.service.ServiceDataStoreOperations;
34 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.node.types.rev210528.NodeIdType;
35 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.ConnectionType;
36 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.Restorable;
37 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.configuration.response.common.ConfigurationResponseCommonBuilder;
38 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.sdnc.request.header.SdncRequestHeaderBuilder;
39 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.service.ServiceAEndBuilder;
40 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.service.ServiceZEndBuilder;
41 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.service.endpoint.RxDirection;
42 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.service.endpoint.RxDirectionBuilder;
43 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.service.endpoint.RxDirectionKey;
44 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.service.endpoint.TxDirection;
45 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.service.endpoint.TxDirectionBuilder;
46 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.service.endpoint.TxDirectionKey;
47 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.service.lgx.LgxBuilder;
48 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.service.port.PortBuilder;
49 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.service.resiliency.ServiceResiliency;
50 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.service.resiliency.ServiceResiliencyBuilder;
51 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.state.types.rev191129.State;
52 import org.opendaylight.yang.gen.v1.http.org.openroadm.equipment.states.types.rev191129.AdminStates;
53 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.format.rev191129.ServiceFormat;
54 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.ServiceCreate;
55 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.ServiceCreateOutputBuilder;
56 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.ServiceDelete;
57 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.ServiceDeleteOutputBuilder;
58 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.ServiceReroute;
59 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.ServiceRerouteOutputBuilder;
60 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.service.list.Services;
61 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.service.list.ServicesBuilder;
62 import org.opendaylight.yang.gen.v1.nbi.notifications.rev230728.PublishNotificationAlarmService;
63 import org.opendaylight.yang.gen.v1.nbi.notifications.rev230728.PublishNotificationAlarmServiceBuilder;
64 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
65 import org.opendaylight.yangtools.yang.common.Uint32;
66 import org.opendaylight.yangtools.yang.common.Uint8;
68 @ExtendWith(MockitoExtension.class)
69 public class ServiceListenerTest {
72 private RpcService rpcService;
74 private ServiceDataStoreOperations serviceDataStoreOperations;
76 private NotificationPublishService notificationPublishService;
78 private PathComputationService pathComputationService;
80 private ServiceDelete serviceDelete;
82 private ServiceReroute serviceReroute;
84 private ServiceCreate serviceCreate;
87 void testOnDataTreeChangedWhenDeleteService() {
88 @SuppressWarnings("unchecked") final DataObjectModification<Services> service =
89 mock(DataObjectModification.class);
90 final List<DataTreeModification<Services>> changes = new ArrayList<>();
91 @SuppressWarnings("unchecked") final DataTreeModification<Services> ch = mock(DataTreeModification.class);
93 when(ch.getRootNode()).thenReturn(service);
95 when(service.modificationType()).thenReturn(DataObjectModification.ModificationType.DELETE);
96 when(service.dataBefore()).thenReturn(buildService(State.InService, AdminStates.InService));
97 ServiceListener listener = new ServiceListener(rpcService, serviceDataStoreOperations,
98 notificationPublishService);
99 listener.onDataTreeChanged(changes);
100 verify(ch, times(1)).getRootNode();
101 verify(service, times(1)).modificationType();
102 verify(service, times(2)).dataBefore();
103 verify(service, never()).dataAfter();
105 verify(notificationPublishService, never()).putNotification(any(PublishNotificationAlarmService.class));
106 } catch (InterruptedException e) {
107 fail("Failed publishing notification");
112 void testOnDataTreeChangedWhenServiceBecomesOutOfService() {
113 @SuppressWarnings("unchecked") final DataObjectModification<Services> service =
114 mock(DataObjectModification.class);
115 final List<DataTreeModification<Services>> changes = new ArrayList<>();
116 @SuppressWarnings("unchecked") final DataTreeModification<Services> ch = mock(DataTreeModification.class);
118 when(ch.getRootNode()).thenReturn(service);
120 Services serviceDown = buildService(State.OutOfService, AdminStates.InService);
121 when(service.modificationType()).thenReturn(DataObjectModification.ModificationType.WRITE);
122 when(service.dataBefore()).thenReturn(buildService(State.InService, AdminStates.InService));
123 when(service.dataAfter()).thenReturn(serviceDown);
124 ServiceListener listener = new ServiceListener(rpcService, serviceDataStoreOperations,
125 notificationPublishService);
126 listener.onDataTreeChanged(changes);
127 verify(ch, times(1)).getRootNode();
128 verify(service, times(1)).modificationType();
129 verify(service, times(3)).dataBefore();
130 verify(service, times(1)).dataAfter();
132 verify(notificationPublishService, times(1))
133 .putNotification(buildNotificationAlarmService(serviceDown, "The service is now outOfService"));
134 } catch (InterruptedException e) {
135 fail("Failed publishing notification");
140 void testOnDataTreeChangedWhenShouldNeverHappen() {
141 @SuppressWarnings("unchecked") final DataObjectModification<Services> service =
142 mock(DataObjectModification.class);
143 final List<DataTreeModification<Services>> changes = new ArrayList<>();
144 @SuppressWarnings("unchecked") final DataTreeModification<Services> ch = mock(DataTreeModification.class);
146 when(ch.getRootNode()).thenReturn(service);
148 when(service.modificationType()).thenReturn(DataObjectModification.ModificationType.SUBTREE_MODIFIED);
149 when(service.dataBefore()).thenReturn(buildService(State.InService, AdminStates.InService));
150 ServiceListener listener = new ServiceListener(rpcService, serviceDataStoreOperations,
151 notificationPublishService);
152 listener.onDataTreeChanged(changes);
153 verify(ch, times(1)).getRootNode();
154 verify(service, times(2)).modificationType();
155 verify(service, times(2)).dataBefore();
156 verify(service, never()).dataAfter();
158 verify(notificationPublishService, never()).putNotification(any(PublishNotificationAlarmService.class));
159 } catch (InterruptedException e) {
160 fail("Failed publishing notification");
165 void testOnDataTreeChangedWhenServiceDegradedShouldBeRerouted() {
166 @SuppressWarnings("unchecked") final DataObjectModification<Services> service =
167 mock(DataObjectModification.class);
168 final List<DataTreeModification<Services>> changes = new ArrayList<>();
169 @SuppressWarnings("unchecked") final DataTreeModification<Services> ch = mock(DataTreeModification.class);
171 when(ch.getRootNode()).thenReturn(service);
173 ServiceResiliency serviceResiliency = new ServiceResiliencyBuilder().setResiliency(Restorable.VALUE).build();
174 Services serviceAfter = new ServicesBuilder(buildService(State.OutOfService, AdminStates.InService))
175 .setServiceResiliency(serviceResiliency)
177 when(service.modificationType()).thenReturn(DataObjectModification.ModificationType.WRITE);
178 when(service.dataBefore())
179 .thenReturn(new ServicesBuilder(buildService(State.InService, AdminStates.InService))
180 .setServiceResiliency(serviceResiliency)
182 when(service.dataAfter()).thenReturn(serviceAfter);
183 when(serviceDataStoreOperations.getService(anyString())).thenReturn(Optional.of(serviceAfter));
184 when(rpcService.getRpc(ServiceDelete.class)).thenReturn(serviceDelete);
185 when(serviceDelete.invoke(any()))
186 .thenReturn(RpcResultBuilder.success(new ServiceDeleteOutputBuilder()
187 .setConfigurationResponseCommon(new ConfigurationResponseCommonBuilder()
188 .setResponseCode(ResponseCodes.RESPONSE_OK)
192 when(rpcService.getRpc(ServiceReroute.class)).thenReturn(serviceReroute);
193 when(serviceReroute.invoke(any()))
194 .thenReturn(RpcResultBuilder.success(new ServiceRerouteOutputBuilder()
195 .setConfigurationResponseCommon(new ConfigurationResponseCommonBuilder()
196 .setResponseCode(ResponseCodes.RESPONSE_OK)
200 when(rpcService.getRpc(ServiceCreate.class)).thenReturn(serviceCreate);
201 when(serviceCreate.invoke(any()))
202 .thenReturn(RpcResultBuilder.success(new ServiceCreateOutputBuilder()
203 .setConfigurationResponseCommon(new ConfigurationResponseCommonBuilder()
204 .setResponseCode(ResponseCodes.RESPONSE_OK)
208 ServiceListener listener = new ServiceListener(rpcService, serviceDataStoreOperations,
209 notificationPublishService);
210 listener.onDataTreeChanged(changes);
211 verify(ch, times(1)).getRootNode();
212 verify(service, times(1)).modificationType();
213 verify(service, times(3)).dataBefore();
214 verify(service, times(1)).dataAfter();
215 // verify(servicehandler, times(1)).serviceDelete(any());
217 when(service.modificationType()).thenReturn(DataObjectModification.ModificationType.DELETE);
218 listener.onDataTreeChanged(changes);
219 // verify(servicehandler, times(1)).serviceCreate(any());
223 void testOnDataTreeChangedWhenServiceDegradedShouldNotBeRerouted() {
224 @SuppressWarnings("unchecked") final DataObjectModification<Services> service =
225 mock(DataObjectModification.class);
226 final List<DataTreeModification<Services>> changes = new ArrayList<>();
227 @SuppressWarnings("unchecked") final DataTreeModification<Services> ch = mock(DataTreeModification.class);
229 when(ch.getRootNode()).thenReturn(service);
231 Services serviceAfter = buildService(State.OutOfService, AdminStates.InService);
232 when(service.modificationType()).thenReturn(DataObjectModification.ModificationType.WRITE);
233 when(service.dataBefore()).thenReturn(buildService(State.InService, AdminStates.InService));
234 when(service.dataAfter()).thenReturn(serviceAfter);
235 ServiceListener listener = new ServiceListener(rpcService, serviceDataStoreOperations,
236 notificationPublishService);
237 listener.onDataTreeChanged(changes);
238 verify(ch, times(1)).getRootNode();
239 verify(service, times(1)).modificationType();
240 verify(service, times(3)).dataBefore();
241 verify(service, times(1)).dataAfter();
242 // verify(servicehandler, times(0)).serviceDelete(any());
244 when(service.modificationType()).thenReturn(DataObjectModification.ModificationType.DELETE);
245 listener.onDataTreeChanged(changes);
246 // verify(servicehandler, times(0)).serviceCreate(any());
249 private Services buildService(State state, AdminStates adminStates) {
250 return new ServicesBuilder()
251 .setSdncRequestHeader(new SdncRequestHeaderBuilder().build())
252 .setCommonId("commonId")
253 .setConnectionType(ConnectionType.Service)
254 .setCustomer("Customer")
255 .setServiceName("service 1")
256 .setServiceAEnd(getServiceAEndBuild().build())
257 .setServiceZEnd(new ServiceZEndBuilder()
259 .setServiceFormat(ServiceFormat.Ethernet)
260 .setServiceRate(Uint32.valueOf(1))
261 .setNodeId(new NodeIdType("XPONDER-3-2"))
262 .setTxDirection(Map.of(new TxDirectionKey(getTxDirection().key()), getTxDirection()))
263 .setRxDirection(Map.of(new RxDirectionKey(getRxDirection().key()), getRxDirection()))
265 .setOperationalState(state)
266 .setAdministrativeState(adminStates)
270 private ServiceAEndBuilder getServiceAEndBuild() {
271 return new ServiceAEndBuilder()
273 .setServiceFormat(ServiceFormat.Ethernet)
274 .setServiceRate(Uint32.valueOf(1))
275 .setNodeId(new NodeIdType("XPONDER-1-2"))
276 .setTxDirection(Map.of(new TxDirectionKey(getTxDirection().key()), getTxDirection()))
277 .setRxDirection(Map.of(new RxDirectionKey(getRxDirection().key()), getRxDirection()));
280 private TxDirection getTxDirection() {
281 return new TxDirectionBuilder()
282 .setPort(new PortBuilder()
283 .setPortDeviceName("device name")
284 .setPortName("port name")
285 .setPortRack("port rack")
286 .setPortShelf("port shelf")
287 .setPortSlot("port slot")
288 .setPortSubSlot("port subslot")
289 .setPortType("port type")
291 .setLgx(new LgxBuilder()
292 .setLgxDeviceName("lgx device name")
293 .setLgxPortName("lgx port name")
294 .setLgxPortRack("lgx port rack")
295 .setLgxPortShelf("lgx port shelf")
297 .setIndex(Uint8.ZERO)
301 private RxDirection getRxDirection() {
302 return new RxDirectionBuilder()
303 .setPort(new PortBuilder()
304 .setPortDeviceName("device name")
305 .setPortName("port name")
306 .setPortRack("port rack")
307 .setPortShelf("port shelf")
308 .setPortSlot("port slot")
309 .setPortSubSlot("port subslot")
310 .setPortType("port type")
312 .setLgx(new LgxBuilder()
313 .setLgxDeviceName("lgx device name")
314 .setLgxPortName("lgx port name")
315 .setLgxPortRack("lgx port rack")
316 .setLgxPortShelf("lgx port shelf")
318 .setIndex(Uint8.ZERO)
322 private PublishNotificationAlarmService buildNotificationAlarmService(Services services, String message) {
323 return new PublishNotificationAlarmServiceBuilder()
324 .setServiceName("service 1")
325 .setConnectionType(ConnectionType.Service)
327 .setOperationalState(services.getOperationalState())
328 .setPublisherName("ServiceListener")