2 * Copyright (c) 2015, 2017 Ericsson India Global Services Pvt Ltd. and others. 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.ovsdb.hwvtepsouthbound;
11 import java.net.ConnectException;
12 import java.net.UnknownHostException;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.HashMap;
16 import java.util.List;
18 import java.util.Map.Entry;
19 import java.util.concurrent.ExecutionException;
20 import org.opendaylight.mdsal.binding.api.ClusteredDataTreeChangeListener;
21 import org.opendaylight.mdsal.binding.api.DataBroker;
22 import org.opendaylight.mdsal.binding.api.DataObjectModification;
23 import org.opendaylight.mdsal.binding.api.DataObjectModification.ModificationType;
24 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
25 import org.opendaylight.mdsal.binding.api.DataTreeModification;
26 import org.opendaylight.mdsal.binding.api.ReadWriteTransaction;
27 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
28 import org.opendaylight.ovsdb.hwvtepsouthbound.transact.HwvtepOperationalState;
29 import org.opendaylight.ovsdb.hwvtepsouthbound.transact.TransactCommandAggregator;
30 import org.opendaylight.ovsdb.lib.OvsdbClient;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalAugmentation;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.ConnectionInfo;
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.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.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
39 import org.opendaylight.yangtools.concepts.ListenerRegistration;
40 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
44 public class HwvtepDataChangeListener implements ClusteredDataTreeChangeListener<Node>, AutoCloseable {
46 private ListenerRegistration<HwvtepDataChangeListener> registration;
47 private final HwvtepConnectionManager hcm;
48 private final DataBroker db;
49 private static final Logger LOG = LoggerFactory.getLogger(HwvtepDataChangeListener.class);
51 HwvtepDataChangeListener(DataBroker db, HwvtepConnectionManager hcm) {
52 LOG.info("Registering HwvtepDataChangeListener");
58 private void registerListener() {
59 final DataTreeIdentifier<Node> treeId =
60 DataTreeIdentifier.create(LogicalDatastoreType.CONFIGURATION, getWildcardPath());
62 LOG.trace("Registering on path: {}", treeId);
63 registration = db.registerDataTreeChangeListener(treeId, HwvtepDataChangeListener.this);
68 if (registration != null) {
74 public void onDataTreeChanged(Collection<DataTreeModification<Node>> changes) {
75 LOG.trace("onDataTreeChanged: {}", changes);
78 * Currently only handling changes to Global.
79 * Rest will be added later.
83 updateConnections(changes);
89 disconnectViaCli(changes);
92 private void connect(Collection<DataTreeModification<Node>> changes) {
93 for (DataTreeModification<Node> change : changes) {
94 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
95 final DataObjectModification<Node> mod = change.getRootNode();
96 Node node = getCreated(mod);
98 HwvtepGlobalAugmentation hwvtepGlobal = node.augmentation(HwvtepGlobalAugmentation.class);
99 // We can only connect if user configured connection info
100 if (hwvtepGlobal != null && hwvtepGlobal.getConnectionInfo() != null) {
101 ConnectionInfo connection = hwvtepGlobal.getConnectionInfo();
102 InstanceIdentifier<Node> iid = hcm.getInstanceIdentifier(connection);
104 LOG.warn("Connection to device {} already exists. Plugin does not allow multiple connections "
105 + "to same device, hence dropping the request {}", connection, hwvtepGlobal);
108 hcm.connect(key, hwvtepGlobal);
109 } catch (UnknownHostException | ConnectException e) {
110 LOG.warn("Failed to connect to HWVTEP node", e);
118 private void updateConnections(Collection<DataTreeModification<Node>> changes) {
119 for (DataTreeModification<Node> change : changes) {
120 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
121 final DataObjectModification<Node> mod = change.getRootNode();
122 Node updated = getUpdated(mod);
123 if (updated != null) {
124 Node original = getOriginal(mod);
125 HwvtepGlobalAugmentation hgUpdated = updated.augmentation(HwvtepGlobalAugmentation.class);
126 HwvtepGlobalAugmentation hgOriginal = original.augmentation(HwvtepGlobalAugmentation.class);
127 // Check if user has updated connection information
128 if (hgUpdated != null && hgOriginal != null && hgUpdated.getConnectionInfo() != null
129 && !hgUpdated.getConnectionInfo().equals(hgOriginal.getConnectionInfo())) {
130 OvsdbClient client = hcm.getClient(hgUpdated.getConnectionInfo());
131 if (client == null) {
133 hcm.disconnect(hgOriginal);
134 hcm.stopConnectionReconciliationIfActive(key, hgOriginal);
135 OvsdbClient newClient = hcm.connect(key, hgUpdated);
136 if (newClient == null) {
137 hcm.reconcileConnection(key, hgUpdated);
139 } catch (UnknownHostException | ConnectException e) {
140 LOG.warn("Failed to update connection on HWVTEP Node", e);
148 private void updateData(Collection<DataTreeModification<Node>> changes) {
150 * Get connection instances for each change
151 * Update data for each connection
152 * Requires Command patterns. TBD.
154 for (Entry<HwvtepConnectionInstance, Collection<DataTreeModification<Node>>> changesEntry :
155 changesByConnectionInstance(changes).entrySet()) {
156 HwvtepConnectionInstance connectionInstance = changesEntry.getKey();
157 connectionInstance.transact(new TransactCommandAggregator(
158 new HwvtepOperationalState(db, connectionInstance, changesEntry.getValue()),changesEntry.getValue()));
159 connectionInstance.getDeviceInfo().onConfigDataAvailable();
163 private void disconnect(Collection<DataTreeModification<Node>> changes) {
164 for (DataTreeModification<Node> change : changes) {
165 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
166 final DataObjectModification<Node> mod = change.getRootNode();
167 Node deleted = getRemoved(mod);
168 if (deleted != null) {
169 HwvtepGlobalAugmentation hgDeleted = deleted.augmentation(HwvtepGlobalAugmentation.class);
170 if (hgDeleted != null) {
172 hcm.disconnect(hgDeleted);
173 hcm.stopConnectionReconciliationIfActive(key, hgDeleted);
174 } catch (UnknownHostException e) {
175 LOG.warn("Failed to disconnect HWVTEP Node", e);
182 private static Node getCreated(DataObjectModification<Node> mod) {
183 if (mod.getModificationType() == ModificationType.WRITE && mod.getDataBefore() == null) {
184 return mod.getDataAfter();
189 private static Node getRemoved(DataObjectModification<Node> mod) {
190 if (mod.getModificationType() == ModificationType.DELETE) {
191 return mod.getDataBefore();
196 private static Node getUpdated(DataObjectModification<Node> mod) {
198 switch (mod.getModificationType()) {
199 case SUBTREE_MODIFIED:
200 node = mod.getDataAfter();
203 if (mod.getDataBefore() != null) {
204 node = mod.getDataAfter();
213 private static Node getOriginal(DataObjectModification<Node> mod) {
215 switch (mod.getModificationType()) {
216 case SUBTREE_MODIFIED:
218 node = mod.getDataBefore();
221 if (mod.getDataBefore() != null) {
222 node = mod.getDataBefore();
231 private static InstanceIdentifier<Node> getWildcardPath() {
232 return InstanceIdentifier.create(NetworkTopology.class)
233 .child(Topology.class, new TopologyKey(HwvtepSouthboundConstants.HWVTEP_TOPOLOGY_ID))
237 private Map<HwvtepConnectionInstance, Collection<DataTreeModification<Node>>> changesByConnectionInstance(
238 Collection<DataTreeModification<Node>> changes) {
239 Map<HwvtepConnectionInstance, Collection<DataTreeModification<Node>>> result = new HashMap<>();
240 for (DataTreeModification<Node> change : changes) {
241 final DataObjectModification<Node> mod = change.getRootNode();
242 //From original node to get connection instance
243 Node node = mod.getDataBefore() != null ? mod.getDataBefore() : mod.getDataAfter();
244 HwvtepConnectionInstance connection = hcm.getConnectionInstanceFromNodeIid(
245 change.getRootPath().getRootIdentifier());
246 if (connection != null) {
247 if (!result.containsKey(connection)) {
248 List<DataTreeModification<Node>> tempChanges = new ArrayList<>();
249 tempChanges.add(change);
250 result.put(connection, tempChanges);
252 result.get(connection).add(change);
255 LOG.warn("Failed to get the connection of changed node: {}", node.key().getNodeId().getValue());
258 LOG.trace("Connection Change Map: {}", result);
262 @SuppressWarnings("checkstyle:IllegalCatch")
263 private void disconnectViaCli(Collection<DataTreeModification<Node>> changes) {
264 for (DataTreeModification<Node> change : changes) {
265 String nodeId = change.getRootPath().getRootIdentifier().firstKeyOf(Node.class).getNodeId().getValue();
266 if (!nodeId.contains("/disconnect")) {
269 int reconcileIndex = nodeId.indexOf("/disconnect");
270 String globalNodeId = nodeId.substring(0, reconcileIndex);
271 InstanceIdentifier<Node> globalNodeIid = change.getRootPath()
272 .getRootIdentifier().firstIdentifierOf(Topology.class)
273 .child(Node.class, new NodeKey(new NodeId(globalNodeId)));
274 HwvtepConnectionInstance connectionInstance = hcm.getConnectionInstanceFromNodeIid(globalNodeIid);
275 if (connectionInstance != null) {
276 LOG.error("Disconnecting from controller {}", nodeId);
278 ReadWriteTransaction tx = db.newReadWriteTransaction();
279 tx.delete(LogicalDatastoreType.CONFIGURATION, change.getRootPath().getRootIdentifier());
282 } catch (ExecutionException | InterruptedException e) {
283 LOG.error("Failed to delete the node {}", change.getRootPath().getRootIdentifier());
287 connectionInstance.disconnect();
288 } catch (Exception e) {
289 LOG.debug("Failed to disconnect");