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 hcm.connect(HwvtepSouthboundMapper.createInstanceIdentifier(node.getNodeId()), hwvtepGlobal);
137 } catch (UnknownHostException e) {
138 LOG.warn("Failed to connect to OVSDB 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 OvsdbClient client = hcm.getClient(hgUpdated.getConnectionInfo());
158 if (client == null) {
160 hcm.disconnect(hgOriginal);
161 hcm.connect(HwvtepSouthboundMapper.createInstanceIdentifier(original.getNodeId()), hgUpdated);
162 } catch (UnknownHostException e) {
163 LOG.warn("Failed to update connection on OVSDB Node", e);
171 private void updateData(Collection<DataTreeModification<Node>> changes) {
173 * Get connection instances for each change
174 * Update data for each connection
175 * Requires Command patterns. TBD.
177 for (Entry<HwvtepConnectionInstance, Collection<DataTreeModification<Node>>> changesEntry :
178 changesByConnectionInstance(changes).entrySet()) {
179 HwvtepConnectionInstance connectionInstance = changesEntry.getKey();
180 connectionInstance.transact(new TransactCommandAggregator(
181 new HwvtepOperationalState(db, changesEntry.getValue()),changesEntry.getValue()));
185 private void disconnect(Collection<DataTreeModification<Node>> changes) {
186 for (DataTreeModification<Node> change : changes) {
187 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
188 final DataObjectModification<Node> mod = change.getRootNode();
189 Node deleted = getRemoved(mod);
190 if (deleted != null) {
191 HwvtepGlobalAugmentation hgDeleted = deleted.getAugmentation(HwvtepGlobalAugmentation.class);
192 if (hgDeleted != null) {
194 hcm.disconnect(hgDeleted);
195 } catch (UnknownHostException e) {
196 LOG.warn("Failed to disconnect OVSDB Node", e);
203 private Node getCreated(DataObjectModification<Node> mod) {
204 if((mod.getModificationType() == ModificationType.WRITE)
205 && (mod.getDataBefore() == null)){
206 return mod.getDataAfter();
211 private Node getRemoved(DataObjectModification<Node> mod) {
212 if(mod.getModificationType() == ModificationType.DELETE){
213 return mod.getDataBefore();
218 private Node getUpdated(DataObjectModification<Node> mod) {
220 switch(mod.getModificationType()) {
221 case SUBTREE_MODIFIED:
222 node = mod.getDataAfter();
225 if(mod.getDataBefore() != null) {
226 node = mod.getDataAfter();
235 private Node getOriginal(DataObjectModification<Node> mod) {
237 switch(mod.getModificationType()) {
238 case SUBTREE_MODIFIED:
239 node = mod.getDataBefore();
242 if(mod.getDataBefore() != null) {
243 node = mod.getDataBefore();
247 node = mod.getDataBefore();
255 private InstanceIdentifier<Node> getWildcardPath() {
256 InstanceIdentifier<Node> path = InstanceIdentifier
257 .create(NetworkTopology.class)
258 .child(Topology.class, new TopologyKey(HwvtepSouthboundConstants.HWVTEP_TOPOLOGY_ID))
263 private Map<HwvtepConnectionInstance, Collection<DataTreeModification<Node>>> changesByConnectionInstance(
264 Collection<DataTreeModification<Node>> changes) {
265 Map<HwvtepConnectionInstance, Collection<DataTreeModification<Node>>> result =
266 new HashMap<HwvtepConnectionInstance, Collection<DataTreeModification<Node>>>();
267 for (DataTreeModification<Node> change : changes) {
268 final DataObjectModification<Node> mod = change.getRootNode();
269 //From original node to get connection instance
270 Node node = mod.getDataBefore()!=null ? mod.getDataBefore() : mod.getDataAfter();
271 HwvtepConnectionInstance connection = hcm.getConnectionInstance(node);
272 if(connection == null) {
273 //Let us try getting it from Operational DS
274 final ReadWriteTransaction transaction = db.newReadWriteTransaction();
275 InstanceIdentifier<Node> connectionIid = HwvtepSouthboundMapper.createInstanceIdentifier(node.getNodeId());
276 Optional<Node> optionalNode = HwvtepSouthboundUtil.readNode(transaction, connectionIid);
277 LOG.trace("Node in Operational DataStore for user node {} is {}", node, optionalNode);
278 if(optionalNode.isPresent()) {
279 connection = hcm.getConnectionInstance(optionalNode.get());
282 if (connection != null) {
283 if (!result.containsKey(connection)) {
284 List<DataTreeModification<Node>> tempChanges= new ArrayList<DataTreeModification<Node>>();
285 tempChanges.add(change);
286 result.put(connection, tempChanges);
288 result.get(connection).add(change);
291 LOG.warn("Failed to get the connection of changed node: {}", node);
294 LOG.trace("Connection Change Map: {}", result);