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