2 * Copyright © 2016 AT&T and others. 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.networkmodel;
10 import static org.opendaylight.transportpce.common.StringConstants.OPENROADM_DEVICE_VERSION_1_2_1;
11 import static org.opendaylight.transportpce.common.StringConstants.OPENROADM_DEVICE_VERSION_2_2_1;
12 import static org.opendaylight.transportpce.common.StringConstants.OPENROADM_DEVICE_VERSION_7_1;
14 import java.util.Collection;
15 import java.util.Optional;
16 import java.util.concurrent.ExecutionException;
17 import java.util.stream.Collectors;
18 import java.util.stream.Stream;
19 import org.opendaylight.mdsal.binding.api.DataBroker;
20 import org.opendaylight.mdsal.binding.api.MountPoint;
21 import org.opendaylight.mdsal.binding.api.ReadTransaction;
22 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
23 import org.opendaylight.transportpce.common.Timeouts;
24 import org.opendaylight.transportpce.common.device.DeviceTransactionManager;
25 import org.opendaylight.transportpce.common.network.NetworkTransactionService;
26 import org.opendaylight.transportpce.networkmodel.util.TopologyUtils;
27 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.networkutils.rev220630.InitRoadmNodesInputBuilder;
28 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev220922.Network;
29 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev220922.cp.to.degree.CpToDegree;
30 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev220922.mapping.Mapping;
31 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev220922.network.Nodes;
32 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev220922.network.NodesKey;
33 import org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev170206.OrgOpenroadmDeviceData;
34 import org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev170206.org.openroadm.device.container.OrgOpenroadmDevice;
35 import org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev170206.org.openroadm.device.container.org.openroadm.device.Protocols;
36 import org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev161014.Protocols1;
37 import org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev161014.lldp.container.lldp.NbrList;
38 import org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev161014.lldp.container.lldp.nbr.list.IfName;
39 import org.opendaylight.yang.gen.v1.http.org.transportpce.common.types.rev220926.Direction;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.rev180226.NodeId;
41 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
42 import org.opendaylight.yangtools.yang.common.Uint8;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
46 public class R2RLinkDiscovery {
48 private static final Logger LOG = LoggerFactory.getLogger(R2RLinkDiscovery.class);
50 private final DataBroker dataBroker;
51 private final NetworkTransactionService networkTransactionService;
52 private final DeviceTransactionManager deviceTransactionManager;
54 public R2RLinkDiscovery(final DataBroker dataBroker, DeviceTransactionManager deviceTransactionManager,
55 NetworkTransactionService networkTransactionService) {
56 this.dataBroker = dataBroker;
57 this.deviceTransactionManager = deviceTransactionManager;
58 this.networkTransactionService = networkTransactionService;
61 public boolean readLLDP(NodeId nodeId, String nodeVersion) {
62 switch (nodeVersion) {
63 case OPENROADM_DEVICE_VERSION_1_2_1:
64 InstanceIdentifier<Protocols> protocols121IID = InstanceIdentifier
65 .builderOfInherited(OrgOpenroadmDeviceData.class, OrgOpenroadmDevice.class)
66 .child(Protocols.class)
68 Optional<Protocols> protocol121Object = this.deviceTransactionManager.getDataFromDevice(
69 nodeId.getValue(), LogicalDatastoreType.OPERATIONAL, protocols121IID, Timeouts.DEVICE_READ_TIMEOUT,
70 Timeouts.DEVICE_READ_TIMEOUT_UNIT);
71 if (hasNoNeighbor121(protocol121Object)) {
72 LOG.warn("LLDP subtree is missing or incomplete: isolated openroadm device");
76 NbrList nbr121List = protocol121Object.get().augmentation(Protocols1.class).getLldp().getNbrList();
77 LOG.info("LLDP subtree is present. Device has {} neighbours", nbr121List.getIfName().size());
78 // try to create rdm2rdm link
79 return rdm2rdmLinkCreatedv121(nodeId, nbr121List);
80 case OPENROADM_DEVICE_VERSION_2_2_1:
81 InstanceIdentifier<org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev181019.org.openroadm.device
82 .container.org.openroadm.device.Protocols> protocols221IID = InstanceIdentifier
84 org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev181019.OrgOpenroadmDeviceData.class,
85 org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev181019.org.openroadm.device.container
86 .OrgOpenroadmDevice.class)
88 org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev181019.org.openroadm.device.container
89 .org.openroadm.device.Protocols.class)
91 var protocol221Object = this.deviceTransactionManager
92 .getDataFromDevice(nodeId.getValue(), LogicalDatastoreType.OPERATIONAL, protocols221IID,
93 Timeouts.DEVICE_READ_TIMEOUT, Timeouts.DEVICE_READ_TIMEOUT_UNIT);
94 if (hasNoNeighbor221(protocol221Object)) {
95 LOG.warn("LLDP subtree is missing or incomplete: isolated openroadm device");
98 var nbr221List = protocol221Object.get().augmentation(
99 org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev181019.Protocols1.class)
100 .getLldp().getNbrList();
101 LOG.info("LLDP subtree is present. Device has {} neighbours", nbr221List.getIfName().size());
102 return rdm2rdmLinkCreatedv221(nodeId, nbr221List);
103 case OPENROADM_DEVICE_VERSION_7_1:
104 LOG.info("Not yet implemented?");
107 LOG.error("Unable to read LLDP data for unmanaged openroadm device version");
112 private boolean hasNoNeighbor121(Optional<Protocols> protocol121Object) {
113 return protocol121Object.isEmpty()
114 || protocol121Object.get().augmentation(Protocols1.class) == null
115 || protocol121Object.get().augmentation(Protocols1.class).getLldp() == null
116 || protocol121Object.get().augmentation(Protocols1.class).getLldp().getNbrList() == null;
119 private boolean hasNoNeighbor221(Optional<
120 org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev181019.org.openroadm.device.container.org
121 .openroadm.device.Protocols> protocol221Object) {
122 return protocol221Object.isEmpty()
123 || protocol221Object.get().augmentation(
124 org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev181019.Protocols1.class) == null
125 || protocol221Object.get().augmentation(
126 org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev181019.Protocols1.class)
128 || protocol221Object.get().augmentation(
129 org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev181019.Protocols1.class)
130 .getLldp().getNbrList() == null;
133 private boolean rdm2rdmLinkCreatedv221(NodeId nodeId,
134 org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev181019.lldp.container.lldp.NbrList nbrList) {
135 boolean success = true;
136 for (org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev181019.lldp.container.lldp.nbr.list.IfName
137 ifName : nbrList.nonnullIfName().values()) {
138 if (ifName.getRemoteSysName() == null) {
139 LOG.warn("LLDP subtree neighbour is empty for nodeId: {}, ifName: {}",
140 nodeId.getValue(),ifName.getIfName());
142 Optional<MountPoint> mps = this.deviceTransactionManager.getDeviceMountPoint(ifName
143 .getRemoteSysName());
144 if (!mps.isPresent()) {
145 LOG.warn("Neighbouring nodeId: {} is not mounted yet", ifName.getRemoteSysName());
146 // The controller raises a warning rather than an error because the first node to
147 // mount cannot see its neighbors yet. The link will be detected when processing
148 // the neighbor node.
150 if (!createR2RLink(nodeId, ifName.getIfName(), ifName.getRemoteSysName(),
151 ifName.getRemotePortId())) {
152 LOG.error("Link Creation failed between {} and {} nodes.", nodeId, ifName
153 .getRemoteSysName());
162 private boolean rdm2rdmLinkCreatedv121(NodeId nodeId, NbrList nbrList) {
163 boolean success = true;
164 for (IfName ifName : nbrList.nonnullIfName().values()) {
165 if (ifName.getRemoteSysName() == null) {
166 LOG.warn("LLDP subtree neighbour is empty for nodeId: {}, ifName: {}",
167 nodeId.getValue(),ifName.getIfName());
169 Optional<MountPoint> mps = this.deviceTransactionManager.getDeviceMountPoint(ifName
170 .getRemoteSysName());
171 if (!mps.isPresent()) {
172 LOG.warn("Neighbouring nodeId: {} is not mounted yet", ifName.getRemoteSysName());
173 // The controller raises a warning rather than an error because the first node to
174 // mount cannot see its neighbors yet. The link will be detected when processing
175 // the neighbor node.
177 if (!createR2RLink(nodeId, ifName.getIfName(), ifName.getRemoteSysName(),
178 ifName.getRemotePortId())) {
179 LOG.error("Link Creation failed between {} and {} nodes.", nodeId.getValue(),
180 ifName.getRemoteSysName());
189 public Direction getDegreeDirection(Integer degreeCounter, NodeId nodeId) {
190 InstanceIdentifier<Nodes> nodesIID = InstanceIdentifier.builder(Network.class)
191 .child(Nodes.class, new NodesKey(nodeId.getValue())).build();
192 try (ReadTransaction readTx = this.dataBroker.newReadOnlyTransaction()) {
193 Optional<Nodes> nodesObject = readTx.read(LogicalDatastoreType.CONFIGURATION, nodesIID).get();
194 if (nodesObject.isPresent() && (nodesObject.get().getMapping() != null)) {
195 Collection<Mapping> mappingList = nodesObject.get().nonnullMapping().values();
196 mappingList = mappingList.stream().filter(mp -> mp.getLogicalConnectionPoint().contains("DEG"
197 + degreeCounter)).collect(Collectors.toList());
198 if (mappingList.size() == 1) {
199 return Direction.Bidirectional;
200 } else if (mappingList.size() > 1) {
203 return Direction.NotApplicable;
206 } catch (InterruptedException | ExecutionException e) {
207 LOG.error("Failed getting Mapping data from portMapping",e);
209 return Direction.NotApplicable;
212 public boolean createR2RLink(NodeId nodeId, String interfaceName, String remoteSystemName,
213 String remoteInterfaceName) {
214 String srcTpTx = null;
215 String srcTpRx = null;
216 String destTpTx = null;
217 String destTpRx = null;
218 // Find which degree is associated with ethernet interface
219 Integer srcDegId = getDegFromInterface(nodeId, interfaceName);
220 if (srcDegId == null) {
221 LOG.error("Couldnt find degree connected to Ethernet interface for nodeId: {}", nodeId);
224 // Check whether degree is Unidirectional or Bidirectional by counting
226 // circuit-packs under degree subtree
227 Direction sourceDirection = getDegreeDirection(srcDegId, nodeId);
228 if (Direction.NotApplicable == sourceDirection) {
229 LOG.error("Couldnt find degree direction for nodeId: {} and degree: {}", nodeId, srcDegId);
231 } else if (Direction.Bidirectional == sourceDirection) {
232 srcTpTx = "DEG" + srcDegId + "-TTP-TXRX";
233 srcTpRx = "DEG" + srcDegId + "-TTP-TXRX";
235 srcTpTx = "DEG" + srcDegId + "-TTP-TX";
236 srcTpRx = "DEG" + srcDegId + "-TTP-RX";
238 // Find degree for which Ethernet interface is created on other end
239 NodeId destNodeId = new NodeId(remoteSystemName);
240 Integer destDegId = getDegFromInterface(destNodeId, remoteInterfaceName);
241 if (destDegId == null) {
242 LOG.error("Couldnt find degree connected to Ethernet interface for nodeId: {}", nodeId);
245 // Check whether degree is Unidirectional or Bidirectional by counting
247 // circuit-packs under degree subtree
248 Direction destinationDirection = getDegreeDirection(destDegId, destNodeId);
249 if (Direction.NotApplicable == destinationDirection) {
250 LOG.error("Couldnt find degree direction for nodeId: {} and degree: {}", destNodeId, destDegId);
252 } else if (Direction.Bidirectional == destinationDirection) {
253 destTpTx = "DEG" + destDegId + "-TTP-TXRX";
254 destTpRx = "DEG" + destDegId + "-TTP-TXRX";
256 destTpTx = "DEG" + destDegId + "-TTP-TX";
257 destTpRx = "DEG" + destDegId + "-TTP-RX";
261 "Found a neighbor SrcNodeId: {} , SrcDegId: {} , SrcTPId: {}, DestNodeId:{} , DestDegId: {}, DestTPId: {}",
262 nodeId.getValue(), srcDegId, srcTpTx, destNodeId, destDegId, destTpRx);
263 InitRoadmNodesInputBuilder r2rlinkBuilderAToZ = new InitRoadmNodesInputBuilder();
264 r2rlinkBuilderAToZ.setRdmANode(nodeId.getValue()).setDegANum(Uint8.valueOf(srcDegId))
265 .setTerminationPointA(srcTpTx).setRdmZNode(destNodeId.getValue()).setDegZNum(Uint8.valueOf(destDegId))
266 .setTerminationPointZ(destTpRx);
267 if (!OrdLink.createRdm2RdmLinks(r2rlinkBuilderAToZ.build(), this.dataBroker)) {
268 LOG.error("OMS Link creation failed between node: {} and nodeId: {} in A->Z direction", nodeId.getValue(),
269 destNodeId.getValue());
274 "Found a neighbor SrcNodeId: {} , SrcDegId: {}"
275 + ", SrcTPId: {}, DestNodeId:{} , DestDegId: {}, DestTPId: {}",
276 destNodeId, destDegId, destTpTx, nodeId.getValue(), srcDegId, srcTpRx);
278 InitRoadmNodesInputBuilder r2rlinkBuilderZToA = new InitRoadmNodesInputBuilder()
279 .setRdmANode(destNodeId.getValue())
280 .setDegANum(Uint8.valueOf(destDegId))
281 .setTerminationPointA(destTpTx)
282 .setRdmZNode(nodeId.getValue())
283 .setDegZNum(Uint8.valueOf(srcDegId))
284 .setTerminationPointZ(srcTpRx);
285 if (!OrdLink.createRdm2RdmLinks(r2rlinkBuilderZToA.build(), this.dataBroker)) {
286 LOG.error("OMS Link creation failed between node: {} and nodeId: {} in Z->A direction",
287 destNodeId.getValue(), nodeId.getValue());
293 public boolean deleteR2RLink(NodeId nodeId, String interfaceName, String remoteSystemName,
294 String remoteInterfaceName) {
295 String srcTpTx = null;
296 String srcTpRx = null;
297 String destTpTx = null;
298 String destTpRx = null;
299 // Find which degree is associated with ethernet interface
300 Integer srcDegId = getDegFromInterface(nodeId, interfaceName);
301 if (srcDegId == null) {
302 LOG.error("Couldnt find degree connected to Ethernet interface for nodeId: {}", nodeId);
305 // Check whether degree is Unidirectional or Bidirectional by counting number of
306 // circuit-packs under degree subtree
307 Direction sourceDirection = getDegreeDirection(srcDegId, nodeId);
308 if (Direction.NotApplicable == sourceDirection) {
309 LOG.error("Couldnt find degree direction for nodeId: {} and degree: {}", nodeId, srcDegId);
311 } else if (Direction.Bidirectional == sourceDirection) {
312 srcTpTx = "DEG" + srcDegId + "-TTP-TXRX";
313 srcTpRx = "DEG" + srcDegId + "-TTP-TXRX";
315 srcTpTx = "DEG" + srcDegId + "-TTP-TX";
316 srcTpRx = "DEG" + srcDegId + "-TTP-RX";
318 // Find degree for which Ethernet interface is created on other end
319 NodeId destNodeId = new NodeId(remoteSystemName);
320 Integer destDegId = getDegFromInterface(destNodeId, remoteInterfaceName);
321 if (destDegId == null) {
322 LOG.error("Couldnt find degree connected to Ethernet interface for nodeId: {}", nodeId);
325 // Check whether degree is Unidirectional or Bidirectional by counting number of
326 // circuit-packs under degree subtree
327 Direction destinationDirection = getDegreeDirection(destDegId, destNodeId);
328 if (Direction.NotApplicable == destinationDirection) {
329 LOG.error("Couldnt find degree direction for nodeId: {} and degree: {}", destNodeId, destDegId);
331 } else if (Direction.Bidirectional == destinationDirection) {
332 destTpTx = "DEG" + destDegId + "-TTP-TXRX";
333 destTpRx = "DEG" + destDegId + "-TTP-TXRX";
335 destTpTx = "DEG" + destDegId + "-TTP-TX";
336 destTpRx = "DEG" + destDegId + "-TTP-RX";
338 return TopologyUtils.deleteLink(nodeId.getValue() + "-" + srcDegId, destNodeId.getValue() + "-" + destDegId,
339 srcTpTx, destTpRx, networkTransactionService)
340 && TopologyUtils.deleteLink(destNodeId.getValue() + "-" + destDegId, nodeId.getValue() + "-" + srcDegId,
341 destTpTx, srcTpRx, networkTransactionService);
344 private Integer getDegFromInterface(NodeId nodeId, String interfaceName) {
345 InstanceIdentifier<Nodes> nodesIID = InstanceIdentifier.builder(Network.class)
346 .child(Nodes.class, new NodesKey(nodeId.getValue())).build();
347 try (ReadTransaction readTx = this.dataBroker.newReadOnlyTransaction()) {
348 Optional<Nodes> nodesObject = readTx.read(LogicalDatastoreType.CONFIGURATION, nodesIID).get();
349 if (nodesObject.isPresent() && (nodesObject.get().getCpToDegree() != null)) {
350 Collection<CpToDegree> cpToDeg = nodesObject.get().nonnullCpToDegree().values();
351 Stream<CpToDegree> cpToDegStream = cpToDeg.stream().filter(cp -> cp.getInterfaceName() != null)
352 .filter(cp -> cp.getInterfaceName().equals(interfaceName));
353 if (cpToDegStream != null) {
354 @SuppressWarnings("unchecked") Optional<CpToDegree> firstCpToDegree = cpToDegStream.findFirst();
355 if (firstCpToDegree.isPresent() && (firstCpToDegree != null)) {
356 LOG.debug("Found and returning {}",firstCpToDegree.get().getDegreeNumber().intValue());
357 return firstCpToDegree.get().getDegreeNumber().intValue();
359 LOG.debug("Not found so returning nothing");
363 LOG.warn("CircuitPack stream couldnt find anything for nodeId: {} and interfaceName: {}",
364 nodeId.getValue(),interfaceName);
367 LOG.warn("Could not find mapping for Interface {} for nodeId {}", interfaceName,
370 } catch (InterruptedException | ExecutionException ex) {
371 LOG.error("Unable to read mapping for Interface : {} for nodeId {}", interfaceName, nodeId, ex);