2 * Copyright © 2021 Nokia. All rights reserved.
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
8 package org.opendaylight.transportpce.tapi;
10 import java.util.Collection;
11 import java.util.HashMap;
13 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.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.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;
45 public class R2RTapiLinkDiscovery {
47 private static final Logger LOG = LoggerFactory.getLogger(R2RTapiLinkDiscovery.class);
49 private final NetworkTransactionService networkTransactionService;
50 private final DeviceTransactionManager deviceTransactionManager;
51 private final TapiLink tapiLink;
53 public R2RTapiLinkDiscovery(NetworkTransactionService networkTransactionService,
54 DeviceTransactionManager deviceTransactionManager, TapiLink tapiLink) {
55 this.networkTransactionService = networkTransactionService;
56 this.deviceTransactionManager = deviceTransactionManager;
57 this.tapiLink = tapiLink;
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) {
67 InstanceIdentifier<Protocols> protocols121IID = InstanceIdentifier
68 .builderOfInherited(OrgOpenroadmDeviceData.class, OrgOpenroadmDevice.class)
69 .child(Protocols.class)
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<>();
79 NbrList nbr121List = protocol121Object.get().augmentation(Protocols1.class).getLldp().getNbrList();
80 LOG.info("LLDP subtree is present. Device has {} neighbours", nbr121List.getIfName().size());
81 // try to create rdm2rdm link
82 return rdm2rdmLinkCreatev121(nodeId, tapiTopoUuid, nbr121List);
85 InstanceIdentifier<org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev181019.org.openroadm.device
86 .container.org.openroadm.device.Protocols> protocols221IID = InstanceIdentifier
88 org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev181019.OrgOpenroadmDeviceData.class,
89 org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev181019.org.openroadm.device.container
90 .OrgOpenroadmDevice.class)
91 .child(org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev181019.org.openroadm.device
92 .container.org.openroadm.device.Protocols.class)
94 var protocol221Object = this.deviceTransactionManager
95 .getDataFromDevice(nodeId.getValue(), LogicalDatastoreType.OPERATIONAL, protocols221IID,
96 Timeouts.DEVICE_READ_TIMEOUT, Timeouts.DEVICE_READ_TIMEOUT_UNIT);
97 if (hasNoNeighbor221(protocol221Object)) {
98 LOG.warn("LLDP subtree is missing or incomplete: isolated openroadm device");
99 return new HashMap<>();
101 var nbr221List = protocol221Object.get().augmentation(
102 org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev181019.Protocols1.class)
103 .getLldp().getNbrList();
104 LOG.info("LLDP subtree is present. Device has {} neighbours", nbr221List.getIfName().size());
105 return rdm2rdmLinkCreatev221(nodeId, tapiTopoUuid, nbr221List);
108 LOG.info("Not yet supported?");
109 return new HashMap<>();
111 LOG.error("Unable to read LLDP data for unmanaged openroadm device version");
112 return new HashMap<>();
116 private boolean hasNoNeighbor121(Optional<Protocols> protocol121Object) {
117 return protocol121Object.isEmpty()
118 || protocol121Object.get().augmentation(Protocols1.class) == null
119 || protocol121Object.get().augmentation(Protocols1.class).getLldp() == null
120 || protocol121Object.get().augmentation(Protocols1.class).getLldp().getNbrList() == null;
123 private boolean hasNoNeighbor221(Optional<
124 org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev181019.org.openroadm.device.container.org
125 .openroadm.device.Protocols> protocol221Object) {
126 return protocol221Object.isEmpty()
127 || protocol221Object.get().augmentation(
128 org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev181019.Protocols1.class) == null
129 || protocol221Object.get().augmentation(
130 org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev181019.Protocols1.class)
132 || protocol221Object.get().augmentation(
133 org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev181019.Protocols1.class)
134 .getLldp().getNbrList() == null;
137 private Map<LinkKey, Link> rdm2rdmLinkCreatev221(NodeId nodeId, Uuid tapiTopoUuid,
138 org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev181019.lldp.container.lldp.NbrList nbrList) {
139 Map<LinkKey, Link> linkMap = new HashMap<>();
140 for (org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev181019.lldp.container.lldp.nbr.list.IfName
141 ifName : nbrList.nonnullIfName().values()) {
142 if (ifName.getRemoteSysName() == null) {
143 LOG.warn("Tapi R2R Link LLDP subtree neighbour is empty for nodeId: {}, ifName: {}",
144 nodeId.getValue(),ifName.getIfName());
147 Optional<MountPoint> mps = this.deviceTransactionManager.getDeviceMountPoint(ifName.getRemoteSysName());
148 if (!mps.isPresent()) {
149 LOG.warn("Tapi R2R Link Neighbouring nodeId: {} is not mounted yet", ifName.getRemoteSysName());
150 // The controller raises a warning rather than an error because the first node to
151 // mount cannot see its neighbors yet. The link will be detected when processing
152 // the neighbor node.
155 Link omsLink = createR2RTapiLink(nodeId, ifName.getIfName(), ifName.getRemoteSysName(),
156 ifName.getRemotePortId(), tapiTopoUuid);
157 if (omsLink != null) {
158 linkMap.put(omsLink.key(), omsLink);
160 LOG.error("Link was not created");
166 private Map<LinkKey, Link> rdm2rdmLinkCreatev121(NodeId nodeId, Uuid tapiTopoUuid, NbrList nbrList) {
167 Map<LinkKey, Link> linkMap = new HashMap<>();
168 for (IfName ifName : nbrList.nonnullIfName().values()) {
169 if (ifName.getRemoteSysName() == null) {
170 LOG.warn("Tapi R2R Link LLDP subtree neighbour is empty for nodeId: {}, ifName: {}",
171 nodeId.getValue(),ifName.getIfName());
174 Optional<MountPoint> mps = this.deviceTransactionManager.getDeviceMountPoint(ifName
175 .getRemoteSysName());
176 if (!mps.isPresent()) {
177 LOG.warn("Tapi R2R Link Neighbouring nodeId: {} is not mounted yet", ifName.getRemoteSysName());
178 // The controller raises a warning rather than an error because the first node to
179 // mount cannot see its neighbors yet. The link will be detected when processing
180 // the neighbor node.
183 Link omsLink = createR2RTapiLink(nodeId, ifName.getIfName(), ifName.getRemoteSysName(),
184 ifName.getRemotePortId(), tapiTopoUuid);
185 if (omsLink != null) {
186 linkMap.put(omsLink.key(), omsLink);
188 LOG.error("Link was not created");
194 public Link createR2RTapiLink(NodeId nodeId, String interfaceName, String remoteSystemName,
195 String remoteInterfaceName, Uuid tapiTopoUuid) {
196 String srcTpTx = null;
197 String srcTpRx = null;
198 String destTpTx = null;
199 String destTpRx = null;
200 // Find which degree is associated with ethernet interface
201 Integer srcDegId = getDegFromInterface(nodeId, interfaceName);
202 if (srcDegId == null) {
203 LOG.error("Tapi R2R Link Couldnt find degree connected to Ethernet interface for nodeId: {}", nodeId);
206 // Check whether degree is Unidirectional or Bidirectional by counting
208 // circuit-packs under degree subtree
209 Direction sourceDirection = getDegreeDirection(srcDegId, nodeId);
210 if (Direction.NotApplicable == sourceDirection) {
211 LOG.error("Tapi R2R Link Couldnt find degree direction for nodeId: {} and degree: {}", nodeId, srcDegId);
213 } else if (Direction.Bidirectional == sourceDirection) {
214 srcTpTx = "DEG" + srcDegId + "-TTP-TXRX";
215 srcTpRx = "DEG" + srcDegId + "-TTP-TXRX";
217 srcTpTx = "DEG" + srcDegId + "-TTP-TX";
218 srcTpRx = "DEG" + srcDegId + "-TTP-RX";
220 LOG.debug("Tapi R2R Link SrcTPTx {}, SrcTPRx {}", srcTpTx, srcTpRx);
221 // Find degree for which Ethernet interface is created on other end
222 NodeId destNodeId = new NodeId(remoteSystemName);
223 Integer destDegId = getDegFromInterface(destNodeId, remoteInterfaceName);
224 if (destDegId == null) {
225 LOG.error("Tapi R2R Link Couldnt find degree connected to Ethernet interface for nodeId: {}", nodeId);
228 // Check whether degree is Unidirectional or Bidirectional by counting
230 // circuit-packs under degree subtree
231 Direction destinationDirection = getDegreeDirection(destDegId, destNodeId);
232 if (Direction.NotApplicable == destinationDirection) {
233 LOG.error("Tapi R2R Link Couldnt find degree direction for nodeId: {} and degree: {}",
234 destNodeId, destDegId);
236 } else if (Direction.Bidirectional == destinationDirection) {
237 destTpTx = "DEG" + destDegId + "-TTP-TXRX";
238 destTpRx = "DEG" + destDegId + "-TTP-TXRX";
240 destTpTx = "DEG" + destDegId + "-TTP-TX";
241 destTpRx = "DEG" + destDegId + "-TTP-RX";
243 // Todo -> only handling for the bidirectional case. I assume all tps are of the type bidirectional
244 LOG.debug("Tapi R2R Link DstTPTx {}, DstTPRx {}", destTpTx, srcTpRx);
246 // Create OMS Tapi Link
247 LOG.info("Tapi R2R Link Found a neighbor SrcNodeId: {} , SrcDegId: {} , SrcTPId: {}, DestNodeId:{} , "
248 + "DestDegId: {}, DestTPId: {}", nodeId.getValue(), srcDegId, srcTpTx, destNodeId, destDegId, destTpRx);
249 Link omsLink = this.tapiLink.createTapiLink(nodeId.getValue(), srcTpTx, destNodeId.getValue(), destTpTx,
250 TapiStringConstants.OMS_RDM_RDM_LINK, TapiStringConstants.PHTNC_MEDIA, TapiStringConstants.PHTNC_MEDIA,
251 TapiStringConstants.PHTNC_MEDIA, TapiStringConstants.PHTNC_MEDIA,
252 this.tapiLink.getAdminState(nodeId.getValue(), destNodeId.getValue(), srcTpTx, destTpTx),
253 this.tapiLink.getOperState(nodeId.getValue(), destNodeId.getValue(), srcTpTx, destTpTx),
254 Set.of(LayerProtocolName.PHOTONICMEDIA), Set.of(LayerProtocolName.PHOTONICMEDIA.getName()), tapiTopoUuid);
255 LOG.info("Tapi R2R Link OMS link created = {}", omsLink);
259 private Integer getDegFromInterface(NodeId nodeId, String interfaceName) {
260 InstanceIdentifier<Nodes> nodesIID = InstanceIdentifier.builder(Network.class)
261 .child(Nodes.class, new NodesKey(nodeId.getValue())).build();
264 Optional<Nodes> nodesObject = this.networkTransactionService.read(LogicalDatastoreType.CONFIGURATION,
266 if (nodesObject.isEmpty() || (nodesObject.get().getCpToDegree() == null)) {
267 LOG.warn("Could not find mapping for Interface {} for nodeId {}", interfaceName,
271 Collection<CpToDegree> cpToDeg = nodesObject.get().nonnullCpToDegree().values();
272 Stream<CpToDegree> cpToDegStream = cpToDeg.stream().filter(cp -> cp.getInterfaceName() != null)
273 .filter(cp -> cp.getInterfaceName().equals(interfaceName));
274 if (cpToDegStream != null) {
275 Optional<CpToDegree> firstCpToDegree = cpToDegStream.findFirst();
276 if (firstCpToDegree.isEmpty() || (firstCpToDegree == null)) {
277 LOG.debug("Not found so returning nothing");
280 LOG.debug("Found and returning {}",firstCpToDegree.get().getDegreeNumber().intValue());
281 return firstCpToDegree.get().getDegreeNumber().intValue();
283 LOG.warn("CircuitPack stream couldnt find anything for nodeId: {} and interfaceName: {}",
284 nodeId.getValue(),interfaceName);
286 } catch (InterruptedException | ExecutionException ex) {
287 LOG.error("Unable to read mapping for Interface : {} for nodeId {}", interfaceName, nodeId, ex);
292 public Direction getDegreeDirection(Integer degreeCounter, NodeId nodeId) {
293 InstanceIdentifier<Nodes> nodesIID = InstanceIdentifier.builder(Network.class)
294 .child(Nodes.class, new NodesKey(nodeId.getValue())).build();
296 Optional<Nodes> nodesObject = this.networkTransactionService.read(LogicalDatastoreType.CONFIGURATION,
298 if (nodesObject.isPresent() && (nodesObject.get().getMapping() != null)) {
299 Collection<Mapping> mappingList = nodesObject.get().nonnullMapping().values();
300 mappingList = mappingList.stream().filter(mp -> mp.getLogicalConnectionPoint().contains("DEG"
301 + degreeCounter)).collect(Collectors.toList());
302 if (mappingList.size() == 1) {
303 return Direction.Bidirectional;
304 } else if (mappingList.size() > 1) {
308 } catch (InterruptedException | ExecutionException e) {
309 LOG.error("Failed getting Mapping data from portMapping",e);
311 return Direction.NotApplicable;