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