2 * Copyright (c) 2015 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.UnknownHostException;
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.HashMap;
15 import java.util.List;
17 import java.util.Map.Entry;
19 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
22 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType;
23 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
24 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
25 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
26 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
27 import org.opendaylight.ovsdb.hwvtepsouthbound.transact.HwvtepOperationalState;
28 import org.opendaylight.ovsdb.hwvtepsouthbound.transact.TransactCommandAggregator;
29 import org.opendaylight.ovsdb.lib.OvsdbClient;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalAugmentation;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.ConnectionInfo;
32 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
33 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
34 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
35 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
36 import org.opendaylight.yangtools.concepts.ListenerRegistration;
37 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
41 import com.google.common.base.Optional;
43 public class HwvtepDataChangeListener implements ClusteredDataTreeChangeListener<Node>, AutoCloseable {
45 private ListenerRegistration<HwvtepDataChangeListener> registration;
46 private HwvtepConnectionManager hcm;
47 private DataBroker db;
48 private static final Logger LOG = LoggerFactory.getLogger(HwvtepDataChangeListener.class);
50 HwvtepDataChangeListener(DataBroker db, HwvtepConnectionManager hcm) {
51 LOG.info("Registering HwvtepDataChangeListener");
57 private void registerListener(final DataBroker db) {
58 final DataTreeIdentifier<Node> treeId =
59 new DataTreeIdentifier<Node>(LogicalDatastoreType.CONFIGURATION, getWildcardPath());
61 LOG.trace("Registering on path: {}", treeId);
62 registration = db.registerDataTreeChangeListener(treeId, HwvtepDataChangeListener.this);
63 } catch (final Exception e) {
64 LOG.warn("HwvtepDataChangeListener registration failed", e);
65 //TODO: Should we throw an exception here?
70 public void close() throws Exception {
71 if(registration != null) {
77 public void onDataTreeChanged(Collection<DataTreeModification<Node>> changes) {
78 LOG.trace("onDataTreeChanged: {}", changes);
81 * Currently only handling changes to Global.
82 * Rest will be added later.
86 updateConnections(changes);
92 for (DataTreeModification<Node> change : changes) {
93 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
94 final DataObjectModification<Node> mod = change.getRootNode();
95 switch (mod.getModificationType()) {
97 LOG.trace("Data deleted: {}", mod.getDataBefore());
100 case SUBTREE_MODIFIED:
101 LOG.trace("Data modified: {} to {}", mod.getDataBefore(),mod.getDataAfter());
102 updateConnections(mod);
105 if (mod.getDataBefore() == null) {
106 LOG.trace("Data added: {}", mod.getDataAfter());
107 connect(mod.getDataAfter());
109 LOG.trace("Data modified: {} to {}", mod.getDataBefore(),mod.getDataAfter());
110 updateConnections(mod);
114 throw new IllegalArgumentException("Unhandled modification type " + mod.getModificationType());
120 private void connect(Collection<DataTreeModification<Node>> changes) {
121 for (DataTreeModification<Node> change : changes) {
122 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
123 final DataObjectModification<Node> mod = change.getRootNode();
124 Node node = getCreated(mod);
126 HwvtepGlobalAugmentation hwvtepGlobal = node.getAugmentation(HwvtepGlobalAugmentation.class);
127 // We can only connect if user configured connection info
128 if (hwvtepGlobal != null && hwvtepGlobal.getConnectionInfo() != null) {
129 ConnectionInfo connection = hwvtepGlobal.getConnectionInfo();
130 InstanceIdentifier<Node> iid = hcm.getInstanceIdentifier(connection);
132 LOG.warn("Connection to device {} already exists. Plugin does not allow multiple connections "
133 + "to same device, hence dropping the request {}", connection, hwvtepGlobal);
136 OvsdbClient client = hcm.connect(key, hwvtepGlobal);
137 } catch (UnknownHostException e) {
138 LOG.warn("Failed to connect to HWVTEP node", e);
146 private void updateConnections(Collection<DataTreeModification<Node>> changes) {
147 for (DataTreeModification<Node> change : changes) {
148 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
149 final DataObjectModification<Node> mod = change.getRootNode();
150 Node updated = getUpdated(mod);
151 if (updated != null) {
152 Node original = getOriginal(mod);
153 HwvtepGlobalAugmentation hgUpdated = updated.getAugmentation(HwvtepGlobalAugmentation.class);
154 HwvtepGlobalAugmentation hgOriginal = original.getAugmentation(HwvtepGlobalAugmentation.class);
155 // Check if user has updated connection information
156 if (hgUpdated != null && hgOriginal != null && hgUpdated.getConnectionInfo() != null
157 && !hgUpdated.getConnectionInfo().equals(hgOriginal.getConnectionInfo())) {
158 OvsdbClient client = hcm.getClient(hgUpdated.getConnectionInfo());
159 if (client == null) {
161 hcm.disconnect(hgOriginal);
162 hcm.stopConnectionReconciliationIfActive(key, hgOriginal);
163 OvsdbClient newClient = hcm.connect(key, hgUpdated);
164 if (newClient == null) {
165 hcm.reconcileConnection(key, hgUpdated);
167 } catch (UnknownHostException e) {
168 LOG.warn("Failed to update connection on HWVTEP Node", e);
176 private void updateData(Collection<DataTreeModification<Node>> changes) {
178 * Get connection instances for each change
179 * Update data for each connection
180 * Requires Command patterns. TBD.
182 for (Entry<HwvtepConnectionInstance, Collection<DataTreeModification<Node>>> changesEntry :
183 changesByConnectionInstance(changes).entrySet()) {
184 HwvtepConnectionInstance connectionInstance = changesEntry.getKey();
185 connectionInstance.transact(new TransactCommandAggregator(
186 new HwvtepOperationalState(db, changesEntry.getValue()),changesEntry.getValue()));
190 private void disconnect(Collection<DataTreeModification<Node>> changes) {
191 for (DataTreeModification<Node> change : changes) {
192 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
193 final DataObjectModification<Node> mod = change.getRootNode();
194 Node deleted = getRemoved(mod);
195 if (deleted != null) {
196 HwvtepGlobalAugmentation hgDeleted = deleted.getAugmentation(HwvtepGlobalAugmentation.class);
197 if (hgDeleted != null) {
199 hcm.disconnect(hgDeleted);
200 hcm.stopConnectionReconciliationIfActive(key, hgDeleted);
201 } catch (UnknownHostException e) {
202 LOG.warn("Failed to disconnect HWVTEP Node", e);
209 private Node getCreated(DataObjectModification<Node> mod) {
210 if((mod.getModificationType() == ModificationType.WRITE)
211 && (mod.getDataBefore() == null)){
212 return mod.getDataAfter();
217 private Node getRemoved(DataObjectModification<Node> mod) {
218 if(mod.getModificationType() == ModificationType.DELETE){
219 return mod.getDataBefore();
224 private Node getUpdated(DataObjectModification<Node> mod) {
226 switch(mod.getModificationType()) {
227 case SUBTREE_MODIFIED:
228 node = mod.getDataAfter();
231 if(mod.getDataBefore() != null) {
232 node = mod.getDataAfter();
241 private Node getOriginal(DataObjectModification<Node> mod) {
243 switch(mod.getModificationType()) {
244 case SUBTREE_MODIFIED:
245 node = mod.getDataBefore();
248 if(mod.getDataBefore() != null) {
249 node = mod.getDataBefore();
253 node = mod.getDataBefore();
261 private InstanceIdentifier<Node> getWildcardPath() {
262 InstanceIdentifier<Node> path = InstanceIdentifier
263 .create(NetworkTopology.class)
264 .child(Topology.class, new TopologyKey(HwvtepSouthboundConstants.HWVTEP_TOPOLOGY_ID))
269 private Map<HwvtepConnectionInstance, Collection<DataTreeModification<Node>>> changesByConnectionInstance(
270 Collection<DataTreeModification<Node>> changes) {
271 Map<HwvtepConnectionInstance, Collection<DataTreeModification<Node>>> result =
272 new HashMap<HwvtepConnectionInstance, Collection<DataTreeModification<Node>>>();
273 for (DataTreeModification<Node> change : changes) {
274 final DataObjectModification<Node> mod = change.getRootNode();
275 //From original node to get connection instance
276 Node node = mod.getDataBefore()!=null ? mod.getDataBefore() : mod.getDataAfter();
277 HwvtepConnectionInstance connection = hcm.getConnectionInstance(node);
278 if(connection == null) {
279 //Let us try getting it from Operational DS
280 final ReadWriteTransaction transaction = db.newReadWriteTransaction();
281 InstanceIdentifier<Node> connectionIid = HwvtepSouthboundMapper.createInstanceIdentifier(node.getNodeId());
282 Optional<Node> optionalNode = HwvtepSouthboundUtil.readNode(transaction, connectionIid);
283 LOG.trace("Node in Operational DataStore for user node {} is {}", node, optionalNode);
284 if(optionalNode.isPresent()) {
285 connection = hcm.getConnectionInstance(optionalNode.get());
288 if (connection != null) {
289 if (!result.containsKey(connection)) {
290 List<DataTreeModification<Node>> tempChanges= new ArrayList<DataTreeModification<Node>>();
291 tempChanges.add(change);
292 result.put(connection, tempChanges);
294 result.get(connection).add(change);
297 LOG.warn("Failed to get the connection of changed node: {}", node);
300 LOG.trace("Connection Change Map: {}", result);