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