2 * Copyright (c) 2017 Intel Corporation. All rights reserved.
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
9 package org.opendaylight.neutron.hostconfig.vpp;
11 import com.google.common.base.Preconditions;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.List;
16 import java.util.Locale;
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;
45 public class NeutronHostconfigVppListener implements ClusteredDataTreeChangeListener<Node> {
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);
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;
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.");
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);
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);
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);
100 updateHostConfig(mod.getDataBefore(), NeutronHostconfigUtils.Action.DELETE);
104 updateHostConfig(mod.getDataBefore(), NeutronHostconfigUtils.Action.DELETE);
107 if (validateVppNode(mod.getDataAfter())) {
108 updateHostConfig(mod.getDataAfter(), NeutronHostconfigUtils.Action.ADD);
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))
122 listenerRegistration =
123 dataBroker.registerDataTreeChangeListener(dataTreeIdentifier, NeutronHostconfigVppListener.this);
124 LOG.info("Registered listener to netconf nodes {}.", dataTreeIdentifier.getRootIdentifier());
127 private void updateHostConfig(Node node, NeutronHostconfigUtils.Action action) {
128 for (Map.Entry<String, String> entry : HostconfigUtil.createHostconfigsDataFor(node.getNodeId(), socketInfo)
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);
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());
143 NetconfNodeConnectionStatus.ConnectionStatus connectionStatus = netconfNode.getConnectionStatus();
144 switch (connectionStatus) {
146 LOG.info("Connecting device {} ...", node.getNodeId().getValue());
149 if (isCapabilitiesPresent(netconfNode)) {
150 LOG.warn("Node {} does not contain any capabilities", node.getNodeId().getValue());
153 if (!capabilityCheck(netconfNode.getAvailableCapabilities().getAvailableCapability())) {
154 LOG.warn("Node {} does not contain all capabilities required by vpp-renderer",
155 node.getNodeId().getValue());
158 LOG.info("VPP node connected {}", node.getNodeId().getValue());
160 case UnableToConnect:
161 LOG.warn("Unable to connect to node {}.", node.getNodeId().getValue());
168 private boolean isCapabilitiesPresent(final NetconfNode netconfNode) {
169 return netconfNode.getAvailableCapabilities() == null
170 || netconfNode.getAvailableCapabilities().getAvailableCapability() == null
171 || netconfNode.getAvailableCapabilities().getAvailableCapability().isEmpty();
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);
180 public void close() throws Exception {
181 if (listenerRegistration != null) {
182 listenerRegistration.close();
183 LOG.info("HostConfig listener Closed");