NBI notification support for service-result-rpc
[transportpce.git] / servicehandler / src / main / java / org / opendaylight / transportpce / servicehandler / listeners / NetworkModelNotificationHandler.java
1 /*
2  * Copyright © 2020 Nokia, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.transportpce.servicehandler.listeners;
9
10 import java.util.HashMap;
11 import java.util.Iterator;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.Optional;
15 import java.util.Set;
16 import java.util.stream.Collectors;
17 import org.eclipse.jdt.annotation.Nullable;
18 import org.opendaylight.mdsal.binding.api.NotificationPublishService;
19 import org.opendaylight.mdsal.binding.api.NotificationService.CompositeListener;
20 import org.opendaylight.transportpce.common.OperationResult;
21 import org.opendaylight.transportpce.servicehandler.service.ServiceDataStoreOperations;
22 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.networkmodel.rev201116.TopologyUpdateResult;
23 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.networkmodel.rev201116.TopologyUpdateResultBuilder;
24 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.networkmodel.rev201116.topology.update.result.TopologyChanges;
25 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.networkmodel.rev201116.topology.update.result.TopologyChangesKey;
26 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.state.types.rev191129.State;
27 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.service.list.Services;
28 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev230501.path.description.AToZDirection;
29 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev230501.path.description.AToZDirectionBuilder;
30 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev230501.path.description.ZToADirection;
31 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev230501.path.description.ZToADirectionBuilder;
32 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev230501.path.description.atoz.direction.AToZ;
33 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev230501.path.description.atoz.direction.AToZBuilder;
34 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev230501.path.description.atoz.direction.AToZKey;
35 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev230501.path.description.ztoa.direction.ZToA;
36 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev230501.path.description.ztoa.direction.ZToABuilder;
37 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev230501.path.description.ztoa.direction.ZToAKey;
38 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev230501.pce.resource.Resource;
39 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev230501.pce.resource.ResourceBuilder;
40 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev230501.pce.resource.resource.resource.Link;
41 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev230501.pce.resource.resource.resource.TerminationPoint;
42 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev220118.service.path.PathDescription;
43 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev220118.service.path.PathDescriptionBuilder;
44 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.servicepath.rev171017.ServicePathList;
45 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.servicepath.rev171017.service.path.list.ServicePaths;
46 import org.osgi.service.component.annotations.Activate;
47 import org.osgi.service.component.annotations.Component;
48 import org.osgi.service.component.annotations.Reference;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 @Component(service = {NetworkModelNotificationHandler.class, NetworkListener.class})
53 public class NetworkModelNotificationHandler implements NetworkListener {
54
55     private static final Logger LOG = LoggerFactory.getLogger(NetworkModelNotificationHandler.class);
56     private ServiceDataStoreOperations serviceDataStoreOperations;
57     private TopologyUpdateResult topologyUpdateResult;
58
59     @Activate
60     public NetworkModelNotificationHandler(@Reference NotificationPublishService notificationPublishService,
61             @Reference ServiceDataStoreOperations serviceDataStoreOperations) {
62         this.serviceDataStoreOperations = serviceDataStoreOperations;
63     }
64
65     public CompositeListener getCompositeListener() {
66         return new CompositeListener(Set.of(
67             new CompositeListener.Component<>(TopologyUpdateResult.class, this::onTopologyUpdateResult)));
68     }
69
70     void onTopologyUpdateResult(TopologyUpdateResult notification) {
71         LOG.debug("Topology update notification: {}", notification);
72         if (compareTopologyUpdateResult(notification)) {
73             LOG.warn("TopologyUpdateResult already wired !");
74             return;
75         }
76         topologyUpdateResult = new TopologyUpdateResultBuilder()
77                 .setTopologyChanges(new HashMap<>(notification.getTopologyChanges()))
78                 .build();
79         // Update service datastore and service path description
80         updateServicePaths(notification);
81     }
82
83     /**
84      * Process topology update result.
85      * @param notification the result notification.
86      */
87     protected void updateServicePaths(TopologyUpdateResult notification) {
88         @Nullable
89         Map<TopologyChangesKey, TopologyChanges> topologyChanges = notification.getTopologyChanges();
90         Optional<ServicePathList> servicePathListOptional = this.serviceDataStoreOperations.getServicePaths();
91         if (servicePathListOptional.isEmpty()) {
92             LOG.warn("Enable to retrieve service path list");
93             return;
94         }
95         ServicePathList servicePathList = servicePathListOptional.orElseThrow();
96         for (ServicePaths servicePaths : servicePathList.getServicePaths().values()) {
97             String serviceName = servicePaths.getServicePathName();
98             PathDescription pathDescription = servicePaths.getPathDescription();
99             // update path descriptions in the datastore
100             Map<AToZKey, AToZ> updatedAtoZ = changePathElementStateAZ(topologyChanges, pathDescription);
101             Map<ZToAKey, ZToA> updatedZtoA = changePathElementStateZA(topologyChanges, pathDescription);
102             OperationResult operationResult = this.serviceDataStoreOperations
103                     .modifyServicePath(buildNewPathDescription(pathDescription, updatedAtoZ, updatedZtoA), serviceName);
104             if (!operationResult.isSuccess()) {
105                 LOG.warn("Service Path not updated in datastore!");
106                 continue;
107             }
108             // update service in the datastore. Only path description with all elements in service can have a service
109             // in service. Therefore we check if all the states of the path description resources are inService
110             Optional<Services> serviceOptional = this.serviceDataStoreOperations.getService(serviceName);
111             if (serviceOptional.isEmpty()) {
112                 LOG.error("Couldn't retrieve service");
113                 continue;
114             }
115             Services services = serviceOptional.orElseThrow();
116             State newState;
117             switch (services.getOperationalState()) {
118                 case InService:
119                     if (allElementsinPathinService(updatedAtoZ, updatedZtoA)) {
120                         LOG.debug("Service {} state does not need to be modified", serviceName);
121                         continue;
122                     }
123                     newState = State.OutOfService;
124                     break;
125                 case OutOfService:
126                     if (!allElementsinPathinService(updatedAtoZ, updatedZtoA)) {
127                         LOG.debug("Service {} state does not need to be modified", serviceName);
128                         continue;
129                     }
130                     newState = State.InService;
131                     break;
132                 default:
133                     LOG.warn("Service {} state not managed", serviceName);
134                     continue;
135             }
136
137
138             LOG.debug("Service={} needs to be updated to {}", serviceName, newState);
139             //if (operationResult1 != null && operationResult1.isSuccess()) {
140             //null check probably no more needed
141             if (this.serviceDataStoreOperations
142                     .modifyService(serviceName, newState, services.getAdministrativeState())
143                     .isSuccess()) {
144                 LOG.info("Service state of {} correctly updated to {} in datastore", serviceName, newState);
145                 continue;
146             }
147             LOG.error("Service state of {} cannot be updated to {} in datastore", serviceName, newState);
148
149         }
150     }
151
152     protected Map<ZToAKey, ZToA> changePathElementStateZA(Map<TopologyChangesKey, TopologyChanges> topologyChanges,
153         PathDescription pathDescription) {
154         Map<ZToAKey, ZToA> newztoaMap = new HashMap<>(pathDescription.getZToADirection().getZToA());
155         List<ZToA> tpResources = pathDescription.getZToADirection().getZToA().values().stream()
156                 .filter(ele -> ele.getResource().getResource() instanceof TerminationPoint)
157                 .collect(Collectors.toList());
158         List<ZToA> linkResources = pathDescription.getZToADirection().getZToA().values().stream()
159             .filter(ele -> ele.getResource().getResource() instanceof Link)
160             .collect(Collectors.toList());
161         for (ZToA ztoA : tpResources) {
162             String ztoAid = ztoA.getId();
163             State ztoAState = ztoA.getResource().getState();
164             TerminationPoint tp = (TerminationPoint) ztoA.getResource().getResource();
165             if (topologyChanges.containsKey(new TopologyChangesKey(tp.getTpNodeId(), tp.getTpId()))
166                 && !topologyChanges.get(new TopologyChangesKey(tp.getTpNodeId(), tp.getTpId())).getState()
167                 .equals(ztoAState)) {
168                 LOG.debug("updating ztoa tp {}", ztoA);
169                 State updatedState = topologyChanges.get(new TopologyChangesKey(tp.getTpNodeId(), tp.getTpId()))
170                     .getState();
171                 Resource updatedResource = new ResourceBuilder()
172                     .setResource(tp)
173                     .setState(updatedState)
174                     .build();
175                 ZToA updatedZToA = new ZToABuilder(ztoA)
176                     .setId(ztoAid)
177                     .setResource(updatedResource)
178                     .build();
179                 newztoaMap.put(updatedZToA.key(), updatedZToA);
180                 for (ZToA ztoALink : linkResources) {
181                     Link link = (Link)ztoALink.getResource().getResource();
182                     if (link.getLinkId().contains(tp.getTpNodeId())
183                             && link.getLinkId().contains(tp.getTpId())
184                             && ztoALink.getResource().getState() != updatedState) {
185                         ZToA updatedZToAlink = new ZToABuilder(ztoALink)
186                             .setId(ztoAid)
187                             .setResource(new ResourceBuilder()
188                                 .setResource(link)
189                                 .setState(updatedState)
190                                 .build())
191                             .build();
192                         newztoaMap.put(updatedZToAlink.key(), updatedZToAlink);
193                     }
194                 }
195             }
196         }
197         return newztoaMap;
198     }
199
200     protected Map<AToZKey, AToZ> changePathElementStateAZ(Map<TopologyChangesKey,
201             TopologyChanges> topologyChanges, PathDescription pathDescription) {
202
203         Map<AToZKey, AToZ> newatozMap = new HashMap<>(pathDescription.getAToZDirection().getAToZ());
204         List<AToZ> tpResources = pathDescription.getAToZDirection().getAToZ().values().stream()
205             .filter(ele -> ele.getResource().getResource() instanceof TerminationPoint)
206             .collect(Collectors.toList());
207         List<AToZ> linkResources = pathDescription.getAToZDirection().getAToZ().values().stream()
208             .filter(ele -> ele.getResource().getResource() instanceof Link)
209             .collect(Collectors.toList());
210         for (AToZ atoZ : tpResources) {
211             String atoZid = atoZ.getId();
212             State atoZState = atoZ.getResource().getState();
213             TerminationPoint tp = (TerminationPoint) atoZ.getResource().getResource();
214             if (topologyChanges.containsKey(new TopologyChangesKey(tp.getTpNodeId(), tp.getTpId()))
215                 && !topologyChanges.get(new TopologyChangesKey(tp.getTpNodeId(), tp.getTpId())).getState()
216                 .equals(atoZState)) {
217                 LOG.debug("updating atoz tp {}", atoZ);
218                 State updatedState = topologyChanges.get(new TopologyChangesKey(tp.getTpNodeId(), tp.getTpId()))
219                     .getState();
220                 Resource updatedResource = new ResourceBuilder()
221                     .setResource(tp)
222                     .setState(updatedState)
223                     .build();
224                 AToZ updatedAToZ = new AToZBuilder(atoZ)
225                     .setId(atoZid)
226                     .setResource(updatedResource)
227                     .build();
228                 newatozMap.put(updatedAToZ.key(), updatedAToZ);
229                 for (AToZ atozLink : linkResources) {
230                     Link link = (Link)atozLink.getResource().getResource();
231                     if (link.getLinkId().contains(tp.getTpNodeId())
232                             && link.getLinkId().contains(tp.getTpId())
233                             && atozLink.getResource().getState() != updatedState) {
234                         AToZ updatedAToZlink = new AToZBuilder(atozLink)
235                             .setId(atoZid)
236                             .setResource(new ResourceBuilder()
237                                 .setResource(link)
238                                 .setState(updatedState)
239                                 .build())
240                             .build();
241                         newatozMap.put(updatedAToZlink.key(), updatedAToZlink);
242                     }
243                 }
244             }
245         }
246         return newatozMap;
247     }
248
249     private PathDescription buildNewPathDescription(PathDescription pathDescription, Map<AToZKey, AToZ> updatedAtoZ,
250                                                     Map<ZToAKey, ZToA> updatedZtoA) {
251         AToZDirection atozDir = new AToZDirectionBuilder(pathDescription.getAToZDirection())
252                 .setAToZ(updatedAtoZ)
253                 .build();
254         ZToADirection ztoaDir = new ZToADirectionBuilder(pathDescription.getZToADirection())
255                 .setZToA(updatedZtoA)
256                 .build();
257         return new PathDescriptionBuilder()
258             .setAToZDirection(atozDir)
259             .setZToADirection(ztoaDir)
260             .build();
261     }
262
263     protected boolean allElementsinPathinService(Map<AToZKey, AToZ> updatedAtoZ, Map<ZToAKey, ZToA> updatedZtoA) {
264         boolean allEleminService = true;
265         Iterator<AToZ> i1 = updatedAtoZ.values().iterator();
266         Iterator<ZToA> i2 = updatedZtoA.values().iterator();
267         // TODO: both directions have same length?
268         while (i1.hasNext() && i2.hasNext()) {
269             if (State.OutOfService.getIntValue() == i1.next().getResource().getState().getIntValue()
270                     || State.OutOfService.getIntValue() == i2.next().getResource().getState().getIntValue()) {
271                 allEleminService = false;
272                 break;
273             }
274         }
275
276         return allEleminService;
277     }
278
279     private boolean compareTopologyUpdateResult(TopologyUpdateResult notification) {
280         return topologyUpdateResult != null
281                 && topologyUpdateResult.getTopologyChanges().equals(notification.getTopologyChanges());
282     }
283
284     @Override
285     public void setserviceDataStoreOperations(ServiceDataStoreOperations serviceData) {
286         this.serviceDataStoreOperations = serviceData;
287     }
288 }