Fixup Augmentable and Identifiable methods changing
[neutron.git] / neutron-hostconfig / vpp / src / main / java / org / opendaylight / neutron / hostconfig / vpp / NeutronHostconfigVppListener.java
1 /*
2  * Copyright (c) 2017 Intel Corporation. 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.neutron.hostconfig.vpp;
10
11 import com.google.common.base.Preconditions;
12 import java.net.URI;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.List;
16 import java.util.Locale;
17 import java.util.Map;
18 import java.util.concurrent.ExecutorService;
19 import java.util.concurrent.Executors;
20 import java.util.stream.Collectors;
21 import javax.annotation.Nonnull;
22 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
23 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
24 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
25 import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
26 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
27 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
28 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
29 import org.opendaylight.neutron.hostconfig.utils.NeutronHostconfigUtils;
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.NetconfNodeConnectionStatus;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapability;
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.TopologyId;
35 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
36 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
37 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
38 import org.opendaylight.yangtools.concepts.ListenerRegistration;
39 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
40 import org.opendaylight.yangtools.yang.common.QName;
41 import org.opendaylight.yangtools.yang.common.Revision;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 public class NeutronHostconfigVppListener implements ClusteredDataTreeChangeListener<Node> {
46
47     private static final Logger LOG = LoggerFactory.getLogger(NeutronHostconfigVppListener.class);
48     private final DataBroker dataBroker;
49     private final NeutronHostconfigUtils neutronHostconfig;
50     private ListenerRegistration<DataTreeChangeListener<Node>> listenerRegistration;
51     private final ExecutorService executorService = Executors.newFixedThreadPool(1);
52
53     private static final TopologyId TOPOLOGY_NETCONF = new TopologyId("topology-netconf");
54     private static final QName V3PO_1704_CAPABILITY = QName.create(
55             URI.create("urn:opendaylight:params:xml:ns:yang:v3po"),
56             Revision.of("2017-03-15"), "v3po");
57     private static final QName V3PO_1701_CAPABILITY = QName.create(
58             URI.create("urn:opendaylight:params:xml:ns:yang:v3po"),
59             Revision.of("2016-12-14"), "v3po");
60     private static final QName INTERFACES_CAPABILITY =
61             QName.create(URI.create("urn:ietf:params:xml:ns:yang:ietf-interfaces"),
62                     Revision.of("2014-05-08"), "ietf-interfaces");
63     private static final List<QName> REQUIRED_CAPABILITIES = new ArrayList<>();
64     private final SocketInfo socketInfo;
65
66     public NeutronHostconfigVppListener(final DataBroker dataBroker, String spath, String sname, String vhostMode) {
67         LOG.info("Initializing Neutron-Hostconfig-Vpp-Listener");
68         this.dataBroker = Preconditions.checkNotNull(dataBroker);
69         final String vhostModeChecked = Preconditions.checkNotNull(vhostMode).toLowerCase(Locale.ROOT);
70         Preconditions.checkArgument(vhostModeChecked.equals("server") || vhostModeChecked.equals("client"),
71                 "Supported values for vhostuser-mode are client and server.");
72         this.socketInfo =
73                 new SocketInfo(Preconditions.checkNotNull(spath), Preconditions.checkNotNull(sname), vhostModeChecked);
74         this.neutronHostconfig = new NeutronHostconfigUtils(dataBroker);
75         REQUIRED_CAPABILITIES.add(V3PO_1704_CAPABILITY);
76         REQUIRED_CAPABILITIES.add(V3PO_1701_CAPABILITY);
77         REQUIRED_CAPABILITIES.add(INTERFACES_CAPABILITY);
78     }
79
80     @Override
81     public void onDataTreeChanged(@Nonnull Collection<DataTreeModification<Node>> changes) {
82         LOG.info("onDataTreeChanged: Received Data Tree Changed ...", changes);
83         executorService.execute(() -> {
84             for (DataTreeModification<Node> change : Preconditions.checkNotNull(changes, "Changes may not be null!")) {
85                 processDataTreeModification(change);
86             }
87         });
88     }
89
90     private void processDataTreeModification(DataTreeModification<Node> change) {
91         final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
92         final DataObjectModification<Node> mod = change.getRootNode();
93         LOG.info("onDataTreeChanged: Received Data Tree Changed Update of Type={} for Key={}",
94                 mod.getModificationType(), key);
95         switch (mod.getModificationType()) {
96             case SUBTREE_MODIFIED:
97                 if (validateVppNode(mod.getDataAfter())) {
98                     updateHostConfig(mod.getDataAfter(), NeutronHostconfigUtils.Action.UPDATE);
99                 } else {
100                     updateHostConfig(mod.getDataBefore(), NeutronHostconfigUtils.Action.DELETE);
101                 }
102                 break;
103             case DELETE:
104                 updateHostConfig(mod.getDataBefore(), NeutronHostconfigUtils.Action.DELETE);
105                 break;
106             case WRITE:
107                 if (validateVppNode(mod.getDataAfter())) {
108                     updateHostConfig(mod.getDataAfter(), NeutronHostconfigUtils.Action.ADD);
109                 }
110                 break;
111             default:
112         }
113     }
114
115     public void init() {
116         LOG.info("Initializing {}", getClass().getSimpleName());
117         DataTreeIdentifier<Node> dataTreeIdentifier = new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL,
118                 InstanceIdentifier.builder(NetworkTopology.class)
119                     .child(Topology.class, new TopologyKey(TOPOLOGY_NETCONF))
120                     .child(Node.class)
121                     .build());
122         listenerRegistration =
123                 dataBroker.registerDataTreeChangeListener(dataTreeIdentifier, NeutronHostconfigVppListener.this);
124         LOG.info("Registered listener to netconf nodes {}.", dataTreeIdentifier.getRootIdentifier());
125     }
126
127     private void updateHostConfig(Node node, NeutronHostconfigUtils.Action action) {
128         for (Map.Entry<String, String> entry : HostconfigUtil.createHostconfigsDataFor(node.getNodeId(), socketInfo)
129             .entrySet()) {
130             LOG.info("Updating hostconfig for node {}. Action: {}.", node.key(), action);
131             neutronHostconfig.updateMdsal(neutronHostconfig.buildHostConfigInfo(node.getNodeId().getValue(),
132                     entry.getKey(), entry.getValue()), action);
133         }
134     }
135
136     private boolean validateVppNode(Node node) {
137         LOG.info("Registering new node {}", node.getNodeId().getValue());
138         NetconfNode netconfNode = node.augmentation(NetconfNode.class);
139         if (netconfNode == null) {
140             LOG.warn("Node {} is not a netconf device", node.getNodeId().getValue());
141             return false;
142         }
143         NetconfNodeConnectionStatus.ConnectionStatus connectionStatus = netconfNode.getConnectionStatus();
144         switch (connectionStatus) {
145             case Connecting:
146                 LOG.info("Connecting device {} ...", node.getNodeId().getValue());
147                 break;
148             case Connected:
149                 if (isCapabilitiesPresent(netconfNode)) {
150                     LOG.warn("Node {} does not contain any capabilities", node.getNodeId().getValue());
151                     break;
152                 }
153                 if (!capabilityCheck(netconfNode.getAvailableCapabilities().getAvailableCapability())) {
154                     LOG.warn("Node {} does not contain all capabilities required by vpp-renderer",
155                             node.getNodeId().getValue());
156                     break;
157                 }
158                 LOG.info("VPP node connected {}", node.getNodeId().getValue());
159                 return true;
160             case UnableToConnect:
161                 LOG.warn("Unable to connect to node {}.", node.getNodeId().getValue());
162                 break;
163             default:
164         }
165         return false;
166     }
167
168     private boolean isCapabilitiesPresent(final NetconfNode netconfNode) {
169         return netconfNode.getAvailableCapabilities() == null
170                 || netconfNode.getAvailableCapabilities().getAvailableCapability() == null
171                 || netconfNode.getAvailableCapabilities().getAvailableCapability().isEmpty();
172     }
173
174     private boolean capabilityCheck(final List<AvailableCapability> capabilities) {
175         final List<String> availableCapabilities =
176                 capabilities.stream().map(AvailableCapability::getCapability).collect(Collectors.toList());
177         return REQUIRED_CAPABILITIES.stream().map(QName::toString).allMatch(availableCapabilities::contains);
178     }
179
180     public void close() throws Exception {
181         if (listenerRegistration != null) {
182             listenerRegistration.close();
183             LOG.info("HostConfig listener Closed");
184         }
185     }
186 }