feafd42dad669a3c8b5cd2562ab00ad17dc40fd2
[transportpce.git] / tapi / src / main / java / org / opendaylight / transportpce / tapi / R2RTapiLinkDiscovery.java
1 /*
2  * Copyright © 2021 Nokia.  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.tapi;
9
10 import java.util.Collection;
11 import java.util.HashMap;
12 import java.util.Map;
13 import java.util.Optional;
14 import java.util.Set;
15 import java.util.concurrent.ExecutionException;
16 import java.util.stream.Collectors;
17 import java.util.stream.Stream;
18 import org.opendaylight.mdsal.binding.api.MountPoint;
19 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
20 import org.opendaylight.transportpce.common.Timeouts;
21 import org.opendaylight.transportpce.common.device.DeviceTransactionManager;
22 import org.opendaylight.transportpce.common.network.NetworkTransactionService;
23 import org.opendaylight.transportpce.tapi.utils.TapiLink;
24 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev220922.Network;
25 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev220922.cp.to.degree.CpToDegree;
26 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev220922.mapping.Mapping;
27 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev220922.network.Nodes;
28 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev220922.network.NodesKey;
29 import org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev170206.OrgOpenroadmDeviceData;
30 import org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev170206.org.openroadm.device.container.OrgOpenroadmDevice;
31 import org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev170206.org.openroadm.device.container.org.openroadm.device.Protocols;
32 import org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev161014.Protocols1;
33 import org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev161014.lldp.container.lldp.NbrList;
34 import org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev161014.lldp.container.lldp.nbr.list.IfName;
35 import org.opendaylight.yang.gen.v1.http.org.transportpce.common.types.rev220926.Direction;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.rev180226.NodeId;
37 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev221121.LayerProtocolName;
38 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev221121.Uuid;
39 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.topology.rev221121.topology.Link;
40 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.topology.rev221121.topology.LinkKey;
41 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 public class R2RTapiLinkDiscovery {
46
47     private static final Logger LOG = LoggerFactory.getLogger(R2RTapiLinkDiscovery.class);
48
49     private final NetworkTransactionService networkTransactionService;
50     private final DeviceTransactionManager deviceTransactionManager;
51     private final TapiLink tapiLink;
52
53     public R2RTapiLinkDiscovery(NetworkTransactionService networkTransactionService,
54             DeviceTransactionManager deviceTransactionManager, TapiLink tapiLink) {
55         this.networkTransactionService = networkTransactionService;
56         this.deviceTransactionManager = deviceTransactionManager;
57         this.tapiLink = tapiLink;
58     }
59
60     public Map<LinkKey, Link> readLLDP(NodeId nodeId, int nodeVersion, Uuid tapiTopoUuid) {
61         LOG.info("Tapi R2R Link Node version = {}", nodeVersion);
62         // TODO -> waiting for device 7.1 in network model to change this to a switch statement and include
63         //  support for 7.1 devices
64         switch (nodeVersion) {
65             case 1:
66                 // 1.2.1
67                 InstanceIdentifier<Protocols> protocols121IID = InstanceIdentifier
68                     .builderOfInherited(OrgOpenroadmDeviceData.class, OrgOpenroadmDevice.class)
69                     .child(Protocols.class)
70                     .build();
71                 Optional<Protocols> protocol121Object = this.deviceTransactionManager.getDataFromDevice(
72                     nodeId.getValue(), LogicalDatastoreType.OPERATIONAL, protocols121IID, Timeouts.DEVICE_READ_TIMEOUT,
73                     Timeouts.DEVICE_READ_TIMEOUT_UNIT);
74                 if (hasNoNeighbor121(protocol121Object)) {
75                     LOG.warn("LLDP subtree is missing or incomplete: isolated openroadm device");
76                     return new HashMap<>();
77                 }
78                 // get neighbor list
79                 NbrList nbr121List = protocol121Object.orElseThrow().augmentation(Protocols1.class).getLldp()
80                     .getNbrList();
81                 LOG.info("LLDP subtree is present. Device has {} neighbours", nbr121List.getIfName().size());
82                 // try to create rdm2rdm link
83                 return rdm2rdmLinkCreatev121(nodeId, tapiTopoUuid, nbr121List);
84             case 2:
85                 // 2.2.1
86                 InstanceIdentifier<org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev181019.org.openroadm.device
87                         .container.org.openroadm.device.Protocols> protocols221IID = InstanceIdentifier
88                     .builderOfInherited(
89                         org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev181019.OrgOpenroadmDeviceData.class,
90                         org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev181019.org.openroadm.device.container
91                             .OrgOpenroadmDevice.class)
92                     .child(org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev181019.org.openroadm.device
93                         .container.org.openroadm.device.Protocols.class)
94                     .build();
95                 var protocol221Object = this.deviceTransactionManager
96                     .getDataFromDevice(nodeId.getValue(), LogicalDatastoreType.OPERATIONAL, protocols221IID,
97                         Timeouts.DEVICE_READ_TIMEOUT, Timeouts.DEVICE_READ_TIMEOUT_UNIT);
98                 if (hasNoNeighbor221(protocol221Object)) {
99                     LOG.warn("LLDP subtree is missing or incomplete: isolated openroadm device");
100                     return new HashMap<>();
101                 }
102                 var nbr221List = protocol221Object.orElseThrow().augmentation(
103                         org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev181019.Protocols1.class)
104                     .getLldp().getNbrList();
105                 LOG.info("LLDP subtree is present. Device has {} neighbours", nbr221List.getIfName().size());
106                 return rdm2rdmLinkCreatev221(nodeId, tapiTopoUuid, nbr221List);
107             case 3:
108                 // 7.1.0
109                 LOG.info("Not yet supported?");
110                 return new HashMap<>();
111             default:
112                 LOG.error("Unable to read LLDP data for unmanaged openroadm device version");
113                 return new HashMap<>();
114         }
115     }
116
117     private boolean hasNoNeighbor121(Optional<Protocols> protocol121Object) {
118         return protocol121Object.isEmpty()
119                 || protocol121Object.orElseThrow().augmentation(Protocols1.class) == null
120                 || protocol121Object.orElseThrow().augmentation(Protocols1.class).getLldp() == null
121                 || protocol121Object.orElseThrow().augmentation(Protocols1.class).getLldp().getNbrList() == null;
122     }
123
124     private boolean hasNoNeighbor221(Optional<
125             org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev181019.org.openroadm.device.container.org
126                     .openroadm.device.Protocols> protocol221Object) {
127         return protocol221Object.isEmpty()
128                 || protocol221Object.orElseThrow().augmentation(
129                         org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev181019.Protocols1.class) == null
130                 || protocol221Object.orElseThrow().augmentation(
131                         org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev181019.Protocols1.class)
132                     .getLldp() == null
133                 || protocol221Object.orElseThrow().augmentation(
134                         org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev181019.Protocols1.class)
135                     .getLldp().getNbrList() == null;
136     }
137
138     private Map<LinkKey, Link> rdm2rdmLinkCreatev221(NodeId nodeId, Uuid tapiTopoUuid,
139             org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev181019.lldp.container.lldp.NbrList nbrList) {
140         Map<LinkKey, Link> linkMap = new HashMap<>();
141         for (org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev181019.lldp.container.lldp.nbr.list.IfName
142                 ifName : nbrList.nonnullIfName().values()) {
143             if (ifName.getRemoteSysName() == null) {
144                 LOG.warn("Tapi R2R Link LLDP subtree neighbour is empty for nodeId: {}, ifName: {}",
145                     nodeId.getValue(),ifName.getIfName());
146                 continue;
147             }
148             Optional<MountPoint> mps = this.deviceTransactionManager.getDeviceMountPoint(ifName.getRemoteSysName());
149             if (!mps.isPresent()) {
150                 LOG.warn("Tapi R2R Link Neighbouring nodeId: {} is not mounted yet", ifName.getRemoteSysName());
151                 // The controller raises a warning rather than an error because the first node to
152                 // mount cannot see its neighbors yet. The link will be detected when processing
153                 // the neighbor node.
154                 continue;
155             }
156             Link omsLink = createR2RTapiLink(nodeId, ifName.getIfName(), ifName.getRemoteSysName(),
157                 ifName.getRemotePortId(), tapiTopoUuid);
158             if (omsLink != null) {
159                 linkMap.put(omsLink.key(), omsLink);
160             } else {
161                 LOG.error("Link was not created");
162             }
163         }
164         return linkMap;
165     }
166
167     private Map<LinkKey, Link> rdm2rdmLinkCreatev121(NodeId nodeId, Uuid tapiTopoUuid, NbrList nbrList) {
168         Map<LinkKey, Link> linkMap = new HashMap<>();
169         for (IfName ifName : nbrList.nonnullIfName().values()) {
170             if (ifName.getRemoteSysName() == null) {
171                 LOG.warn("Tapi R2R Link LLDP subtree neighbour is empty for nodeId: {}, ifName: {}",
172                     nodeId.getValue(),ifName.getIfName());
173                 continue;
174             }
175             Optional<MountPoint> mps = this.deviceTransactionManager.getDeviceMountPoint(ifName
176                 .getRemoteSysName());
177             if (!mps.isPresent()) {
178                 LOG.warn("Tapi R2R Link Neighbouring nodeId: {} is not mounted yet", ifName.getRemoteSysName());
179                 // The controller raises a warning rather than an error because the first node to
180                 // mount cannot see its neighbors yet. The link will be detected when processing
181                 // the neighbor node.
182                 continue;
183             }
184             Link omsLink = createR2RTapiLink(nodeId, ifName.getIfName(), ifName.getRemoteSysName(),
185                 ifName.getRemotePortId(), tapiTopoUuid);
186             if (omsLink != null) {
187                 linkMap.put(omsLink.key(), omsLink);
188             } else {
189                 LOG.error("Link was not created");
190             }
191         }
192         return linkMap;
193     }
194
195     public Link createR2RTapiLink(NodeId nodeId, String interfaceName, String remoteSystemName,
196                                  String remoteInterfaceName, Uuid tapiTopoUuid) {
197         String srcTpTx = null;
198         String srcTpRx = null;
199         String destTpTx = null;
200         String destTpRx = null;
201         // Find which degree is associated with ethernet interface
202         Integer srcDegId = getDegFromInterface(nodeId, interfaceName);
203         if (srcDegId == null) {
204             LOG.error("Tapi R2R Link Couldnt find degree connected to Ethernet interface for nodeId: {}", nodeId);
205             return null;
206         }
207         // Check whether degree is Unidirectional or Bidirectional by counting
208         // number of
209         // circuit-packs under degree subtree
210         Direction sourceDirection = getDegreeDirection(srcDegId, nodeId);
211         if (Direction.NotApplicable == sourceDirection) {
212             LOG.error("Tapi R2R Link Couldnt find degree direction for nodeId: {} and degree: {}", nodeId, srcDegId);
213             return null;
214         } else if (Direction.Bidirectional == sourceDirection) {
215             srcTpTx = "DEG" + srcDegId + "-TTP-TXRX";
216             srcTpRx = "DEG" + srcDegId + "-TTP-TXRX";
217         } else {
218             srcTpTx = "DEG" + srcDegId + "-TTP-TX";
219             srcTpRx = "DEG" + srcDegId + "-TTP-RX";
220         }
221         LOG.debug("Tapi R2R Link SrcTPTx {}, SrcTPRx {}", srcTpTx, srcTpRx);
222         // Find degree for which Ethernet interface is created on other end
223         NodeId destNodeId = new NodeId(remoteSystemName);
224         Integer destDegId = getDegFromInterface(destNodeId, remoteInterfaceName);
225         if (destDegId == null) {
226             LOG.error("Tapi R2R Link Couldnt find degree connected to Ethernet interface for nodeId: {}", nodeId);
227             return null;
228         }
229         // Check whether degree is Unidirectional or Bidirectional by counting
230         // number of
231         // circuit-packs under degree subtree
232         Direction destinationDirection = getDegreeDirection(destDegId, destNodeId);
233         if (Direction.NotApplicable == destinationDirection) {
234             LOG.error("Tapi R2R Link Couldnt find degree direction for nodeId: {} and degree: {}",
235                 destNodeId, destDegId);
236             return null;
237         } else if (Direction.Bidirectional == destinationDirection) {
238             destTpTx = "DEG" + destDegId + "-TTP-TXRX";
239             destTpRx = "DEG" + destDegId + "-TTP-TXRX";
240         } else {
241             destTpTx = "DEG" + destDegId + "-TTP-TX";
242             destTpRx = "DEG" + destDegId + "-TTP-RX";
243         }
244         // Todo -> only handling for the bidirectional case. I assume all tps are of the type bidirectional
245         LOG.debug("Tapi R2R Link DstTPTx {}, DstTPRx {}", destTpTx, srcTpRx);
246
247         // Create OMS Tapi Link
248         LOG.info("Tapi R2R Link Found a neighbor SrcNodeId: {} , SrcDegId: {} , SrcTPId: {}, DestNodeId:{} , "
249             + "DestDegId: {}, DestTPId: {}", nodeId.getValue(), srcDegId, srcTpTx, destNodeId, destDegId, destTpRx);
250         Link omsLink = this.tapiLink.createTapiLink(nodeId.getValue(), srcTpTx, destNodeId.getValue(), destTpTx,
251             TapiStringConstants.OMS_RDM_RDM_LINK, TapiStringConstants.PHTNC_MEDIA, TapiStringConstants.PHTNC_MEDIA,
252             TapiStringConstants.PHTNC_MEDIA_OTS, TapiStringConstants.PHTNC_MEDIA_OTS,
253             this.tapiLink.getAdminState(nodeId.getValue(), destNodeId.getValue(), srcTpTx, destTpTx),
254             this.tapiLink.getOperState(nodeId.getValue(), destNodeId.getValue(), srcTpTx, destTpTx),
255             Set.of(LayerProtocolName.PHOTONICMEDIA), Set.of(LayerProtocolName.PHOTONICMEDIA.getName()), tapiTopoUuid);
256         LOG.info("Tapi R2R Link OMS link created = {}", omsLink);
257         LOG.debug("inputAdminstate= {}, inputoperstate = {}",
258             this.tapiLink.getAdminState(nodeId.getValue(), destNodeId.getValue(), srcTpTx, destTpTx),
259             this.tapiLink.getOperState(nodeId.getValue(), destNodeId.getValue(), srcTpTx, destTpTx));
260         return omsLink;
261     }
262
263     private Integer getDegFromInterface(NodeId nodeId, String interfaceName) {
264         InstanceIdentifier<Nodes> nodesIID = InstanceIdentifier.builder(Network.class)
265             .child(Nodes.class, new NodesKey(nodeId.getValue())).build();
266         try {
267
268             Optional<Nodes> nodesObject = this.networkTransactionService.read(LogicalDatastoreType.CONFIGURATION,
269                 nodesIID).get();
270             if (nodesObject.isEmpty() || (nodesObject.orElseThrow().getCpToDegree() == null)) {
271                 LOG.warn("Could not find mapping for Interface {} for nodeId {}", interfaceName,
272                     nodeId.getValue());
273                 return null;
274             }
275             Collection<CpToDegree> cpToDeg = nodesObject.orElseThrow().nonnullCpToDegree().values();
276             Stream<CpToDegree> cpToDegStream = cpToDeg.stream().filter(cp -> cp.getInterfaceName() != null)
277                 .filter(cp -> cp.getInterfaceName().equals(interfaceName));
278             if (cpToDegStream != null) {
279                 Optional<CpToDegree> firstCpToDegree = cpToDegStream.findFirst();
280                 if (firstCpToDegree.isEmpty() || (firstCpToDegree == null)) {
281                     LOG.debug("Not found so returning nothing");
282                     return null;
283                 }
284                 LOG.debug("Found and returning {}",firstCpToDegree.orElseThrow().getDegreeNumber().intValue());
285                 return firstCpToDegree.orElseThrow().getDegreeNumber().intValue();
286             } else {
287                 LOG.warn("CircuitPack stream couldnt find anything for nodeId: {} and interfaceName: {}",
288                     nodeId.getValue(),interfaceName);
289             }
290         } catch (InterruptedException | ExecutionException ex) {
291             LOG.error("Unable to read mapping for Interface : {} for nodeId {}", interfaceName, nodeId, ex);
292         }
293         return null;
294     }
295
296     public Direction getDegreeDirection(Integer degreeCounter, NodeId nodeId) {
297         InstanceIdentifier<Nodes> nodesIID = InstanceIdentifier.builder(Network.class)
298             .child(Nodes.class, new NodesKey(nodeId.getValue())).build();
299         try {
300             Optional<Nodes> nodesObject = this.networkTransactionService.read(LogicalDatastoreType.CONFIGURATION,
301                 nodesIID).get();
302             if (nodesObject.isPresent() && (nodesObject.orElseThrow().getMapping() != null)) {
303                 Collection<Mapping> mappingList = nodesObject.orElseThrow().nonnullMapping().values();
304                 mappingList = mappingList.stream().filter(mp -> mp.getLogicalConnectionPoint().contains("DEG"
305                     + degreeCounter)).collect(Collectors.toList());
306                 if (mappingList.size() == 1) {
307                     return Direction.Bidirectional;
308                 } else if (mappingList.size() > 1) {
309                     return Direction.Tx;
310                 }
311             }
312         } catch (InterruptedException | ExecutionException e) {
313             LOG.error("Failed getting Mapping data from portMapping",e);
314         }
315         return Direction.NotApplicable;
316     }
317 }