Bump upstream dependencies to Ca
[transportpce.git] / networkmodel / src / main / java / org / opendaylight / transportpce / networkmodel / NetConfTopologyListener.java
1 /*
2  * Copyright © 2016 Orange and others.  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.networkmodel;
9
10 import com.google.common.annotations.VisibleForTesting;
11 import com.google.common.util.concurrent.ListenableFuture;
12 import java.util.ArrayList;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Optional;
16 import java.util.concurrent.ConcurrentHashMap;
17 import java.util.concurrent.ExecutionException;
18 import org.opendaylight.mdsal.binding.api.DataBroker;
19 import org.opendaylight.mdsal.binding.api.DataObjectModification;
20 import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
21 import org.opendaylight.mdsal.binding.api.DataTreeModification;
22 import org.opendaylight.mdsal.binding.api.MountPoint;
23 import org.opendaylight.mdsal.binding.api.NotificationService;
24 import org.opendaylight.mdsal.binding.api.RpcService;
25 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
26 import org.opendaylight.transportpce.common.StringConstants;
27 import org.opendaylight.transportpce.common.Timeouts;
28 import org.opendaylight.transportpce.common.device.DeviceTransactionManager;
29 import org.opendaylight.transportpce.common.mapping.PortMapping;
30 import org.opendaylight.transportpce.networkmodel.dto.NodeRegistration;
31 import org.opendaylight.transportpce.networkmodel.service.NetworkModelService;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.CreateSubscription;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.CreateSubscriptionInputBuilder;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.CreateSubscriptionOutput;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.StreamNameType;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netmod.notification.rev080714.Netconf;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netmod.notification.rev080714.netconf.Streams;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netmod.notification.rev080714.netconf.streams.Stream;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.ConnectionOper.ConnectionStatus;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.connection.oper.available.capabilities.AvailableCapability;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev231121.NetconfNode;
42 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
43 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
44 import org.opendaylight.yangtools.yang.common.RpcResult;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 public class NetConfTopologyListener implements DataTreeChangeListener<Node> {
49
50     private static final Logger LOG = LoggerFactory.getLogger(NetConfTopologyListener.class);
51     private static final String RPC_SERVICE_FAILED = "Failed to get RpcService for node {}";
52     private final NetworkModelService networkModelService;
53     private final DataBroker dataBroker;
54     private final DeviceTransactionManager deviceTransactionManager;
55     private final Map<String, NodeRegistration> registrations;
56     private final PortMapping portMapping;
57
58     public NetConfTopologyListener(
59             final NetworkModelService networkModelService,
60             final DataBroker dataBroker,
61             DeviceTransactionManager deviceTransactionManager,
62             PortMapping portMapping) {
63         this.networkModelService = networkModelService;
64         this.dataBroker = dataBroker;
65         this.deviceTransactionManager = deviceTransactionManager;
66         this.registrations = new ConcurrentHashMap<>();
67         this.portMapping = portMapping;
68     }
69
70     @Override
71     public void onDataTreeChanged(List<DataTreeModification<Node>> changes) {
72         LOG.info("onDataTreeChanged - {}", this.getClass().getSimpleName());
73         for (DataTreeModification<Node> change : changes) {
74             DataObjectModification<Node> rootNode = change.getRootNode();
75             if (rootNode.dataBefore() == null) {
76                 continue;
77             }
78             String nodeId = rootNode.dataBefore().key().getNodeId().getValue();
79             NetconfNode netconfNodeBefore = rootNode.dataBefore().augmentation(NetconfNode.class);
80             switch (rootNode.modificationType()) {
81                 case DELETE:
82                     if (this.networkModelService.deleteOpenRoadmnode(nodeId)) {
83                         onDeviceDisConnected(nodeId);
84                         LOG.info("Device {} correctly disconnected from controller", nodeId);
85                     }
86                     break;
87                 case WRITE:
88                     NetconfNode netconfNodeAfter = rootNode.dataAfter().augmentation(NetconfNode.class);
89                     if (ConnectionStatus.Connecting.equals(netconfNodeBefore.getConnectionStatus())
90                             && ConnectionStatus.Connected.equals(netconfNodeAfter.getConnectionStatus())) {
91                         LOG.info("Connecting Node: {}", nodeId);
92                         Optional<AvailableCapability> deviceCapabilityOpt =
93                             netconfNodeAfter.getAvailableCapabilities().getAvailableCapability().stream()
94                                 .filter(cp -> cp.getCapability().contains(StringConstants.OPENROADM_DEVICE_MODEL_NAME))
95                                 .sorted((c1, c2) -> c2.getCapability().compareTo(c1.getCapability()))
96                                 .findFirst();
97                         if (deviceCapabilityOpt.isEmpty()) {
98                             LOG.error("Unable to get openroadm-device-capability");
99                             return;
100                         }
101                         this.networkModelService
102                             .createOpenRoadmNode(nodeId, deviceCapabilityOpt.orElseThrow().getCapability());
103                         onDeviceConnected(nodeId, deviceCapabilityOpt.orElseThrow().getCapability());
104                         LOG.info("Device {} correctly connected to controller", nodeId);
105                     }
106                     if (ConnectionStatus.Connected.equals(netconfNodeBefore.getConnectionStatus())
107                             && ConnectionStatus.Connecting.equals(netconfNodeAfter.getConnectionStatus())) {
108                         LOG.warn("Node: {} is being disconnected", nodeId);
109                     }
110                     break;
111                 default:
112                     LOG.debug("Unknown modification type {}", rootNode.modificationType().name());
113                     break;
114             }
115         }
116     }
117
118     private void onDeviceConnected(final String nodeId, String openRoadmVersion) {
119         LOG.info("onDeviceConnected: {}", nodeId);
120         Optional<MountPoint> mountPointOpt = this.deviceTransactionManager.getDeviceMountPoint(nodeId);
121         if (mountPointOpt.isEmpty()) {
122             LOG.error("Failed to get mount point for node {}", nodeId);
123             return;
124         }
125         MountPoint mountPoint = mountPointOpt.orElseThrow();
126         final Optional<NotificationService> notificationService = mountPoint.getService(NotificationService.class);
127         if (notificationService.isEmpty()) {
128             LOG.error(RPC_SERVICE_FAILED, nodeId);
129             return;
130         }
131         NodeRegistration nodeRegistration =
132             new NodeRegistration(
133                 nodeId, openRoadmVersion, notificationService.orElseThrow(), this.dataBroker, this.portMapping);
134         nodeRegistration.registerListeners();
135         registrations.put(nodeId, nodeRegistration);
136
137         subscribeStream(mountPoint, nodeId);
138     }
139
140     private void onDeviceDisConnected(final String nodeId) {
141         LOG.info("onDeviceDisConnected: {}", nodeId);
142         this.registrations.remove(nodeId).unregisterListeners();
143     }
144
145     private boolean subscribeStream(MountPoint mountPoint, String nodeId) {
146         final Optional<RpcService> service = mountPoint.getService(RpcService.class);
147         if (service.isEmpty()) {
148             return false;
149         }
150         final CreateSubscription rpcService = service.orElseThrow().getRpc(CreateSubscription.class);
151         if (rpcService == null) {
152             LOG.error(RPC_SERVICE_FAILED, nodeId);
153             return false;
154         }
155         // Set the default stream as OPENROADM
156         for (String streamName : getSupportedStream(nodeId)) {
157             LOG.info("Triggering notification stream {} for node {}", streamName, nodeId);
158             ListenableFuture<RpcResult<CreateSubscriptionOutput>> subscription = rpcService.invoke(
159                     new CreateSubscriptionInputBuilder().setStream(new StreamNameType(streamName)).build());
160             if (checkSupportedStream(streamName, subscription)) {
161                 return true;
162             }
163         }
164         return false;
165     }
166
167     @VisibleForTesting
168     public NetConfTopologyListener(
169             final NetworkModelService networkModelService,
170             final DataBroker dataBroker,
171             DeviceTransactionManager deviceTransactionManager,
172             PortMapping portMapping,
173             Map<String, NodeRegistration> registrations) {
174         this.networkModelService = networkModelService;
175         this.dataBroker = dataBroker;
176         this.deviceTransactionManager = deviceTransactionManager;
177         this.portMapping = portMapping;
178         this.registrations = registrations;
179     }
180
181     private boolean checkSupportedStream(
182             String streamName,
183             ListenableFuture<RpcResult<CreateSubscriptionOutput>> subscription) {
184         boolean subscriptionSuccessful = false;
185         try {
186             // Using if condition does not work, since we need to handle exceptions
187             subscriptionSuccessful = subscription.get().isSuccessful();
188             LOG.info("{} subscription is {}", streamName, subscriptionSuccessful);
189         } catch (InterruptedException | ExecutionException e) {
190             LOG.error("Error during subscription to stream {}", streamName, e);
191         }
192         return subscriptionSuccessful;
193     }
194
195     private List<String> getSupportedStream(String nodeId) {
196         InstanceIdentifier<Streams> streamsIID = InstanceIdentifier.create(Netconf.class).child(Streams.class);
197         Optional<Streams> ordmInfoObject =
198                 deviceTransactionManager.getDataFromDevice(nodeId, LogicalDatastoreType.OPERATIONAL, streamsIID,
199                         Timeouts.DEVICE_READ_TIMEOUT, Timeouts.DEVICE_READ_TIMEOUT_UNIT);
200         if (ordmInfoObject == null || ordmInfoObject.isEmpty() || ordmInfoObject.orElseThrow().getStream().isEmpty()) {
201             LOG.error("List of streams supports by device is not present");
202             return List.of("OPENROADM","NETCONF");
203         }
204         List<String> streams = new ArrayList<>();
205         List<String> netconfStreams = new ArrayList<>();
206         for (Stream strm : ordmInfoObject.orElseThrow().getStream().values()) {
207             LOG.debug("Streams are {}", strm);
208             if ("OPENROADM".equalsIgnoreCase(strm.getName().getValue())) {
209                 streams.add(strm.getName().getValue());
210             } else if ("NETCONF".equalsIgnoreCase(strm.getName().getValue())) {
211                 netconfStreams.add(strm.getName().getValue());
212             }
213         }
214         // If OpenROADM streams are not supported, try NETCONF streams subscription
215         streams.addAll(netconfStreams);
216         return
217             streams.isEmpty()
218                 ? List.of("OPENROADM","NETCONF")
219                 : streams;
220     }
221
222 }