Merge "rework sfc flows"
[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.Collection;
13 import java.util.HashMap;
14 import java.util.Map;
15 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
16 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
17 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType;
18 import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
19 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
20 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
21 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
22 import org.opendaylight.ovsdb.lib.OvsdbClient;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalAugmentation;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.ConnectionInfo;
25 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
26 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
27 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
28 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
29 import org.opendaylight.yangtools.concepts.ListenerRegistration;
30 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 public class HwvtepDataChangeListener implements DataTreeChangeListener<Node>, AutoCloseable {
35
36     private ListenerRegistration<HwvtepDataChangeListener> registration;
37     private HwvtepConnectionManager hcm;
38     private DataBroker db;
39     private static final Logger LOG = LoggerFactory.getLogger(HwvtepDataChangeListener.class);
40
41     HwvtepDataChangeListener(DataBroker db, HwvtepConnectionManager hcm) {
42         LOG.info("Registering HwvtepDataChangeListener");
43         this.db = db;
44         this.hcm = hcm;
45         registerListener(db);
46     }
47
48     private void registerListener(final DataBroker db) {
49         final DataTreeIdentifier<Node> treeId =
50                         new DataTreeIdentifier<Node>(LogicalDatastoreType.CONFIGURATION, getWildcardPath());
51         try {
52             LOG.trace("Registering on path: {}", treeId);
53             registration = db.registerDataTreeChangeListener(treeId, HwvtepDataChangeListener.this);
54         } catch (final Exception e) {
55             LOG.warn("HwvtepDataChangeListener registration failed");
56             //TODO: Should we throw an exception here?
57         }
58     }
59
60     @Override
61     public void close() throws Exception {
62         if(registration != null) {
63             registration.close();
64         }
65     }
66
67     @Override
68     public void onDataTreeChanged(Collection<DataTreeModification<Node>> changes) {
69         LOG.trace("onDataTreeChanged: {}", changes);
70
71         /* TODO:
72          * Currently only handling changes to Global.
73          * Rest will be added later.
74          */
75         connect(changes);
76         
77         updateConnections(changes);
78         
79         updateData(changes);
80         
81         disconnect(changes);
82         /*
83         for (DataTreeModification<Node> change : changes) {
84             final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
85             final DataObjectModification<Node> mod = change.getRootNode();
86                 switch (mod.getModificationType()) {
87                 case DELETE:
88                     LOG.trace("Data deleted: {}", mod.getDataBefore());
89                     //disconnect(mod);
90                     break;
91                 case SUBTREE_MODIFIED:
92                     LOG.trace("Data modified: {} to {}", mod.getDataBefore(),mod.getDataAfter());
93                     updateConnections(mod);
94                     break;
95                 case WRITE:
96                     if (mod.getDataBefore() == null) {
97                         LOG.trace("Data added: {}", mod.getDataAfter());
98                         connect(mod.getDataAfter());
99                     } else {
100                         LOG.trace("Data modified: {} to {}", mod.getDataBefore(),mod.getDataAfter());
101                         updateConnections(mod);
102                     }
103                     break;
104                 default:
105                     throw new IllegalArgumentException("Unhandled modification type " + mod.getModificationType());
106                 }
107         }
108         */
109     }
110
111     private void connect(Collection<DataTreeModification<Node>> changes) {
112         for (DataTreeModification<Node> change : changes) {
113             final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
114             final DataObjectModification<Node> mod = change.getRootNode();
115             Node node = getCreated(mod);
116             if (node != null) {
117                 HwvtepGlobalAugmentation hwvtepGlobal = node.getAugmentation(HwvtepGlobalAugmentation.class);
118                 ConnectionInfo connection = hwvtepGlobal.getConnectionInfo();
119                 InstanceIdentifier<Node> iid = hcm.getInstanceIdentifier(connection);
120                 if (iid != null) {
121                     LOG.warn("Connection to device {} already exists. Plugin does not allow multiple connections "
122                                     + "to same device, hence dropping the request {}", connection, hwvtepGlobal);
123                 } else {
124                     try {
125                         hcm.connect(HwvtepSouthboundMapper.createInstanceIdentifier(node.getNodeId()), hwvtepGlobal);
126                     } catch (UnknownHostException e) {
127                         LOG.warn("Failed to connect to OVSDB node", e);
128                     }
129                 }
130             }
131         }
132
133     }
134
135     private void updateConnections(Collection<DataTreeModification<Node>> changes) {
136         for (DataTreeModification<Node> change : changes) {
137             final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
138             final DataObjectModification<Node> mod = change.getRootNode();
139             Node updated = getUpdated(mod);
140             if (updated != null) {
141                 Node original = getOriginal(mod);
142                 HwvtepGlobalAugmentation hgUpdated = updated.getAugmentation(HwvtepGlobalAugmentation.class);
143                 HwvtepGlobalAugmentation hgOriginal = original.getAugmentation(HwvtepGlobalAugmentation.class);
144                 OvsdbClient client = hcm.getClient(hgUpdated.getConnectionInfo());
145                 if (client == null) {
146                     try {
147                         hcm.disconnect(hgOriginal);
148                         hcm.connect(HwvtepSouthboundMapper.createInstanceIdentifier(original.getNodeId()), hgUpdated);
149                     } catch (UnknownHostException e) {
150                         LOG.warn("Failed to update connection on OVSDB Node", e);
151                     }
152                 }
153             }
154         }
155
156     }
157
158     private void updateData(Collection<DataTreeModification<Node>> changes) {
159         /* TODO: 
160          * Get connection instances for each change
161          * Update data for each connection
162          * Requires Command patterns. TBD.
163          */
164         connectionInstancesFromChanges(changes);
165         /*for (Entry<InstanceIdentifier<Node>, HwvtepConnectionInstance> connectionInstanceEntry :
166             connectionInstancesFromChanges(changes).entrySet()) {
167             
168         }*/
169     }
170
171     private void disconnect(Collection<DataTreeModification<Node>> changes) {
172         for (DataTreeModification<Node> change : changes) {
173             final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
174             final DataObjectModification<Node> mod = change.getRootNode();
175             Node deleted = getRemoved(mod);
176             if (deleted != null) {
177                 HwvtepGlobalAugmentation hgDeleted = deleted.getAugmentation(HwvtepGlobalAugmentation.class);
178                 try {
179                     hcm.disconnect(hgDeleted);
180                 } catch (UnknownHostException e) {
181                     LOG.warn("Failed to disconnect OVSDB Node", e);
182                 }
183             }
184         }
185     }
186
187     private Node getCreated(DataObjectModification<Node> mod) {
188         if((mod.getModificationType() == ModificationType.WRITE) 
189                         && (mod.getDataBefore() == null)){
190             return mod.getDataAfter();
191         }
192         return null;
193     }
194
195     private Node getRemoved(DataObjectModification<Node> mod) {
196         if(mod.getModificationType() == ModificationType.DELETE){
197             return mod.getDataBefore();
198         }
199         return null;
200     }
201
202     private Node getUpdated(DataObjectModification<Node> mod) {
203         Node node = null;
204         switch(mod.getModificationType()) {
205             case SUBTREE_MODIFIED:
206                 node = mod.getDataAfter();
207                 break;
208             case WRITE:
209                 if(mod.getDataBefore() !=  null) {
210                     node = mod.getDataAfter();
211                 }
212                 break;
213             default:
214                 break;
215         }
216         return node;
217     }
218
219     private Node getOriginal(DataObjectModification<Node> mod) {
220         Node node = null;
221         switch(mod.getModificationType()) {
222             case SUBTREE_MODIFIED:
223                 node = mod.getDataBefore();
224                 break;
225             case WRITE:
226                 if(mod.getDataBefore() !=  null) {
227                     node = mod.getDataBefore();
228                 }
229                 break;
230             case DELETE:
231                 node = mod.getDataBefore();
232                 break;
233             default:
234                 break;
235         }
236         return node;
237     }
238
239     private InstanceIdentifier<Node> getWildcardPath() {
240         InstanceIdentifier<Node> path = InstanceIdentifier
241                         .create(NetworkTopology.class)
242                         .child(Topology.class, new TopologyKey(HwvtepSouthboundConstants.HWVTEP_TOPOLOGY_ID))
243                         .child(Node.class);
244         return path;
245     }
246
247     public Map<InstanceIdentifier<Node>, HwvtepConnectionInstance> connectionInstancesFromChanges(
248                     Collection<DataTreeModification<Node>> changes) {
249         Map<InstanceIdentifier<Node>, HwvtepConnectionInstance> result =
250                         new HashMap<InstanceIdentifier<Node>, HwvtepConnectionInstance>();
251         for (DataTreeModification<Node> change : changes) {
252             final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
253             final DataObjectModification<Node> mod = change.getRootNode();
254             Node created = getCreated(mod);
255             Node updated = getUpdated(mod);
256             Node original = getOriginal(mod);
257             Node deleted = getRemoved(mod);
258
259             if((original != null) && (deleted == null)) {
260                 result.put(key, getConnectionInstance(original));
261             }
262             if(created != null) {
263                 result.put(key, getConnectionInstance(created));
264             } else if(updated != null) {
265                 result.put(key, getConnectionInstance(updated));
266             }
267         }
268         LOG.trace("Connection Instance Map: {}", result);
269         return result;
270     }
271
272     private HwvtepConnectionInstance getConnectionInstance(Node node) {
273         // TODO Auto-generated method stub
274         return null;
275     }
276
277
278 }