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