Merge "Move NetvirtIT to use latest IT parent poms"
[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.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;
39
40 public class HwvtepDataChangeListener implements DataTreeChangeListener<Node>, AutoCloseable {
41
42     private ListenerRegistration<HwvtepDataChangeListener> registration;
43     private HwvtepConnectionManager hcm;
44     private DataBroker db;
45     private static final Logger LOG = LoggerFactory.getLogger(HwvtepDataChangeListener.class);
46
47     HwvtepDataChangeListener(DataBroker db, HwvtepConnectionManager hcm) {
48         LOG.info("Registering HwvtepDataChangeListener");
49         this.db = db;
50         this.hcm = hcm;
51         registerListener(db);
52     }
53
54     private void registerListener(final DataBroker db) {
55         final DataTreeIdentifier<Node> treeId =
56                         new DataTreeIdentifier<Node>(LogicalDatastoreType.CONFIGURATION, getWildcardPath());
57         try {
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?
63         }
64     }
65
66     @Override
67     public void close() throws Exception {
68         if(registration != null) {
69             registration.close();
70         }
71     }
72
73     @Override
74     public void onDataTreeChanged(Collection<DataTreeModification<Node>> changes) {
75         LOG.trace("onDataTreeChanged: {}", changes);
76
77         /* TODO:
78          * Currently only handling changes to Global.
79          * Rest will be added later.
80          */
81         connect(changes);
82         
83         updateConnections(changes);
84         
85         updateData(changes);
86         
87         disconnect(changes);
88         /*
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()) {
93                 case DELETE:
94                     LOG.trace("Data deleted: {}", mod.getDataBefore());
95                     //disconnect(mod);
96                     break;
97                 case SUBTREE_MODIFIED:
98                     LOG.trace("Data modified: {} to {}", mod.getDataBefore(),mod.getDataAfter());
99                     updateConnections(mod);
100                     break;
101                 case WRITE:
102                     if (mod.getDataBefore() == null) {
103                         LOG.trace("Data added: {}", mod.getDataAfter());
104                         connect(mod.getDataAfter());
105                     } else {
106                         LOG.trace("Data modified: {} to {}", mod.getDataBefore(),mod.getDataAfter());
107                         updateConnections(mod);
108                     }
109                     break;
110                 default:
111                     throw new IllegalArgumentException("Unhandled modification type " + mod.getModificationType());
112                 }
113         }
114         */
115     }
116
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);
122             if (node != null) {
123                 HwvtepGlobalAugmentation hwvtepGlobal = node.getAugmentation(HwvtepGlobalAugmentation.class);
124                 if (hwvtepGlobal != null) {
125                     ConnectionInfo connection = hwvtepGlobal.getConnectionInfo();
126                     InstanceIdentifier<Node> iid = hcm.getInstanceIdentifier(connection);
127                     if (iid != null) {
128                         LOG.warn("Connection to device {} already exists. Plugin does not allow multiple connections "
129                                         + "to same device, hence dropping the request {}", connection, hwvtepGlobal);
130                     } else {
131                         try {
132                             hcm.connect(HwvtepSouthboundMapper.createInstanceIdentifier(node.getNodeId()), hwvtepGlobal);
133                         } catch (UnknownHostException e) {
134                             LOG.warn("Failed to connect to OVSDB node", e);
135                         }
136                     }
137                 }
138             }
139         }
140     }
141
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) {
154                         try {
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);
159                         }
160                     }
161                 }
162             }
163         }
164     }
165
166     private void updateData(Collection<DataTreeModification<Node>> changes) {
167         /* TODO: 
168          * Get connection instances for each change
169          * Update data for each connection
170          * Requires Command patterns. TBD.
171          */
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()));
177         }
178     }
179
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) {
188                     try {
189                         hcm.disconnect(hgDeleted);
190                     } catch (UnknownHostException e) {
191                         LOG.warn("Failed to disconnect OVSDB Node", e);
192                     }
193                 }
194             }
195         }
196     }
197
198     private Node getCreated(DataObjectModification<Node> mod) {
199         if((mod.getModificationType() == ModificationType.WRITE)
200                         && (mod.getDataBefore() == null)){
201             return mod.getDataAfter();
202         }
203         return null;
204     }
205
206     private Node getRemoved(DataObjectModification<Node> mod) {
207         if(mod.getModificationType() == ModificationType.DELETE){
208             return mod.getDataBefore();
209         }
210         return null;
211     }
212
213     private Node getUpdated(DataObjectModification<Node> mod) {
214         Node node = null;
215         switch(mod.getModificationType()) {
216             case SUBTREE_MODIFIED:
217                 node = mod.getDataAfter();
218                 break;
219             case WRITE:
220                 if(mod.getDataBefore() !=  null) {
221                     node = mod.getDataAfter();
222                 }
223                 break;
224             default:
225                 break;
226         }
227         return node;
228     }
229
230     private Node getOriginal(DataObjectModification<Node> mod) {
231         Node node = null;
232         switch(mod.getModificationType()) {
233             case SUBTREE_MODIFIED:
234                 node = mod.getDataBefore();
235                 break;
236             case WRITE:
237                 if(mod.getDataBefore() !=  null) {
238                     node = mod.getDataBefore();
239                 }
240                 break;
241             case DELETE:
242                 node = mod.getDataBefore();
243                 break;
244             default:
245                 break;
246         }
247         return node;
248     }
249
250     private InstanceIdentifier<Node> getWildcardPath() {
251         InstanceIdentifier<Node> path = InstanceIdentifier
252                         .create(NetworkTopology.class)
253                         .child(Topology.class, new TopologyKey(HwvtepSouthboundConstants.HWVTEP_TOPOLOGY_ID))
254                         .child(Node.class);
255         return path;
256     }
257
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);
272                 } else {
273                     result.get(connection).add(change);
274                 }
275             } else {
276                 LOG.warn("Failed to get the connection of changed node: {}", node);
277             }
278         }
279         LOG.trace("Connection Change Map: {}", result);
280         return result;
281     }
282 }