2 * Copyright (c) 2016, 2017 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.genius.itm.listeners;
11 import java.math.BigInteger;
12 import java.time.Duration;
13 import java.util.List;
14 import javax.inject.Inject;
15 import javax.inject.Singleton;
16 import org.eclipse.jdt.annotation.NonNull;
17 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
18 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
19 import org.opendaylight.genius.datastoreutils.listeners.DataTreeEventCallbackRegistrar;
20 import org.opendaylight.genius.itm.commons.OvsdbTepInfo;
21 import org.opendaylight.genius.itm.confighelpers.OvsdbTepAddWorker;
22 import org.opendaylight.genius.itm.confighelpers.OvsdbTepRemoveWorker;
23 import org.opendaylight.genius.itm.globals.ITMConstants;
24 import org.opendaylight.genius.itm.impl.ItmUtils;
25 import org.opendaylight.genius.mdsalutil.MDSALUtil;
26 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
27 import org.opendaylight.serviceutils.tools.mdsal.listener.AbstractSyncDataTreeChangeListener;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.ItmConfig;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.DpnEndpoints;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.DPNTEPsInfo;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.DPNTEPsInfoKey;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.dpn.teps.info.TunnelEndPoints;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.dpn.teps.info.tunnel.end.points.TzMembership;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.OpenvswitchExternalIds;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.OpenvswitchOtherConfigs;
42 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
43 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
44 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
45 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
50 * This class listens for OvsdbNode creation/removal/update in Network Topology Operational DS.
51 * This is used to handle add/update/remove of TEPs of switches into/from ITM.
54 public class OvsdbNodeListener extends AbstractSyncDataTreeChangeListener<Node> {
56 private static final Logger LOG = LoggerFactory.getLogger(OvsdbNodeListener.class);
58 private final DataBroker dataBroker;
59 private final JobCoordinator jobCoordinator;
60 private final ItmConfig itmConfig;
61 private final DataTreeEventCallbackRegistrar eventCallbacks;
64 public OvsdbNodeListener(DataBroker dataBroker, ItmConfig itmConfig, JobCoordinator jobCoordinator,
65 DataTreeEventCallbackRegistrar eventCallbacks) {
66 super(dataBroker, LogicalDatastoreType.OPERATIONAL,
67 InstanceIdentifier.create(NetworkTopology.class).child(Topology.class).child(Node.class));
68 this.dataBroker = dataBroker;
69 this.jobCoordinator = jobCoordinator;
70 this.itmConfig = itmConfig;
71 this.eventCallbacks = eventCallbacks;
75 public void add(@NonNull InstanceIdentifier<Node> instanceIdentifier, @NonNull Node ovsdbNodeNew) {
76 String bridgeName = null;
78 OvsdbNodeAugmentation ovsdbNewNodeAugmentation = null;
80 LOG.trace("OvsdbNodeListener called for Ovsdb Node ({}) Add.", ovsdbNodeNew.getNodeId().getValue());
82 // check for OVS bridge node
83 OvsdbBridgeAugmentation ovsdbNewBridgeAugmentation = ovsdbNodeNew
84 .augmentation(OvsdbBridgeAugmentation.class);
86 if (ovsdbNewBridgeAugmentation != null) {
87 processBridgeUpdate(ovsdbNewBridgeAugmentation, true);
92 public void remove(@NonNull InstanceIdentifier<Node> instanceIdentifier, @NonNull Node removedDataObject) {
93 LOG.trace("OvsdbNodeListener called for Ovsdb Node {} Remove.", removedDataObject);
94 processBridgeUpdate(removedDataObject, false);
98 public void update(@NonNull InstanceIdentifier<Node> instanceIdentifier, @NonNull Node originalOvsdbNode,
99 @NonNull Node updatedOvsdbNode) {
100 String newLocalIp = null;
101 String oldLocalIp = null;
102 String tzName = null;
103 String oldTzName = null;
104 String oldDpnBridgeName = null;
105 String newDpnBridgeName = null;
106 boolean newOfTunnel = false;
107 boolean isTepInfoUpdated = false;
108 boolean isTepInfoDeleted = false;
109 boolean isLocalIpAdded = false;
110 boolean isLocalIpRemoved = false;
111 boolean isLocalIpUpdated = false;
112 boolean isTzChanged = false;
113 boolean isDpnBrChanged = false;
115 LOG.trace("OvsdbNodeListener called for Ovsdb Node ({}) Update.", originalOvsdbNode.getNodeId().getValue());
117 // If this is a bridge update, see if dpid was added. If so, need to
118 // addTep, as TEP would not be added in node add case above
119 if (isBridgeDpIdAdded(originalOvsdbNode, updatedOvsdbNode)) {
120 processBridgeUpdate(updatedOvsdbNode, true);
124 // get OVSDB TEP info from old ovsdb node
125 OvsdbTepInfo newTepInfoObj = getOvsdbTepInfo(
126 updatedOvsdbNode.augmentation(OvsdbNodeAugmentation.class));
128 // get OVSDB TEP info from new ovsdb node
129 OvsdbTepInfo oldTepInfoObj = getOvsdbTepInfo(
130 originalOvsdbNode.augmentation(OvsdbNodeAugmentation.class));
132 if (oldTepInfoObj == null && newTepInfoObj == null) {
133 LOG.trace("Tep Info is not received in old and new Ovsdb Nodes.");
137 if (oldTepInfoObj != null && newTepInfoObj == null) {
138 isTepInfoDeleted = true;
139 LOG.trace("Tep Info is deleted from Ovsdb node: {}", originalOvsdbNode.getNodeId().getValue());
142 // store TEP info required parameters
143 if (newTepInfoObj != null) {
144 tzName = newTepInfoObj.getTzName();
145 newLocalIp = newTepInfoObj.getLocalIp();
146 newDpnBridgeName = newTepInfoObj.getBrName();
147 newOfTunnel = newTepInfoObj.getOfTunnel();
150 if (oldTepInfoObj != null) {
151 oldLocalIp = oldTepInfoObj.getLocalIp();
152 oldDpnBridgeName = oldTepInfoObj.getBrName();
153 oldTzName = oldTepInfoObj.getTzName();
156 // handle case when TEP parameters are not configured from switch side
157 if (newLocalIp == null && oldLocalIp == null) {
158 LOG.trace("TEP info Local IP parameters are not specified in old and new Ovsdb Nodes.");
162 if (!isTepInfoDeleted) {
163 isLocalIpRemoved = isLocalIpRemoved(oldLocalIp, newLocalIp);
164 isLocalIpAdded = isLocalIpAdded(oldLocalIp, newLocalIp);
165 isLocalIpUpdated = isLocalIpUpdated(oldLocalIp, newLocalIp);
167 if (isLocalIpAdded || isLocalIpRemoved || isLocalIpUpdated) {
168 isTepInfoUpdated = true;
170 if (isTzUpdated(oldTzName, tzName)) {
171 isTepInfoUpdated = true;
172 if (oldLocalIp != null && newLocalIp != null) {
174 LOG.trace("tzname is changed from {} to {} for Local IP: {}", oldTzName, tzName, newLocalIp);
177 if (isDpnUpdated(oldDpnBridgeName, newDpnBridgeName)) {
178 isTepInfoUpdated = true;
179 if (oldLocalIp != null && newLocalIp != null) {
180 isDpnBrChanged = true;
181 LOG.trace("dpn-br-name is changed from {} to {} for Local IP: {}", oldDpnBridgeName,
182 newDpnBridgeName, newLocalIp);
186 if (!isTepInfoUpdated) {
187 LOG.trace("Old TEP (local-ip: {}, tz-name: {}) and New TEP (local-ip: {}, tz-name: {}). "
188 + "No updates in the TEP Info parameters. Nothing to do.", oldLocalIp, oldTzName,
194 LOG.trace("TepInfo state change flags (isTepInfoUpdated: {}, isTepInfoDeleted: {}, isLocalIpRemoved: {},"
195 + "isLocalIpAdded: {}, isLocalIpUpdated: {}, isTzChanged:{}, isDpnBrChanged: {})",
196 isTepInfoUpdated, isTepInfoDeleted, isLocalIpRemoved, isLocalIpAdded, isLocalIpUpdated,
197 isTzChanged, isDpnBrChanged);
199 String jobKey = oldLocalIp;
200 // handle TEP-remove in remove case, tep-ip update case, TZ change case, Bridge change case
201 if (isTepInfoDeleted || isLocalIpRemoved || isTzChanged || isDpnBrChanged || isLocalIpUpdated) {
202 // TBD: Move this time taking operations into DataStoreJobCoordinator
203 String strOldDpnId = ItmUtils.getBridgeDpid(originalOvsdbNode, oldDpnBridgeName, dataBroker);
204 if (strOldDpnId == null || strOldDpnId.isEmpty()) {
205 LOG.error("TEP {} cannot be deleted. DPID for bridge {} is NULL.", oldLocalIp, oldDpnBridgeName);
208 addOrRemoveTep(oldTzName, strOldDpnId, jobKey, oldLocalIp, oldDpnBridgeName, false, false);
210 // handle TEP-add in add case, tep-ip update case, TZ change case, Bridge change case
211 if (isLocalIpAdded || isTzChanged || isDpnBrChanged || isLocalIpUpdated) {
212 // TBD: Move this time taking operations into DataStoreJobCoordinator
213 String strNewDpnId = ItmUtils.getBridgeDpid(updatedOvsdbNode, newDpnBridgeName, dataBroker);
214 if (strNewDpnId == null || strNewDpnId.isEmpty()) {
215 LOG.error("TEP {} cannot be added. DPID for bridge {} is NULL.", newLocalIp, newDpnBridgeName);
219 * Special handling for TEP movement from one TZ to another TZ
220 * Register for DpnTepsInfo remove event to make sure TEP remove is happened through ITM internal logic,
221 * then after perform TEP addition into updated TZ
224 IpAddress tepIpAddress = IpAddressBuilder.getDefaultInstance(newLocalIp);
225 BigInteger dpnId = MDSALUtil.getDpnId(strNewDpnId);
226 String tos = itmConfig.getDefaultTunnelTos();
227 Class<? extends TunnelTypeBase> tunnelType = TunnelTypeVxlan.class;
228 List<TzMembership> zones = ItmUtils.createTransportZoneMembership(oldTzName);
229 TunnelEndPoints tunnelEndPoints = ItmUtils.createDummyTunnelEndPoints(dpnId, tepIpAddress, newOfTunnel,
230 tos, zones, tunnelType);
231 String finalTzName = tzName;
232 String finalJobKey = jobKey;
233 String finalLocalIp = newLocalIp;
234 String finalDpnBridgeName = newDpnBridgeName;
235 boolean finalOfTunnel = newOfTunnel;
237 InstanceIdentifier<TunnelEndPoints> tunnelEndPointsIdentifier =
238 InstanceIdentifier.builder(DpnEndpoints.class).child(DPNTEPsInfo.class,
239 new DPNTEPsInfoKey(dpnId)).child(TunnelEndPoints.class, tunnelEndPoints.key()).build();
240 eventCallbacks.onRemove(LogicalDatastoreType.CONFIGURATION, tunnelEndPointsIdentifier, (unused) -> {
241 LOG.info("TZ movement case: callback event for a deletion of {} from DpnTepsInfo.", dpnId);
242 addOrRemoveTep(finalTzName, strNewDpnId, finalJobKey, finalLocalIp,
243 finalDpnBridgeName, finalOfTunnel, true);
244 return DataTreeEventCallbackRegistrar.NextAction.UNREGISTER;
245 }, Duration.ofMillis(5000), (id) -> {
246 LOG.info("TZ movement case: callback event timed-out for a deletion of {} from DpnTepsInfo.",
248 addOrRemoveTep(finalTzName, strNewDpnId, finalJobKey, finalLocalIp,
249 finalDpnBridgeName, finalOfTunnel, true);
252 jobKey = isLocalIpUpdated ? oldLocalIp : newLocalIp;
253 addOrRemoveTep(tzName, strNewDpnId, jobKey, newLocalIp, newDpnBridgeName, newOfTunnel, true);
258 private void addOrRemoveTep(String tzName, String strDpnId, String jobKey, String localIp, String bridgeName,
259 boolean newOfTunnel, boolean isTepAdd) {
260 // check if defTzEnabled flag is false in config file,
261 // if flag is OFF, then no need to add TEP into ITM config DS.
262 if (tzName == null || tzName.equals(ITMConstants.DEFAULT_TRANSPORT_ZONE)) {
263 boolean defTzEnabled = itmConfig.isDefTzEnabled();
266 LOG.info("TEP ({}) cannot be added into {} when def-tz-enabled flag is false.", localIp,
267 ITMConstants.DEFAULT_TRANSPORT_ZONE);
269 LOG.info("TEP ({}) cannot be removed from {} when def-tz-enabled flag is false.", localIp,
270 ITMConstants.DEFAULT_TRANSPORT_ZONE);
274 tzName = ITMConstants.DEFAULT_TRANSPORT_ZONE;
279 LOG.trace("Update case: Adding TEP-IP: {}, TZ name: {}, Bridge Name: {}, Bridge DPID: {}, of-tunnel: {}",
280 localIp, tzName, bridgeName, strDpnId, newOfTunnel);
282 // Enqueue 'add TEP into new TZ' operation into DataStoreJobCoordinator
283 jobCoordinator.enqueueJob(jobKey,
284 new OvsdbTepAddWorker(localIp, strDpnId, tzName, newOfTunnel, dataBroker));
287 LOG.trace("Update case: Removing TEP-IP: {}, TZ name: {}, Bridge Name: {}, Bridge DPID: {}", localIp,
288 tzName, bridgeName, strDpnId);
290 // Enqueue 'remove TEP from TZ' operation into DataStoreJobCoordinator
291 jobCoordinator.enqueueJob(jobKey, new OvsdbTepRemoveWorker(localIp, strDpnId, tzName, dataBroker));
295 private boolean isLocalIpRemoved(String oldTepIp, String newTepIp) {
296 return oldTepIp != null && newTepIp == null;
299 private boolean isLocalIpAdded(String oldTepIp, String newTepIp) {
300 return oldTepIp == null && newTepIp != null;
303 private boolean isLocalIpUpdated(String oldTepIp, String newTepIp) {
304 return oldTepIp != null && newTepIp != null && !oldTepIp.equals(newTepIp);
307 private boolean isTzUpdated(String oldTzName, String tzName) {
308 return oldTzName == null && tzName != null || oldTzName != null && tzName == null
309 || oldTzName != null && !oldTzName.equals(tzName);
312 private boolean isDpnUpdated(String oldDpnBridgeName, String dpnBridgeName) {
313 return oldDpnBridgeName == null && dpnBridgeName != null || oldDpnBridgeName != null && dpnBridgeName == null
314 || oldDpnBridgeName != null && !oldDpnBridgeName.equals(dpnBridgeName);
317 private boolean isBridgeDpIdAdded(Node ovsdbNodeOld, Node ovsdbNodeNew) {
318 String oldBridgeName = null;
319 String oldDpId = null;
320 String newDpId = null;
322 OvsdbBridgeAugmentation ovsdbNewBridgeAugmentation =
323 ovsdbNodeNew.augmentation(OvsdbBridgeAugmentation.class);
324 if (ovsdbNewBridgeAugmentation != null) {
325 // Read DPID from OVSDBBridgeAugmentation
326 newDpId = ItmUtils.getStrDatapathId(ovsdbNewBridgeAugmentation);
329 OvsdbBridgeAugmentation ovsdbOldBridgeAugmentation =
330 ovsdbNodeOld.augmentation(OvsdbBridgeAugmentation.class);
331 if (ovsdbOldBridgeAugmentation != null) {
332 oldBridgeName = ovsdbNewBridgeAugmentation.getBridgeName().getValue();
333 // Read DPID from OVSDBBridgeAugmentation
334 oldDpId = ItmUtils.getStrDatapathId(ovsdbOldBridgeAugmentation);
336 if (oldDpId == null && newDpId != null) {
337 LOG.trace("DpId changed to {} for bridge {}", newDpId, oldBridgeName);
343 private OvsdbTepInfo getOvsdbTepInfo(OvsdbNodeAugmentation ovsdbNodeAugmentation) {
344 if (ovsdbNodeAugmentation == null) {
348 List<OpenvswitchOtherConfigs> ovsdbNodeOtherConfigsList = ovsdbNodeAugmentation.getOpenvswitchOtherConfigs();
349 if (ovsdbNodeOtherConfigsList == null) {
350 //Local IP is not configured
351 LOG.debug("OtherConfigs list does not exist in the OVSDB Node Augmentation.");
355 OvsdbTepInfo ovsdbTepInfoObj = new OvsdbTepInfo();
357 for (OpenvswitchOtherConfigs otherConfigs : ovsdbNodeOtherConfigsList) {
358 if (ITMConstants.OTH_CFG_TEP_PARAM_KEY_LOCAL_IP.equals(otherConfigs.getOtherConfigKey())) {
359 String tepIp = otherConfigs.getOtherConfigValue();
360 ovsdbTepInfoObj.setLocalIp(tepIp);
364 List<OpenvswitchExternalIds> ovsdbNodeExternalIdsList = ovsdbNodeAugmentation.getOpenvswitchExternalIds();
365 if (ovsdbNodeExternalIdsList == null) {
366 LOG.debug("ExternalIds list does not exist in the OVSDB Node Augmentation.");
368 for (OpenvswitchExternalIds externalId : ovsdbNodeExternalIdsList) {
369 switch (externalId.getExternalIdKey()) {
370 case ITMConstants.EXT_ID_TEP_PARAM_KEY_TZNAME:
371 ovsdbTepInfoObj.setTzName(externalId.getExternalIdValue());
373 case ITMConstants.EXT_ID_TEP_PARAM_KEY_BR_NAME:
374 ovsdbTepInfoObj.setBrName(externalId.getExternalIdValue());
376 case ITMConstants.EXT_ID_TEP_PARAM_KEY_OF_TUNNEL:
377 ovsdbTepInfoObj.setOfTunnel(Boolean.parseBoolean(externalId.getExternalIdValue()));
384 return ovsdbTepInfoObj;
387 private void processBridgeUpdate(Node ovsdbNodeNew, boolean isBridgeAdd) {
388 OvsdbBridgeAugmentation ovsdbNewBridgeAugmentation =
389 ovsdbNodeNew.augmentation(OvsdbBridgeAugmentation.class);
390 if (ovsdbNewBridgeAugmentation != null) {
391 processBridgeUpdate(ovsdbNewBridgeAugmentation, isBridgeAdd);
395 private void processBridgeUpdate(OvsdbBridgeAugmentation ovsdbNewBridgeAugmentation, boolean isBridgeAdd) {
396 String bridgeName = null;
397 String strDpnId = null;
398 OvsdbNodeAugmentation ovsdbNewNodeAugmentation = null;
400 if (ovsdbNewBridgeAugmentation != null) {
401 bridgeName = ovsdbNewBridgeAugmentation.getBridgeName().getValue();
403 // Read DPID from OVSDBBridgeAugmentation
404 strDpnId = ItmUtils.getStrDatapathId(ovsdbNewBridgeAugmentation);
405 if (strDpnId == null || strDpnId.isEmpty()) {
406 LOG.trace("OvsdbBridgeAugmentation processBridgeUpdate: DPID for bridge {} is NULL.", bridgeName);
410 // TBD: Move this time taking operations into DataStoreJobCoordinator
411 Node ovsdbNodeFromBridge = ItmUtils.getOvsdbNode(ovsdbNewBridgeAugmentation, dataBroker);
412 // check for OVSDB node. NOTE: it can be null during bridge removal notification
413 // when switch is disconnected
414 if (ovsdbNodeFromBridge != null) {
415 ovsdbNewNodeAugmentation = ovsdbNodeFromBridge.augmentation(OvsdbNodeAugmentation.class);
417 LOG.warn("processBridgeUpdate: bridge {} removal case when Switch is disconnected."
418 + "Hence, Ovsdb Node could not be fetched from Oper DS.", bridgeName);
423 if (ovsdbNewNodeAugmentation != null) {
424 OvsdbTepInfo ovsdbTepInfo = getOvsdbTepInfo(ovsdbNewNodeAugmentation);
426 if (ovsdbTepInfo == null) {
427 LOG.trace("processBridgeUpdate: No Tep Info");
430 // store TEP info required parameters
431 String newLocalIp = ovsdbTepInfo.getLocalIp();
432 String tzName = ovsdbTepInfo.getTzName();
433 String newBridgeName = ovsdbTepInfo.getBrName();
434 boolean ofTunnel = ovsdbTepInfo.getOfTunnel();
436 // check if Local IP is configured or not
437 if (newLocalIp != null && !newLocalIp.isEmpty()) {
438 // if bridge received is the one configured for TEPs from OVS side or
439 // if it is br-int, then add TEP into Config DS
440 if (newBridgeName.equals(bridgeName)) {
441 LOG.trace("processBridgeUpdate for bridge {} that is configured with Local IP.", bridgeName);
442 String jobKey = newLocalIp;
443 // add or remove tep based on bridge (br-int) is added or removed
444 addOrRemoveTep(tzName, strDpnId, jobKey, newLocalIp, newBridgeName, ofTunnel, isBridgeAdd);
446 LOG.trace("processBridgeUpdate invoked for bridge {}, nothing to do.", bridgeName);
449 LOG.trace("processBridgeUpdate for bridge {} without Local IP set for ovs node. Nothing to do.",