Add getTerminationPointOfBridge method to SouthboundUtils
[ovsdb.git] / hwvtepsouthbound / hwvtepsouthbound-impl / src / main / java / org / opendaylight / ovsdb / hwvtepsouthbound / HwvtepDataChangeListener.java
1 /*
2  * Copyright (c) 2015 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.ovsdb.hwvtepsouthbound;
10
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;
16 import java.util.Map;
17 import java.util.Map.Entry;
18
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;
40
41 import com.google.common.base.Optional;
42
43 public class HwvtepDataChangeListener implements ClusteredDataTreeChangeListener<Node>, AutoCloseable {
44
45     private ListenerRegistration<HwvtepDataChangeListener> registration;
46     private HwvtepConnectionManager hcm;
47     private DataBroker db;
48     private static final Logger LOG = LoggerFactory.getLogger(HwvtepDataChangeListener.class);
49
50     HwvtepDataChangeListener(DataBroker db, HwvtepConnectionManager hcm) {
51         LOG.info("Registering HwvtepDataChangeListener");
52         this.db = db;
53         this.hcm = hcm;
54         registerListener(db);
55     }
56
57     private void registerListener(final DataBroker db) {
58         final DataTreeIdentifier<Node> treeId =
59                         new DataTreeIdentifier<Node>(LogicalDatastoreType.CONFIGURATION, getWildcardPath());
60         try {
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?
66         }
67     }
68
69     @Override
70     public void close() throws Exception {
71         if(registration != null) {
72             registration.close();
73         }
74     }
75
76     @Override
77     public void onDataTreeChanged(Collection<DataTreeModification<Node>> changes) {
78         LOG.trace("onDataTreeChanged: {}", changes);
79
80         /* TODO:
81          * Currently only handling changes to Global.
82          * Rest will be added later.
83          */
84         connect(changes);
85         
86         updateConnections(changes);
87         
88         updateData(changes);
89         
90         disconnect(changes);
91         /*
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()) {
96                 case DELETE:
97                     LOG.trace("Data deleted: {}", mod.getDataBefore());
98                     //disconnect(mod);
99                     break;
100                 case SUBTREE_MODIFIED:
101                     LOG.trace("Data modified: {} to {}", mod.getDataBefore(),mod.getDataAfter());
102                     updateConnections(mod);
103                     break;
104                 case WRITE:
105                     if (mod.getDataBefore() == null) {
106                         LOG.trace("Data added: {}", mod.getDataAfter());
107                         connect(mod.getDataAfter());
108                     } else {
109                         LOG.trace("Data modified: {} to {}", mod.getDataBefore(),mod.getDataAfter());
110                         updateConnections(mod);
111                     }
112                     break;
113                 default:
114                     throw new IllegalArgumentException("Unhandled modification type " + mod.getModificationType());
115                 }
116         }
117         */
118     }
119
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);
125             if (node != null) {
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);
131                     if (iid != null) {
132                         LOG.warn("Connection to device {} already exists. Plugin does not allow multiple connections "
133                                         + "to same device, hence dropping the request {}", connection, hwvtepGlobal);
134                     } else {
135                         try {
136                             OvsdbClient client = hcm.connect(key, hwvtepGlobal);
137                         } catch (UnknownHostException e) {
138                             LOG.warn("Failed to connect to HWVTEP node", e);
139                         }
140                     }
141                 }
142             }
143         }
144     }
145
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) {
160                         try {
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);
166                             }
167                         } catch (UnknownHostException e) {
168                             LOG.warn("Failed to update connection on HWVTEP Node", e);
169                         }
170                     }
171                 }
172             }
173         }
174     }
175
176     private void updateData(Collection<DataTreeModification<Node>> changes) {
177         /* TODO:
178          * Get connection instances for each change
179          * Update data for each connection
180          * Requires Command patterns. TBD.
181          */
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()));
187         }
188     }
189
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) {
198                     try {
199                         hcm.disconnect(hgDeleted);
200                         hcm.stopConnectionReconciliationIfActive(key, hgDeleted);
201                     } catch (UnknownHostException e) {
202                         LOG.warn("Failed to disconnect HWVTEP Node", e);
203                     }
204                 }
205             }
206         }
207     }
208
209     private Node getCreated(DataObjectModification<Node> mod) {
210         if((mod.getModificationType() == ModificationType.WRITE)
211                         && (mod.getDataBefore() == null)){
212             return mod.getDataAfter();
213         }
214         return null;
215     }
216
217     private Node getRemoved(DataObjectModification<Node> mod) {
218         if(mod.getModificationType() == ModificationType.DELETE){
219             return mod.getDataBefore();
220         }
221         return null;
222     }
223
224     private Node getUpdated(DataObjectModification<Node> mod) {
225         Node node = null;
226         switch(mod.getModificationType()) {
227             case SUBTREE_MODIFIED:
228                 node = mod.getDataAfter();
229                 break;
230             case WRITE:
231                 if(mod.getDataBefore() !=  null) {
232                     node = mod.getDataAfter();
233                 }
234                 break;
235             default:
236                 break;
237         }
238         return node;
239     }
240
241     private Node getOriginal(DataObjectModification<Node> mod) {
242         Node node = null;
243         switch(mod.getModificationType()) {
244             case SUBTREE_MODIFIED:
245                 node = mod.getDataBefore();
246                 break;
247             case WRITE:
248                 if(mod.getDataBefore() !=  null) {
249                     node = mod.getDataBefore();
250                 }
251                 break;
252             case DELETE:
253                 node = mod.getDataBefore();
254                 break;
255             default:
256                 break;
257         }
258         return node;
259     }
260
261     private InstanceIdentifier<Node> getWildcardPath() {
262         InstanceIdentifier<Node> path = InstanceIdentifier
263                         .create(NetworkTopology.class)
264                         .child(Topology.class, new TopologyKey(HwvtepSouthboundConstants.HWVTEP_TOPOLOGY_ID))
265                         .child(Node.class);
266         return path;
267     }
268
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());
286                 }
287             }
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);
293                 } else {
294                     result.get(connection).add(change);
295                 }
296             } else {
297                 LOG.warn("Failed to get the connection of changed node: {}", node);
298             }
299         }
300         LOG.trace("Connection Change Map: {}", result);
301         return result;
302     }
303 }