Code for TEP auto-config feature in ITM module
[genius.git] / itm / itm-impl / src / main / java / org / opendaylight / genius / itm / listeners / OvsdbNodeListener.java
1 /*
2  * Copyright (c) 2016 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.genius.itm.listeners;
10
11 import java.util.List;
12
13 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
14 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
15 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
16 import org.opendaylight.genius.itm.impl.ItmUtils;
17 import org.opendaylight.genius.datastoreutils.DataStoreJobCoordinator;
18 import org.opendaylight.genius.itm.confighelpers.OvsdbTepAddWorker;
19 import org.opendaylight.genius.itm.confighelpers.OvsdbTepRemoveWorker;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.OpenvswitchOtherConfigs;
23 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
24 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
25 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
26 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 import javax.annotation.PostConstruct;
31 import javax.annotation.PreDestroy;
32 import javax.inject.Inject;
33 import javax.inject.Singleton;
34
35 /**
36  * This class listens for OvsdbNode creation/removal/update in Network Topology Operational DS.
37  * This is used to handle add/update/remove of TEPs of switches into/from ITM.
38  */
39 @Singleton
40 public class OvsdbNodeListener extends AsyncDataTreeChangeListenerBase<Node, OvsdbNodeListener>
41     implements AutoCloseable {
42     private static final Logger LOG = LoggerFactory.getLogger(OvsdbNodeListener.class);
43     private DataBroker dataBroker;
44
45     @Inject
46     public OvsdbNodeListener(final DataBroker dataBroker) {
47         super(Node.class, OvsdbNodeListener.class);
48         this.dataBroker = dataBroker;
49         LOG.trace("OvsdbNodeListener Created");
50     }
51
52     @PostConstruct
53     public void start() throws Exception {
54         registerListener(this.dataBroker);
55         LOG.info("OvsdbNodeListener Started");
56     }
57
58     @Override
59     @PreDestroy
60     public void close() throws Exception {
61         LOG.trace("OvsdbNodeListener Closed");
62     }
63
64     private void registerListener(final DataBroker db) {
65         try {
66             registerListener(LogicalDatastoreType.OPERATIONAL, db);
67         } catch (final Exception e) {
68             LOG.error("Network Topology Node listener registration failed.", e);
69             throw new IllegalStateException("Network Topology Node listener registration failed.", e);
70         }
71     }
72
73     @Override protected InstanceIdentifier<Node> getWildCardPath() {
74         return InstanceIdentifier.builder(NetworkTopology.class).child(Topology.class)
75             .child(Node.class).build();
76     }
77
78     @Override protected OvsdbNodeListener getDataTreeChangeListener() {
79         return OvsdbNodeListener.this;
80     }
81
82     @Override protected void remove(InstanceIdentifier<Node> identifier, Node ovsdbNode) {
83         LOG.trace("OvsdbNodeListener called for Ovsdb Node Remove.");
84     }
85
86     @Override protected void add(InstanceIdentifier<Node> identifier, Node ovsdbNodeNew) {
87         String newTepIp = "", tzName = "", dpnBridgeName = "", bridgeName = "";
88         String strDpnId = "";
89         OvsdbNodeAugmentation ovsdbNewNodeAugmentation = null;
90
91         LOG.trace("OvsdbNodeListener called for Ovsdb Node ({}) Add.",
92             ovsdbNodeNew.getNodeId().getValue());
93
94         // check for OVS bridge node
95         OvsdbBridgeAugmentation ovsdbNewBridgeAugmentation =
96             ovsdbNodeNew.getAugmentation(OvsdbBridgeAugmentation.class);
97         if (ovsdbNewBridgeAugmentation != null) {
98             bridgeName = ovsdbNewBridgeAugmentation.getBridgeName().getValue();
99
100             // Read DPN ID from OVSDBBridgeAugmentation
101             strDpnId = ItmUtils.getStrDatapathId(ovsdbNodeNew);
102             if (strDpnId == null || strDpnId.isEmpty()) {
103                 LOG.error("OvsdbBridgeAugmentation ADD: DPID for bridge {} is NULL.",
104                     bridgeName);
105                 return;
106             }
107
108             Node ovsdbNodeFromBridge = ItmUtils.getOvsdbNode(ovsdbNewBridgeAugmentation, dataBroker);
109             // check for OVSDB node
110             if (ovsdbNodeFromBridge != null) {
111                 ovsdbNewNodeAugmentation = ovsdbNodeFromBridge.getAugmentation(OvsdbNodeAugmentation.class);
112             } else {
113                 LOG.error("Ovsdb Node could not be fetched from Oper DS for bridge {}.",
114                     bridgeName);
115                 return;
116             }
117         }
118
119         if (ovsdbNewNodeAugmentation != null) {
120             // get OVSDB other_configs list from old ovsdb node
121             OvsdbOtherConfigInfo ovsdbOtherConfigObj = getOvsdbNodeOtherConfigs(ovsdbNewNodeAugmentation);
122             if (ovsdbOtherConfigObj == null) {
123                 return;
124             }
125             // store other config required parameters
126             newTepIp = ovsdbOtherConfigObj.getTepIp();
127             tzName = ovsdbOtherConfigObj.getTzName();
128             dpnBridgeName = ovsdbOtherConfigObj.getDpnBrName();
129
130             // if bridge received is the one configured for TEPs from OVS side or
131             // if it is br-int, then add TEP into Config DS
132             if (dpnBridgeName.equals(bridgeName)) {
133                 // check if TEP-IP is configured or not
134                 if (!newTepIp.isEmpty()) {
135                     LOG.trace("Ovs Node [{}] is configured with TEP-IP.",
136                         ovsdbNodeNew.getNodeId().getValue());
137                 } else {
138                     LOG.trace("Ovs Node [{}] is not configured with TEP-IP. Nothing to do.",
139                         ovsdbNodeNew.getNodeId().getValue());
140                     return;
141                 }
142
143                 LOG.trace("TEP-IP: {}, TZ name: {}, DPN Bridge Name: {}, Bridge DPID: {}", newTepIp,
144                     tzName, dpnBridgeName, strDpnId);
145
146                 // Enqueue 'add TEP received from southbound OVSDB into ITM config DS' operation
147                 // into DataStoreJobCoordinator
148                 DataStoreJobCoordinator coordinator = DataStoreJobCoordinator.getInstance();
149                 OvsdbTepAddWorker addWorker =
150                     new OvsdbTepAddWorker(newTepIp, strDpnId, tzName, dataBroker);
151                 coordinator.enqueueJob(newTepIp, addWorker);
152             }
153         }
154     }
155
156     @Override protected void update(InstanceIdentifier<Node> identifier, Node ovsdbNodeOld,
157         Node ovsdbNodeNew) {
158         String newTepIp = "", oldTepIp = "";
159         String tzName = "", oldTzName = "";
160         String oldDpnBridgeName = "", newDpnBridgeName = "";
161         boolean isOtherConfigUpdated = false, isOtherConfigDeleted = false;
162         boolean isTepIpAdded = false, isTepIpRemoved = false;
163         boolean isTzChanged = false, isDpnBrChanged = false;
164
165         LOG.trace("OvsdbNodeListener called for Ovsdb Node ({}) Update.",
166             ovsdbNodeOld.getNodeId().getValue());
167
168         // get OVSDB other_configs list from old ovsdb node
169         OvsdbOtherConfigInfo newOtherConfigsInfoObj = getOvsdbNodeOtherConfigs(
170             ovsdbNodeNew.getAugmentation(OvsdbNodeAugmentation.class));
171
172         // get OVSDB other_configs list from new ovsdb node
173         OvsdbOtherConfigInfo oldOtherConfigInfoObj = getOvsdbNodeOtherConfigs(
174             ovsdbNodeOld.getAugmentation(OvsdbNodeAugmentation.class));
175
176         if (oldOtherConfigInfoObj == null && newOtherConfigsInfoObj == null) {
177             LOG.trace("OtherConfig is not received in old and new Ovsdb Nodes.");
178             return;
179         }
180
181         if (oldOtherConfigInfoObj != null && newOtherConfigsInfoObj == null) {
182             isOtherConfigDeleted = true;
183             LOG.trace("OtherConfig is deleted from Ovsdb node: {}",
184                 ovsdbNodeOld.getNodeId().getValue());
185         }
186
187         // store other config required parameters
188         if (newOtherConfigsInfoObj != null) {
189             newTepIp = newOtherConfigsInfoObj.getTepIp();
190             tzName = newOtherConfigsInfoObj.getTzName();
191             newDpnBridgeName = newOtherConfigsInfoObj.getDpnBrName();
192
193             // All map params have been read, now clear it up.
194             newOtherConfigsInfoObj = null;
195         }
196
197         if (oldOtherConfigInfoObj != null) {
198             oldDpnBridgeName = oldOtherConfigInfoObj.getDpnBrName();
199             oldTzName = oldOtherConfigInfoObj.getTzName();
200             oldTepIp = oldOtherConfigInfoObj.getTepIp();
201
202             // All map params have been read, now clear it up.
203             oldOtherConfigInfoObj = null;
204         }
205
206         if (!isOtherConfigDeleted) {
207             isTepIpRemoved = isTepIpRemoved(oldTepIp, newTepIp);
208             isTepIpAdded = isTepIpAdded(oldTepIp, newTepIp);
209
210             if (!oldTepIp.equals(newTepIp)) {
211                 isOtherConfigUpdated = true;
212             }
213             if (!oldTzName.equals(tzName)) {
214                 isOtherConfigUpdated = true;
215                 if (!oldTepIp.isEmpty() && !newTepIp.isEmpty()) {
216                     isTzChanged = true;
217                     LOG.trace("tzname is changed from {} to {} for TEP-IP: {}", oldTzName, tzName, newTepIp);
218                 }
219             }
220             if (!oldDpnBridgeName.equals(newDpnBridgeName)) {
221                 isOtherConfigUpdated = true;
222                 if (!oldTepIp.isEmpty() && !newTepIp.isEmpty()) {
223                     isDpnBrChanged = true;
224                     LOG.trace("dpn-br-name is changed from {} to {} for TEP-IP: {}", oldDpnBridgeName, newDpnBridgeName, newTepIp);
225                 }
226             }
227
228             if (!isOtherConfigUpdated) {
229                 LOG.trace("No updates in the other config parameters. Nothing to do.");
230                 return;
231             }
232         }
233
234         String strOldDpnId = "", strNewDpnId = "";
235         // handle TEP-remove in remove case, TZ change case, Bridge change case
236         if (isOtherConfigDeleted || isTepIpRemoved || isTzChanged || isDpnBrChanged) {
237             strOldDpnId = ItmUtils.getBridgeDpid(ovsdbNodeNew, oldDpnBridgeName,
238                 dataBroker);
239             if (strOldDpnId == null || strOldDpnId.isEmpty()) {
240                 LOG.error(
241                     "TEP {} cannot be deleted. DPN-ID for bridge {} is NULL.",
242                     oldTepIp, oldDpnBridgeName);
243                 return;
244             }
245             // remove TEP
246             LOG.trace(
247                 "Update case: Removing TEP-IP: {}, TZ name: {}, DPN Bridge Name: {}, Bridge DPID: {}",
248                 oldTepIp, oldTzName, oldDpnBridgeName, strOldDpnId);
249
250             // Enqueue 'remove TEP from TZ' operation into DataStoreJobCoordinator
251             DataStoreJobCoordinator coordinator = DataStoreJobCoordinator.getInstance();
252             OvsdbTepRemoveWorker
253                 removeWorker = new OvsdbTepRemoveWorker(oldTepIp, strOldDpnId, oldTzName, dataBroker);
254             coordinator.enqueueJob(oldTepIp, removeWorker);
255         }
256         // handle TEP-add in add case, TZ change case, Bridge change case
257         if (isTepIpAdded || isTzChanged || isDpnBrChanged) {
258             // get Datapath ID for bridge
259             strNewDpnId = ItmUtils.getBridgeDpid(ovsdbNodeNew, newDpnBridgeName,
260                 dataBroker);
261             if (strNewDpnId == null || strNewDpnId.isEmpty()) {
262                 LOG.error(
263                     "TEP {} cannot be added. DPN-ID for bridge {} is NULL.",
264                     newTepIp, newDpnBridgeName);
265                 return;
266             }
267             LOG.trace(
268                 "Update case: Adding TEP-IP: {}, TZ name: {}, DPN Bridge Name: {}, Bridge DPID: {}",
269                 newTepIp, tzName, newDpnBridgeName, strNewDpnId);
270
271             // Enqueue 'add TEP into new TZ' operation into DataStoreJobCoordinator
272             DataStoreJobCoordinator coordinator = DataStoreJobCoordinator.getInstance();
273             OvsdbTepAddWorker
274                 addWorker = new OvsdbTepAddWorker(newTepIp, strNewDpnId, tzName, dataBroker);
275             coordinator.enqueueJob(newTepIp, addWorker);
276         }
277     }
278
279     public boolean isTepIpRemoved(String oldTepIp, String newTepIp) {
280         if (!oldTepIp.isEmpty() && newTepIp.isEmpty()) {
281             return true;
282         }
283         return false;
284     }
285
286     public boolean isTepIpAdded(String oldTepIp, String newTepIp) {
287         if (oldTepIp.isEmpty() && !newTepIp.isEmpty()) {
288             return true;
289         }
290         return false;
291     }
292
293     public OvsdbOtherConfigInfo getOvsdbNodeOtherConfigs(OvsdbNodeAugmentation ovsdbNodeAugmentation) {
294         String tepIp = "", tzName = "", dpnBridgeName = "";
295
296         if (ovsdbNodeAugmentation == null) {
297             return null;
298         }
299
300         List<OpenvswitchOtherConfigs> ovsdbNodeOtherConfigsList =
301             ovsdbNodeAugmentation.getOpenvswitchOtherConfigs();
302         if (ovsdbNodeOtherConfigsList == null) {
303             LOG.error("Other-configs list does not exist in the OVSDB Node Augmentation.");
304             return null;
305         }
306
307         OvsdbOtherConfigInfo otherConfigInfoObj = new OvsdbOtherConfigInfo();
308
309         if (otherConfigInfoObj == null) {
310             LOG.error("Memory could not be allocated. System fatal error.");
311             return null;
312         }
313
314         if (ovsdbNodeOtherConfigsList != null) {
315             for (OpenvswitchOtherConfigs otherConfig : ovsdbNodeOtherConfigsList) {
316                 if (otherConfig.getOtherConfigKey().equals("tep-ip")) {
317                     tepIp = otherConfig.getOtherConfigValue();
318                     otherConfigInfoObj.setTepIp(tepIp);
319                 } else if (otherConfig.getOtherConfigKey().equals("tzname")) {
320                     tzName = otherConfig.getOtherConfigValue();
321                     otherConfigInfoObj.setTzName(tzName);
322                 } else if (otherConfig.getOtherConfigKey().equals("dpn-br-name")) {
323                     dpnBridgeName = otherConfig.getOtherConfigValue();
324                     otherConfigInfoObj.setDpnBrName(dpnBridgeName);
325                 } else {
326                     LOG.trace("other_config {}:{}", otherConfig.getOtherConfigKey(),
327                         otherConfig.getOtherConfigValue());
328                 }
329             }
330             LOG.trace("{}", otherConfigInfoObj.toString());
331         }
332         return otherConfigInfoObj;
333     }
334     // End of class
335 }