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