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.DataBroker;
20 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
21 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType;
22 import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
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.common.api.data.LogicalDatastoreType;
26 import org.opendaylight.ovsdb.hwvtepsouthbound.transact.HwvtepOperationalState;
27 import org.opendaylight.ovsdb.hwvtepsouthbound.transact.TransactCommandAggregator;
28 import org.opendaylight.ovsdb.lib.OvsdbClient;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalAugmentation;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.ConnectionInfo;
31 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
32 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
33 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
34 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
35 import org.opendaylight.yangtools.concepts.ListenerRegistration;
36 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
40 public class HwvtepDataChangeListener implements DataTreeChangeListener<Node>, AutoCloseable {
42 private ListenerRegistration<HwvtepDataChangeListener> registration;
43 private HwvtepConnectionManager hcm;
44 private DataBroker db;
45 private static final Logger LOG = LoggerFactory.getLogger(HwvtepDataChangeListener.class);
47 HwvtepDataChangeListener(DataBroker db, HwvtepConnectionManager hcm) {
48 LOG.info("Registering HwvtepDataChangeListener");
54 private void registerListener(final DataBroker db) {
55 final DataTreeIdentifier<Node> treeId =
56 new DataTreeIdentifier<Node>(LogicalDatastoreType.CONFIGURATION, getWildcardPath());
58 LOG.trace("Registering on path: {}", treeId);
59 registration = db.registerDataTreeChangeListener(treeId, HwvtepDataChangeListener.this);
60 } catch (final Exception e) {
61 LOG.warn("HwvtepDataChangeListener registration failed");
62 //TODO: Should we throw an exception here?
67 public void close() throws Exception {
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 for (DataTreeModification<Node> change : changes) {
90 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
91 final DataObjectModification<Node> mod = change.getRootNode();
92 switch (mod.getModificationType()) {
94 LOG.trace("Data deleted: {}", mod.getDataBefore());
97 case SUBTREE_MODIFIED:
98 LOG.trace("Data modified: {} to {}", mod.getDataBefore(),mod.getDataAfter());
99 updateConnections(mod);
102 if (mod.getDataBefore() == null) {
103 LOG.trace("Data added: {}", mod.getDataAfter());
104 connect(mod.getDataAfter());
106 LOG.trace("Data modified: {} to {}", mod.getDataBefore(),mod.getDataAfter());
107 updateConnections(mod);
111 throw new IllegalArgumentException("Unhandled modification type " + mod.getModificationType());
117 private void connect(Collection<DataTreeModification<Node>> changes) {
118 for (DataTreeModification<Node> change : changes) {
119 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
120 final DataObjectModification<Node> mod = change.getRootNode();
121 Node node = getCreated(mod);
123 HwvtepGlobalAugmentation hwvtepGlobal = node.getAugmentation(HwvtepGlobalAugmentation.class);
124 if (hwvtepGlobal != null) {
125 ConnectionInfo connection = hwvtepGlobal.getConnectionInfo();
126 InstanceIdentifier<Node> iid = hcm.getInstanceIdentifier(connection);
128 LOG.warn("Connection to device {} already exists. Plugin does not allow multiple connections "
129 + "to same device, hence dropping the request {}", connection, hwvtepGlobal);
132 hcm.connect(HwvtepSouthboundMapper.createInstanceIdentifier(node.getNodeId()), hwvtepGlobal);
133 } catch (UnknownHostException e) {
134 LOG.warn("Failed to connect to OVSDB node", e);
142 private void updateConnections(Collection<DataTreeModification<Node>> changes) {
143 for (DataTreeModification<Node> change : changes) {
144 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
145 final DataObjectModification<Node> mod = change.getRootNode();
146 Node updated = getUpdated(mod);
147 if (updated != null) {
148 Node original = getOriginal(mod);
149 HwvtepGlobalAugmentation hgUpdated = updated.getAugmentation(HwvtepGlobalAugmentation.class);
150 HwvtepGlobalAugmentation hgOriginal = original.getAugmentation(HwvtepGlobalAugmentation.class);
151 if (hgUpdated != null && hgOriginal != null) {
152 OvsdbClient client = hcm.getClient(hgUpdated.getConnectionInfo());
153 if (client == null) {
155 hcm.disconnect(hgOriginal);
156 hcm.connect(HwvtepSouthboundMapper.createInstanceIdentifier(original.getNodeId()), hgUpdated);
157 } catch (UnknownHostException e) {
158 LOG.warn("Failed to update connection on OVSDB Node", e);
166 private void updateData(Collection<DataTreeModification<Node>> changes) {
168 * Get connection instances for each change
169 * Update data for each connection
170 * Requires Command patterns. TBD.
172 for (Entry<HwvtepConnectionInstance, Collection<DataTreeModification<Node>>> changesEntry :
173 changesByConnectionInstance(changes).entrySet()) {
174 HwvtepConnectionInstance connectionInstance = changesEntry.getKey();
175 connectionInstance.transact(new TransactCommandAggregator(
176 new HwvtepOperationalState(db, changesEntry.getValue()),changesEntry.getValue()));
180 private void disconnect(Collection<DataTreeModification<Node>> changes) {
181 for (DataTreeModification<Node> change : changes) {
182 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
183 final DataObjectModification<Node> mod = change.getRootNode();
184 Node deleted = getRemoved(mod);
185 if (deleted != null) {
186 HwvtepGlobalAugmentation hgDeleted = deleted.getAugmentation(HwvtepGlobalAugmentation.class);
187 if (hgDeleted != null) {
189 hcm.disconnect(hgDeleted);
190 } catch (UnknownHostException e) {
191 LOG.warn("Failed to disconnect OVSDB Node", e);
198 private Node getCreated(DataObjectModification<Node> mod) {
199 if((mod.getModificationType() == ModificationType.WRITE)
200 && (mod.getDataBefore() == null)){
201 return mod.getDataAfter();
206 private Node getRemoved(DataObjectModification<Node> mod) {
207 if(mod.getModificationType() == ModificationType.DELETE){
208 return mod.getDataBefore();
213 private Node getUpdated(DataObjectModification<Node> mod) {
215 switch(mod.getModificationType()) {
216 case SUBTREE_MODIFIED:
217 node = mod.getDataAfter();
220 if(mod.getDataBefore() != null) {
221 node = mod.getDataAfter();
230 private Node getOriginal(DataObjectModification<Node> mod) {
232 switch(mod.getModificationType()) {
233 case SUBTREE_MODIFIED:
234 node = mod.getDataBefore();
237 if(mod.getDataBefore() != null) {
238 node = mod.getDataBefore();
242 node = mod.getDataBefore();
250 private InstanceIdentifier<Node> getWildcardPath() {
251 InstanceIdentifier<Node> path = InstanceIdentifier
252 .create(NetworkTopology.class)
253 .child(Topology.class, new TopologyKey(HwvtepSouthboundConstants.HWVTEP_TOPOLOGY_ID))
258 private Map<HwvtepConnectionInstance, Collection<DataTreeModification<Node>>> changesByConnectionInstance(
259 Collection<DataTreeModification<Node>> changes) {
260 Map<HwvtepConnectionInstance, Collection<DataTreeModification<Node>>> result =
261 new HashMap<HwvtepConnectionInstance, Collection<DataTreeModification<Node>>>();
262 for (DataTreeModification<Node> change : changes) {
263 final DataObjectModification<Node> mod = change.getRootNode();
264 //From original node to get connection instance
265 Node node = mod.getDataBefore()!=null ? mod.getDataBefore() : mod.getDataAfter();
266 HwvtepConnectionInstance connection = hcm.getConnectionInstance(node);
267 if (connection != null) {
268 if (!result.containsKey(connection)) {
269 List<DataTreeModification<Node>> tempChanges= new ArrayList<DataTreeModification<Node>>();
270 tempChanges.add(change);
271 result.put(connection, tempChanges);
273 result.get(connection).add(change);
276 LOG.warn("Failed to get the connection of changed node: {}", node);
279 LOG.trace("Connection Change Map: {}", result);