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.rev231221.Network;
29 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev231221.cp.to.degree.CpToDegree;
30 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev231221.mapping.Mapping;
31 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev231221.network.Nodes;
32 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev231221.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.orElseThrow().augmentation(Protocols1.class).getLldp()
78 LOG.info("LLDP subtree is present. Device has {} neighbours", nbr121List.getIfName().size());
79 // try to create rdm2rdm link
80 return rdm2rdmLinkCreatedv121(nodeId, nbr121List);
81 case OPENROADM_DEVICE_VERSION_2_2_1:
82 InstanceIdentifier<org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev181019.org.openroadm.device
83 .container.org.openroadm.device.Protocols> protocols221IID = InstanceIdentifier
85 org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev181019.OrgOpenroadmDeviceData.class,
86 org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev181019.org.openroadm.device.container
87 .OrgOpenroadmDevice.class)
89 org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev181019.org.openroadm.device.container
90 .org.openroadm.device.Protocols.class)
92 var protocol221Object = this.deviceTransactionManager
93 .getDataFromDevice(nodeId.getValue(), LogicalDatastoreType.OPERATIONAL, protocols221IID,
94 Timeouts.DEVICE_READ_TIMEOUT, Timeouts.DEVICE_READ_TIMEOUT_UNIT);
95 if (hasNoNeighbor221(protocol221Object)) {
96 LOG.warn("LLDP subtree is missing or incomplete: isolated openroadm device");
99 var nbr221List = protocol221Object.orElseThrow().augmentation(
100 org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev181019.Protocols1.class)
101 .getLldp().getNbrList();
102 LOG.info("LLDP subtree is present. Device has {} neighbours", nbr221List.getIfName().size());
103 return rdm2rdmLinkCreatedv221(nodeId, nbr221List);
104 case OPENROADM_DEVICE_VERSION_7_1:
105 LOG.info("Not yet implemented?");
108 LOG.error("Unable to read LLDP data for unmanaged openroadm device version");
113 private boolean hasNoNeighbor121(Optional<Protocols> protocol121Object) {
114 return protocol121Object.isEmpty()
115 || protocol121Object.orElseThrow().augmentation(Protocols1.class) == null
116 || protocol121Object.orElseThrow().augmentation(Protocols1.class).getLldp() == null
117 || protocol121Object.orElseThrow().augmentation(Protocols1.class).getLldp().getNbrList() == null;
120 private boolean hasNoNeighbor221(Optional<
121 org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev181019.org.openroadm.device.container.org
122 .openroadm.device.Protocols> protocol221Object) {
123 return protocol221Object.isEmpty()
124 || protocol221Object.orElseThrow().augmentation(
125 org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev181019.Protocols1.class) == null
126 || protocol221Object.orElseThrow().augmentation(
127 org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev181019.Protocols1.class)
129 || protocol221Object.orElseThrow().augmentation(
130 org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev181019.Protocols1.class)
131 .getLldp().getNbrList() == null;
134 private boolean rdm2rdmLinkCreatedv221(NodeId nodeId,
135 org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev181019.lldp.container.lldp.NbrList nbrList) {
136 boolean success = true;
137 for (org.opendaylight.yang.gen.v1.http.org.openroadm.lldp.rev181019.lldp.container.lldp.nbr.list.IfName
138 ifName : nbrList.nonnullIfName().values()) {
139 if (ifName.getRemoteSysName() == null) {
140 LOG.warn("LLDP subtree neighbour is empty for nodeId: {}, ifName: {}",
141 nodeId.getValue(),ifName.getIfName());
143 Optional<MountPoint> mps = this.deviceTransactionManager.getDeviceMountPoint(ifName
144 .getRemoteSysName());
145 if (!mps.isPresent()) {
146 LOG.warn("Neighbouring nodeId: {} is not mounted yet", ifName.getRemoteSysName());
147 // The controller raises a warning rather than an error because the first node to
148 // mount cannot see its neighbors yet. The link will be detected when processing
149 // the neighbor node.
151 if (!createR2RLink(nodeId, ifName.getIfName(), ifName.getRemoteSysName(),
152 ifName.getRemotePortId())) {
153 LOG.error("Link Creation failed between {} and {} nodes.", nodeId, ifName
154 .getRemoteSysName());
163 private boolean rdm2rdmLinkCreatedv121(NodeId nodeId, NbrList nbrList) {
164 boolean success = true;
165 for (IfName ifName : nbrList.nonnullIfName().values()) {
166 if (ifName.getRemoteSysName() == null) {
167 LOG.warn("LLDP subtree neighbour is empty for nodeId: {}, ifName: {}",
168 nodeId.getValue(),ifName.getIfName());
170 Optional<MountPoint> mps = this.deviceTransactionManager.getDeviceMountPoint(ifName
171 .getRemoteSysName());
172 if (!mps.isPresent()) {
173 LOG.warn("Neighbouring nodeId: {} is not mounted yet", ifName.getRemoteSysName());
174 // The controller raises a warning rather than an error because the first node to
175 // mount cannot see its neighbors yet. The link will be detected when processing
176 // the neighbor node.
178 if (!createR2RLink(nodeId, ifName.getIfName(), ifName.getRemoteSysName(),
179 ifName.getRemotePortId())) {
180 LOG.error("Link Creation failed between {} and {} nodes.", nodeId.getValue(),
181 ifName.getRemoteSysName());
190 public Direction getDegreeDirection(Integer degreeCounter, NodeId nodeId) {
191 InstanceIdentifier<Nodes> nodesIID = InstanceIdentifier.builder(Network.class)
192 .child(Nodes.class, new NodesKey(nodeId.getValue())).build();
193 try (ReadTransaction readTx = this.dataBroker.newReadOnlyTransaction()) {
194 Optional<Nodes> nodesObject = readTx.read(LogicalDatastoreType.CONFIGURATION, nodesIID).get();
195 if (nodesObject.isPresent() && (nodesObject.orElseThrow().getMapping() != null)) {
196 Collection<Mapping> mappingList = nodesObject.orElseThrow().nonnullMapping().values();
197 mappingList = mappingList.stream().filter(mp -> mp.getLogicalConnectionPoint().contains("DEG"
198 + degreeCounter)).collect(Collectors.toList());
199 if (mappingList.size() == 1) {
200 return Direction.Bidirectional;
201 } else if (mappingList.size() > 1) {
204 return Direction.NotApplicable;
207 } catch (InterruptedException | ExecutionException e) {
208 LOG.error("Failed getting Mapping data from portMapping",e);
210 return Direction.NotApplicable;
213 public boolean createR2RLink(NodeId nodeId, String interfaceName, String remoteSystemName,
214 String remoteInterfaceName) {
215 String srcTpTx = null;
216 String srcTpRx = null;
217 String destTpTx = null;
218 String destTpRx = null;
219 // Find which degree is associated with ethernet interface
220 Integer srcDegId = getDegFromInterface(nodeId, interfaceName);
221 if (srcDegId == null) {
222 LOG.error("Couldnt find degree connected to Ethernet interface for nodeId: {}", nodeId);
225 // Check whether degree is Unidirectional or Bidirectional by counting
227 // circuit-packs under degree subtree
228 Direction sourceDirection = getDegreeDirection(srcDegId, nodeId);
229 if (Direction.NotApplicable == sourceDirection) {
230 LOG.error("Couldnt find degree direction for nodeId: {} and degree: {}", nodeId, srcDegId);
232 } else if (Direction.Bidirectional == sourceDirection) {
233 srcTpTx = "DEG" + srcDegId + "-TTP-TXRX";
234 srcTpRx = "DEG" + srcDegId + "-TTP-TXRX";
236 srcTpTx = "DEG" + srcDegId + "-TTP-TX";
237 srcTpRx = "DEG" + srcDegId + "-TTP-RX";
239 // Find degree for which Ethernet interface is created on other end
240 NodeId destNodeId = new NodeId(remoteSystemName);
241 Integer destDegId = getDegFromInterface(destNodeId, remoteInterfaceName);
242 if (destDegId == null) {
243 LOG.error("Couldnt find degree connected to Ethernet interface for nodeId: {}", nodeId);
246 // Check whether degree is Unidirectional or Bidirectional by counting
248 // circuit-packs under degree subtree
249 Direction destinationDirection = getDegreeDirection(destDegId, destNodeId);
250 if (Direction.NotApplicable == destinationDirection) {
251 LOG.error("Couldnt find degree direction for nodeId: {} and degree: {}", destNodeId, destDegId);
253 } else if (Direction.Bidirectional == destinationDirection) {
254 destTpTx = "DEG" + destDegId + "-TTP-TXRX";
255 destTpRx = "DEG" + destDegId + "-TTP-TXRX";
257 destTpTx = "DEG" + destDegId + "-TTP-TX";
258 destTpRx = "DEG" + destDegId + "-TTP-RX";
262 "Found a neighbor SrcNodeId: {} , SrcDegId: {} , SrcTPId: {}, DestNodeId:{} , DestDegId: {}, DestTPId: {}",
263 nodeId.getValue(), srcDegId, srcTpTx, destNodeId, destDegId, destTpRx);
264 InitRoadmNodesInputBuilder r2rlinkBuilderAToZ = new InitRoadmNodesInputBuilder();
265 r2rlinkBuilderAToZ.setRdmANode(nodeId.getValue()).setDegANum(Uint8.valueOf(srcDegId))
266 .setTerminationPointA(srcTpTx).setRdmZNode(destNodeId.getValue()).setDegZNum(Uint8.valueOf(destDegId))
267 .setTerminationPointZ(destTpRx);
268 if (!OrdLink.createRdm2RdmLinks(r2rlinkBuilderAToZ.build(), this.dataBroker)) {
269 LOG.error("OMS Link creation failed between node: {} and nodeId: {} in A->Z direction", nodeId.getValue(),
270 destNodeId.getValue());
275 "Found a neighbor SrcNodeId: {} , SrcDegId: {}"
276 + ", SrcTPId: {}, DestNodeId:{} , DestDegId: {}, DestTPId: {}",
277 destNodeId, destDegId, destTpTx, nodeId.getValue(), srcDegId, srcTpRx);
279 InitRoadmNodesInputBuilder r2rlinkBuilderZToA = new InitRoadmNodesInputBuilder()
280 .setRdmANode(destNodeId.getValue())
281 .setDegANum(Uint8.valueOf(destDegId))
282 .setTerminationPointA(destTpTx)
283 .setRdmZNode(nodeId.getValue())
284 .setDegZNum(Uint8.valueOf(srcDegId))
285 .setTerminationPointZ(srcTpRx);
286 if (!OrdLink.createRdm2RdmLinks(r2rlinkBuilderZToA.build(), this.dataBroker)) {
287 LOG.error("OMS Link creation failed between node: {} and nodeId: {} in Z->A direction",
288 destNodeId.getValue(), nodeId.getValue());
294 public boolean deleteR2RLink(NodeId nodeId, String interfaceName, String remoteSystemName,
295 String remoteInterfaceName) {
296 String srcTpTx = null;
297 String srcTpRx = null;
298 String destTpTx = null;
299 String destTpRx = null;
300 // Find which degree is associated with ethernet interface
301 Integer srcDegId = getDegFromInterface(nodeId, interfaceName);
302 if (srcDegId == null) {
303 LOG.error("Couldnt find degree connected to Ethernet interface for nodeId: {}", nodeId);
306 // Check whether degree is Unidirectional or Bidirectional by counting number of
307 // circuit-packs under degree subtree
308 Direction sourceDirection = getDegreeDirection(srcDegId, nodeId);
309 if (Direction.NotApplicable == sourceDirection) {
310 LOG.error("Couldnt find degree direction for nodeId: {} and degree: {}", nodeId, srcDegId);
312 } else if (Direction.Bidirectional == sourceDirection) {
313 srcTpTx = "DEG" + srcDegId + "-TTP-TXRX";
314 srcTpRx = "DEG" + srcDegId + "-TTP-TXRX";
316 srcTpTx = "DEG" + srcDegId + "-TTP-TX";
317 srcTpRx = "DEG" + srcDegId + "-TTP-RX";
319 // Find degree for which Ethernet interface is created on other end
320 NodeId destNodeId = new NodeId(remoteSystemName);
321 Integer destDegId = getDegFromInterface(destNodeId, remoteInterfaceName);
322 if (destDegId == null) {
323 LOG.error("Couldnt find degree connected to Ethernet interface for nodeId: {}", nodeId);
326 // Check whether degree is Unidirectional or Bidirectional by counting number of
327 // circuit-packs under degree subtree
328 Direction destinationDirection = getDegreeDirection(destDegId, destNodeId);
329 if (Direction.NotApplicable == destinationDirection) {
330 LOG.error("Couldnt find degree direction for nodeId: {} and degree: {}", destNodeId, destDegId);
332 } else if (Direction.Bidirectional == destinationDirection) {
333 destTpTx = "DEG" + destDegId + "-TTP-TXRX";
334 destTpRx = "DEG" + destDegId + "-TTP-TXRX";
336 destTpTx = "DEG" + destDegId + "-TTP-TX";
337 destTpRx = "DEG" + destDegId + "-TTP-RX";
339 return TopologyUtils.deleteLink(nodeId.getValue() + "-" + srcDegId, destNodeId.getValue() + "-" + destDegId,
340 srcTpTx, destTpRx, networkTransactionService)
341 && TopologyUtils.deleteLink(destNodeId.getValue() + "-" + destDegId, nodeId.getValue() + "-" + srcDegId,
342 destTpTx, srcTpRx, networkTransactionService);
345 private Integer getDegFromInterface(NodeId nodeId, String interfaceName) {
346 InstanceIdentifier<Nodes> nodesIID = InstanceIdentifier.builder(Network.class)
347 .child(Nodes.class, new NodesKey(nodeId.getValue())).build();
348 try (ReadTransaction readTx = this.dataBroker.newReadOnlyTransaction()) {
349 Optional<Nodes> nodesObject = readTx.read(LogicalDatastoreType.CONFIGURATION, nodesIID).get();
350 if (nodesObject.isPresent() && (nodesObject.orElseThrow().getCpToDegree() != null)) {
351 Collection<CpToDegree> cpToDeg = nodesObject.orElseThrow().nonnullCpToDegree().values();
352 Stream<CpToDegree> cpToDegStream = cpToDeg.stream().filter(cp -> cp.getInterfaceName() != null)
353 .filter(cp -> cp.getInterfaceName().equals(interfaceName));
354 if (cpToDegStream != null) {
355 @SuppressWarnings("unchecked") Optional<CpToDegree> firstCpToDegree = cpToDegStream.findFirst();
356 if (firstCpToDegree.isPresent() && (firstCpToDegree != null)) {
357 LOG.debug("Found and returning {}",firstCpToDegree.orElseThrow().getDegreeNumber().intValue());
358 return firstCpToDegree.orElseThrow().getDegreeNumber().intValue();
360 LOG.debug("Not found so returning nothing");
364 LOG.warn("CircuitPack stream couldnt find anything for nodeId: {} and interfaceName: {}",
365 nodeId.getValue(),interfaceName);
368 LOG.warn("Could not find mapping for Interface {} for nodeId {}", interfaceName,
371 } catch (InterruptedException | ExecutionException ex) {
372 LOG.error("Unable to read mapping for Interface : {} for nodeId {}", interfaceName, nodeId, ex);