Merge "Endpoints"
[groupbasedpolicy.git] / renderers / ios-xe / src / main / java / org / opendaylight / groupbasedpolicy / renderer / ios_xe_provider / impl / manager / NodeManager.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. 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
9 package org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager;
10
11 import com.google.common.base.Optional;
12 import com.google.common.base.Preconditions;
13 import com.google.common.util.concurrent.CheckedFuture;
14 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
15 import org.opendaylight.controller.md.sal.binding.api.MountPoint;
16 import org.opendaylight.controller.md.sal.binding.api.MountPointService;
17 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
18 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
19 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
20 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
21 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer.NodeWriter;
22 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.RendererName;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.nodes.RendererNode;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.nodes.RendererNodeBuilder;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.nodes.RendererNodeKey;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeConnectionStatus;
29 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
30 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
31 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
32 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
33 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
34 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
35 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
36 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 import java.util.Arrays;
41 import java.util.List;
42
43 import static org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeConnectionStatus.ConnectionStatus.Connected;
44 import static org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeConnectionStatus.ConnectionStatus.Connecting;
45 import static org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeConnectionStatus.ConnectionStatus.UnableToConnect;
46
47 public class NodeManager {
48
49     public static final RendererName iosXeRenderer = new RendererName("ios-xe-renderer");
50     private static final TopologyId TOPOLOGY_ID = new TopologyId("topology-netconf");
51     private static final Logger LOG = LoggerFactory.getLogger(NodeManager.class);
52     private final DataBroker dataBroker;
53     private final MountPointService mountService;
54     private final List<String> requiredCapabilities;
55
56     public NodeManager(final DataBroker dataBroker, BindingAwareBroker.ProviderContext session) {
57         this.dataBroker = Preconditions.checkNotNull(dataBroker);
58         mountService = Preconditions.checkNotNull(session.getSALService(MountPointService.class));
59         requiredCapabilities = new RequiredCapabilities().initializeRequiredCapabilities();
60     }
61
62     public void syncNodes(Node dataAfter, Node dataBefore) {
63         // New node
64         if (dataBefore == null && dataAfter != null) {
65             createNode(dataAfter);
66         }
67         // Connected/disconnected node
68         if (dataBefore != null && dataAfter != null) {
69             updateNode(dataAfter);
70         }
71         // Removed node
72         if (dataBefore != null && dataAfter == null) {
73             removeNode(dataBefore);
74         }
75     }
76
77     private void createNode(Node node) {
78         LOG.info("Registering new node {}", node.getNodeId().getValue());
79         NetconfNode netconfNode = getNodeAugmentation(node);
80         if (netconfNode == null) {
81             return;
82         }
83         NetconfNodeConnectionStatus.ConnectionStatus connectionStatus = netconfNode.getConnectionStatus();
84         switch (connectionStatus) {
85             case Connecting: {
86                 LOG.info("Connecting device {} ...", node.getNodeId().getValue());
87                 break;
88             }
89             case Connected: {
90                 resolveConnectedNode(node, netconfNode);
91                 LOG.info("Node {} is ready, added to available nodes for IOS-XE Renderer", node.getNodeId().getValue());
92             }
93             case UnableToConnect: {
94                 LOG.info("Unable to connect device {}", node.getNodeId().getValue());
95                 break;
96             }
97         }
98     }
99
100     private void updateNode(Node node) {
101         NetconfNode netconfNode = getNodeAugmentation(node);
102         if (netconfNode == null || netconfNode.getConnectionStatus() == null) {
103             return;
104         }
105         NetconfNodeConnectionStatus.ConnectionStatus afterNodeStatus = netconfNode.getConnectionStatus();
106         if (afterNodeStatus.equals(Connected)) {
107             resolveConnectedNode(node, netconfNode);
108             LOG.info("Node {} is ready, added to available nodes for IOS-XE Renderer", node.getNodeId().getValue());
109         }
110         if (afterNodeStatus.equals(Connecting)) {
111             resolveDisconnectedNode(node);
112             LOG.info("Node {} has been disconnected, removing from available nodes", node.getNodeId().getValue());
113         }
114         if (afterNodeStatus.equals(UnableToConnect)) {
115             resolveDisconnectedNode(node);
116             LOG.info("Unable to connect node {}, removing from available nodes", node.getNodeId().getValue());
117         }
118     }
119
120     private void removeNode(Node node) {
121         resolveDisconnectedNode(node);
122         LOG.info("Node {} has been removed", node.getNodeId().getValue());
123     }
124
125     private void resolveConnectedNode(Node node, NetconfNode netconfNode) {
126         InstanceIdentifier mountPointIid = getMountpointIid(node);
127         // Mountpoint iid == path in renderer-node
128         RendererNode rendererNode = remapNode(mountPointIid);
129         NodeWriter nodeWriter = new NodeWriter();
130         nodeWriter.cache(rendererNode);
131         if (isCapableNetconfDevice(node, netconfNode)) {
132             resolveDisconnectedNode(node);
133             return;
134         }
135         IpAddress managementIpAddress = netconfNode.getHost().getIpAddress();
136         if (managementIpAddress == null) {
137             LOG.warn("Node {} does not contain management ip address", node.getNodeId().getValue());
138             resolveDisconnectedNode(node);
139             return;
140         }
141         nodeWriter.commitToDatastore(dataBroker);
142     }
143
144     private void resolveDisconnectedNode(Node node) {
145         InstanceIdentifier mountPointIid = getMountpointIid(node);
146         RendererNode rendererNode = remapNode(mountPointIid);
147         NodeWriter nodeWriter = new NodeWriter();
148         nodeWriter.cache(rendererNode);
149         nodeWriter.removeFromDatastore(dataBroker);
150     }
151
152     private RendererNode remapNode(InstanceIdentifier path) {
153         RendererNodeBuilder rendererNodeBuilder = new RendererNodeBuilder();
154         rendererNodeBuilder.setKey(new RendererNodeKey(path))
155                 .setNodePath(path);
156         return rendererNodeBuilder.build();
157     }
158
159     private InstanceIdentifier getMountpointIid(Node node) {
160         return InstanceIdentifier.builder(NetworkTopology.class)
161                 .child(Topology.class, new TopologyKey(TOPOLOGY_ID))
162                 .child(Node.class, new NodeKey(node.getNodeId())).build();
163     }
164
165     private boolean isCapableNetconfDevice(Node node, NetconfNode netconfAugmentation) {
166         if (netconfAugmentation.getAvailableCapabilities() == null ||
167                 netconfAugmentation.getAvailableCapabilities().getAvailableCapability() == null ||
168                 netconfAugmentation.getAvailableCapabilities().getAvailableCapability().isEmpty()) {
169             LOG.warn("Node {} does not contain any capabilities", node.getNodeId().getValue());
170             return true;
171         }
172         if (!capabilityCheck(netconfAugmentation.getAvailableCapabilities().getAvailableCapability())) {
173             LOG.warn("Node {} does not contain all capabilities required by io-xe-renderer",
174                     node.getNodeId().getValue());
175             return true;
176         }
177         return false;
178     }
179
180     private boolean capabilityCheck(final List<String> capabilities) {
181         for (String requiredCapability : requiredCapabilities) {
182             if (!capabilities.contains(requiredCapability)) {
183                 return false;
184             }
185         }
186         return true;
187     }
188
189     DataBroker getNodeMountPoint(InstanceIdentifier mountPointIid) {
190         if (mountPointIid == null) {
191             return null;
192         }
193         Optional<MountPoint> optionalObject = mountService.getMountPoint(mountPointIid);
194         MountPoint mountPoint;
195         if (optionalObject.isPresent()) {
196             mountPoint = optionalObject.get();
197             if (mountPoint != null) {
198                 Optional<DataBroker> optionalDataBroker = mountPoint.getService(DataBroker.class);
199                 if (optionalDataBroker.isPresent()) {
200                     return optionalDataBroker.get();
201                 } else {
202                     LOG.debug("Cannot obtain data broker from mountpoint {}", mountPoint);
203                 }
204             } else {
205                 LOG.debug("Cannot obtain mountpoint with IID {}", mountPointIid);
206             }
207         }
208         return null;
209     }
210
211     NodeId getNodeIdByMountpointIid(InstanceIdentifier mountpointIid) {
212         NodeKey identifier = (NodeKey) mountpointIid.firstKeyOf(Node.class);
213         return identifier.getNodeId();
214     }
215
216     String getNodeManagementIpByMountPointIid(InstanceIdentifier mountpointIid) {
217         NodeId nodeId = getNodeIdByMountpointIid(mountpointIid);
218         InstanceIdentifier<Node> nodeIid = InstanceIdentifier.builder(NetworkTopology.class)
219                 .child(Topology.class, new TopologyKey(new TopologyId(NodeManager.TOPOLOGY_ID)))
220                 .child(Node.class, new NodeKey(nodeId)).build();
221         ReadWriteTransaction rwt = dataBroker.newReadWriteTransaction();
222         try {
223             CheckedFuture<Optional<Node>, ReadFailedException> submitFuture =
224                     rwt.read(LogicalDatastoreType.CONFIGURATION, nodeIid);
225             Optional<Node> optional = submitFuture.checkedGet();
226             if (optional != null && optional.isPresent()) {
227                 Node node = optional.get();
228                 if (node != null) {
229                     NetconfNode netconfNode = getNodeAugmentation(node);
230                     if (netconfNode != null && netconfNode.getHost() != null) {
231                         IpAddress ipAddress = netconfNode.getHost().getIpAddress();
232                         if (ipAddress != null && ipAddress.getIpv4Address() != null) {
233                             return ipAddress.getIpv4Address().getValue();
234                         }
235                         return null;
236                     }
237                 }
238             } else {
239                 LOG.debug("Failed to read. {}", Thread.currentThread().getStackTrace()[1]);
240             }
241         } catch (ReadFailedException e) {
242             LOG.warn("Read transaction failed to {} ", e);
243         } catch (Exception e) {
244             LOG.error("Failed to .. {}", e.getMessage());
245         }
246         return null;
247     }
248
249     private NetconfNode getNodeAugmentation(Node node) {
250         NetconfNode netconfNode = node.getAugmentation(NetconfNode.class);
251         if (netconfNode == null) {
252             LOG.warn("Node {} is not a netconf device", node.getNodeId().getValue());
253             return null;
254         }
255         return netconfNode;
256     }
257
258     private class RequiredCapabilities {
259
260         private static final String ned = "(urn:ios?revision=2016-03-08)ned";
261         private static final String tailfCommon = "(http://tail-f.com/yang/common?revision=2015-05-22)tailf-common";
262         private static final String tailfCliExtension = "(http://tail-f.com/yang/common?revision=2015-03-19)tailf-cli-extensions";
263         private static final String tailfMetaExtension = "(http://tail-f.com/yang/common?revision=2013-11-07)tailf-meta-extensions";
264         private static final String ietfYangTypes = "(urn:ietf:params:xml:ns:yang:ietf-yang-types?revision=2013-07-15)ietf-yang-types";
265         private static final String ietfInetTypes = "(urn:ietf:params:xml:ns:yang:ietf-inet-types?revision=2013-07-15)ietf-inet-types";
266
267         /**
268          * Initialize all common capabilities required by IOS-XE renderer. Any connected node is examined whether it's
269          * an appropriate device to handle configuration created by this renderer. A device must support all capabilities
270          * in list below.
271          *
272          * @return list of string representations of required capabilities
273          */
274         List<String> initializeRequiredCapabilities() {
275             String capabilityEntries[] = {ned, tailfCommon, tailfCliExtension, tailfMetaExtension, ietfYangTypes,
276                     ietfInetTypes};
277             return Arrays.asList(capabilityEntries);
278         }
279     }
280 }