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.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;
20 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
21 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
22 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
23 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType;
24 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
25 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
26 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
27 import org.opendaylight.controller.md.sal.common.api.data.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.network.topology.Topology;
35 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
36 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
37 import org.opendaylight.yangtools.concepts.ListenerRegistration;
38 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
42 import com.google.common.base.Optional;
44 public class HwvtepDataChangeListener implements ClusteredDataTreeChangeListener<Node>, AutoCloseable {
46 private ListenerRegistration<HwvtepDataChangeListener> registration;
47 private HwvtepConnectionManager hcm;
48 private 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(final DataBroker db) {
59 final DataTreeIdentifier<Node> treeId =
60 new DataTreeIdentifier<Node>(LogicalDatastoreType.CONFIGURATION, getWildcardPath());
62 LOG.trace("Registering on path: {}", treeId);
63 registration = db.registerDataTreeChangeListener(treeId, HwvtepDataChangeListener.this);
64 } catch (final Exception e) {
65 LOG.warn("HwvtepDataChangeListener registration failed", e);
66 //TODO: Should we throw an exception here?
71 public void close() throws Exception {
72 if(registration != null) {
78 public void onDataTreeChanged(Collection<DataTreeModification<Node>> changes) {
79 LOG.trace("onDataTreeChanged: {}", changes);
82 * Currently only handling changes to Global.
83 * Rest will be added later.
87 updateConnections(changes);
93 for (DataTreeModification<Node> change : changes) {
94 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
95 final DataObjectModification<Node> mod = change.getRootNode();
96 switch (mod.getModificationType()) {
98 LOG.trace("Data deleted: {}", mod.getDataBefore());
101 case SUBTREE_MODIFIED:
102 LOG.trace("Data modified: {} to {}", mod.getDataBefore(),mod.getDataAfter());
103 updateConnections(mod);
106 if (mod.getDataBefore() == null) {
107 LOG.trace("Data added: {}", mod.getDataAfter());
108 connect(mod.getDataAfter());
110 LOG.trace("Data modified: {} to {}", mod.getDataBefore(),mod.getDataAfter());
111 updateConnections(mod);
115 throw new IllegalArgumentException("Unhandled modification type " + mod.getModificationType());
121 private void connect(Collection<DataTreeModification<Node>> changes) {
122 for (DataTreeModification<Node> change : changes) {
123 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
124 final DataObjectModification<Node> mod = change.getRootNode();
125 Node node = getCreated(mod);
127 HwvtepGlobalAugmentation hwvtepGlobal = node.getAugmentation(HwvtepGlobalAugmentation.class);
128 // We can only connect if user configured connection info
129 if (hwvtepGlobal != null && hwvtepGlobal.getConnectionInfo() != null) {
130 ConnectionInfo connection = hwvtepGlobal.getConnectionInfo();
131 InstanceIdentifier<Node> iid = hcm.getInstanceIdentifier(connection);
133 LOG.warn("Connection to device {} already exists. Plugin does not allow multiple connections "
134 + "to same device, hence dropping the request {}", connection, hwvtepGlobal);
137 OvsdbClient client = hcm.connect(key, hwvtepGlobal);
138 } catch (UnknownHostException | ConnectException e) {
139 LOG.warn("Failed to connect to HWVTEP node", e);
147 private void updateConnections(Collection<DataTreeModification<Node>> changes) {
148 for (DataTreeModification<Node> change : changes) {
149 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
150 final DataObjectModification<Node> mod = change.getRootNode();
151 Node updated = getUpdated(mod);
152 if (updated != null) {
153 Node original = getOriginal(mod);
154 HwvtepGlobalAugmentation hgUpdated = updated.getAugmentation(HwvtepGlobalAugmentation.class);
155 HwvtepGlobalAugmentation hgOriginal = original.getAugmentation(HwvtepGlobalAugmentation.class);
156 // Check if user has updated connection information
157 if (hgUpdated != null && hgOriginal != null && hgUpdated.getConnectionInfo() != null
158 && !hgUpdated.getConnectionInfo().equals(hgOriginal.getConnectionInfo())) {
159 OvsdbClient client = hcm.getClient(hgUpdated.getConnectionInfo());
160 if (client == null) {
162 hcm.disconnect(hgOriginal);
163 hcm.stopConnectionReconciliationIfActive(key, hgOriginal);
164 OvsdbClient newClient = hcm.connect(key, hgUpdated);
165 if (newClient == null) {
166 hcm.reconcileConnection(key, hgUpdated);
168 } catch (UnknownHostException | ConnectException e) {
169 LOG.warn("Failed to update connection on HWVTEP Node", e);
177 private void updateData(Collection<DataTreeModification<Node>> changes) {
179 * Get connection instances for each change
180 * Update data for each connection
181 * Requires Command patterns. TBD.
183 for (Entry<HwvtepConnectionInstance, Collection<DataTreeModification<Node>>> changesEntry :
184 changesByConnectionInstance(changes).entrySet()) {
185 HwvtepConnectionInstance connectionInstance = changesEntry.getKey();
186 connectionInstance.transact(new TransactCommandAggregator(
187 new HwvtepOperationalState(db, connectionInstance, changesEntry.getValue()),changesEntry.getValue()));
188 connectionInstance.getDeviceInfo().onConfigDataAvailable();
192 private void disconnect(Collection<DataTreeModification<Node>> changes) {
193 for (DataTreeModification<Node> change : changes) {
194 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
195 final DataObjectModification<Node> mod = change.getRootNode();
196 Node deleted = getRemoved(mod);
197 if (deleted != null) {
198 HwvtepGlobalAugmentation hgDeleted = deleted.getAugmentation(HwvtepGlobalAugmentation.class);
199 if (hgDeleted != null) {
201 hcm.disconnect(hgDeleted);
202 hcm.stopConnectionReconciliationIfActive(key, hgDeleted);
203 } catch (UnknownHostException e) {
204 LOG.warn("Failed to disconnect HWVTEP Node", e);
211 private Node getCreated(DataObjectModification<Node> mod) {
212 if((mod.getModificationType() == ModificationType.WRITE)
213 && (mod.getDataBefore() == null)){
214 return mod.getDataAfter();
219 private Node getRemoved(DataObjectModification<Node> mod) {
220 if(mod.getModificationType() == ModificationType.DELETE){
221 return mod.getDataBefore();
226 private Node getUpdated(DataObjectModification<Node> mod) {
228 switch(mod.getModificationType()) {
229 case SUBTREE_MODIFIED:
230 node = mod.getDataAfter();
233 if(mod.getDataBefore() != null) {
234 node = mod.getDataAfter();
243 private Node getOriginal(DataObjectModification<Node> mod) {
245 switch(mod.getModificationType()) {
246 case SUBTREE_MODIFIED:
247 node = mod.getDataBefore();
250 if(mod.getDataBefore() != null) {
251 node = mod.getDataBefore();
255 node = mod.getDataBefore();
263 private InstanceIdentifier<Node> getWildcardPath() {
264 InstanceIdentifier<Node> path = InstanceIdentifier
265 .create(NetworkTopology.class)
266 .child(Topology.class, new TopologyKey(HwvtepSouthboundConstants.HWVTEP_TOPOLOGY_ID))
271 private Map<HwvtepConnectionInstance, Collection<DataTreeModification<Node>>> changesByConnectionInstance(
272 Collection<DataTreeModification<Node>> changes) {
273 Map<HwvtepConnectionInstance, Collection<DataTreeModification<Node>>> result =
274 new HashMap<HwvtepConnectionInstance, Collection<DataTreeModification<Node>>>();
275 for (DataTreeModification<Node> change : changes) {
276 final DataObjectModification<Node> mod = change.getRootNode();
277 //From original node to get connection instance
278 Node node = mod.getDataBefore()!=null ? mod.getDataBefore() : mod.getDataAfter();
279 HwvtepConnectionInstance connection = hcm.getConnectionInstance(node);
280 if(connection == null) {
281 //Let us try getting it from Operational DS
282 final ReadWriteTransaction transaction = db.newReadWriteTransaction();
283 InstanceIdentifier<Node> connectionIid = HwvtepSouthboundMapper.createInstanceIdentifier(node.getNodeId());
284 Optional<Node> optionalNode = HwvtepSouthboundUtil.readNode(transaction, connectionIid);
285 LOG.trace("Node in Operational DataStore for user node {} is {}", node, optionalNode);
286 if(optionalNode.isPresent()) {
287 connection = hcm.getConnectionInstance(optionalNode.get());
290 if (connection != null) {
291 if (!result.containsKey(connection)) {
292 List<DataTreeModification<Node>> tempChanges= new ArrayList<DataTreeModification<Node>>();
293 tempChanges.add(change);
294 result.put(connection, tempChanges);
296 result.get(connection).add(change);
299 LOG.warn("Failed to get the connection of changed node: {}", node.getKey().getNodeId().getValue());
302 LOG.trace("Connection Change Map: {}", result);