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.time.Duration;
12 import java.util.List;
13 import javax.inject.Inject;
14 import javax.inject.Singleton;
15 import org.eclipse.jdt.annotation.NonNull;
16 import org.opendaylight.genius.datastoreutils.listeners.DataTreeEventCallbackRegistrar;
17 import org.opendaylight.genius.itm.commons.OvsdbTepInfo;
18 import org.opendaylight.genius.itm.confighelpers.OvsdbTepAddWorker;
19 import org.opendaylight.genius.itm.confighelpers.OvsdbTepRemoveWorker;
20 import org.opendaylight.genius.itm.globals.ITMConstants;
21 import org.opendaylight.genius.itm.impl.ItmUtils;
22 import org.opendaylight.genius.mdsalutil.MDSALUtil;
23 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
24 import org.opendaylight.mdsal.binding.api.DataBroker;
25 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
26 import org.opendaylight.serviceutils.tools.listener.AbstractSyncDataTreeChangeListener;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.ItmConfig;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.DpnEndpoints;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.DPNTEPsInfo;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.DPNTEPsInfoKey;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.dpn.teps.info.TunnelEndPoints;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.dpn.teps.info.tunnel.end.points.TzMembership;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.OpenvswitchExternalIds;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.OpenvswitchOtherConfigs;
41 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
42 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
43 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
44 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
45 import org.opendaylight.yangtools.yang.common.Uint64;
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 Uint64 dpnId = MDSALUtil.getDpnId(strNewDpnId);
226 String tos = itmConfig.getDefaultTunnelTos();
227 Class<? extends TunnelTypeBase> tunnelType = TunnelTypeVxlan.class;
228 List<TzMembership> zones = ItmUtils.createTransportZoneMembership(oldTzName);
230 String portName = itmConfig.getPortname() == null ? ITMConstants.DUMMY_PORT : itmConfig.getPortname();
231 int vlanId = itmConfig.getVlanId() != null ? itmConfig.getVlanId().toJava()
232 : ITMConstants.DUMMY_VLANID;
234 TunnelEndPoints tunnelEndPoints = ItmUtils.createDummyTunnelEndPoints(dpnId, tepIpAddress, newOfTunnel,
235 tos, zones, tunnelType, portName, vlanId);
236 String finalTzName = tzName;
237 String finalJobKey = jobKey;
238 String finalLocalIp = newLocalIp;
239 String finalDpnBridgeName = newDpnBridgeName;
240 boolean finalOfTunnel = newOfTunnel;
242 InstanceIdentifier<TunnelEndPoints> tunnelEndPointsIdentifier =
243 InstanceIdentifier.builder(DpnEndpoints.class).child(DPNTEPsInfo.class,
244 new DPNTEPsInfoKey(dpnId)).child(TunnelEndPoints.class, tunnelEndPoints.key()).build();
245 eventCallbacks.onRemove(LogicalDatastoreType.CONFIGURATION, tunnelEndPointsIdentifier, (unused) -> {
246 LOG.info("TZ movement case: callback event for a deletion of {} from DpnTepsInfo.", dpnId);
247 addOrRemoveTep(finalTzName, strNewDpnId, finalJobKey, finalLocalIp,
248 finalDpnBridgeName, finalOfTunnel, true);
249 return DataTreeEventCallbackRegistrar.NextAction.UNREGISTER;
250 }, Duration.ofMillis(5000), (id) -> {
251 LOG.info("TZ movement case: callback event timed-out for a deletion of {} from DpnTepsInfo.",
253 addOrRemoveTep(finalTzName, strNewDpnId, finalJobKey, finalLocalIp,
254 finalDpnBridgeName, finalOfTunnel, true);
257 jobKey = isLocalIpUpdated ? oldLocalIp : newLocalIp;
258 addOrRemoveTep(tzName, strNewDpnId, jobKey, newLocalIp, newDpnBridgeName, newOfTunnel, true);
263 private void addOrRemoveTep(String tzName, String strDpnId, String jobKey, String localIp, String bridgeName,
264 boolean newOfTunnel, boolean isTepAdd) {
265 // check if defTzEnabled flag is false in config file,
266 // if flag is OFF, then no need to add TEP into ITM config DS.
267 if (tzName == null || tzName.equals(ITMConstants.DEFAULT_TRANSPORT_ZONE)) {
268 boolean defTzEnabled = itmConfig.isDefTzEnabled();
271 LOG.info("TEP ({}) cannot be added into {} when def-tz-enabled flag is false.", localIp,
272 ITMConstants.DEFAULT_TRANSPORT_ZONE);
274 LOG.info("TEP ({}) cannot be removed from {} when def-tz-enabled flag is false.", localIp,
275 ITMConstants.DEFAULT_TRANSPORT_ZONE);
279 tzName = ITMConstants.DEFAULT_TRANSPORT_ZONE;
284 LOG.trace("Update case: Adding TEP-IP: {}, TZ name: {}, Bridge Name: {}, Bridge DPID: {}, of-tunnel: {}",
285 localIp, tzName, bridgeName, strDpnId, newOfTunnel);
287 // Enqueue 'add TEP into new TZ' operation into DataStoreJobCoordinator
288 jobCoordinator.enqueueJob(jobKey,
289 new OvsdbTepAddWorker(localIp, strDpnId, tzName, newOfTunnel, dataBroker));
292 LOG.trace("Update case: Removing TEP-IP: {}, TZ name: {}, Bridge Name: {}, Bridge DPID: {}", localIp,
293 tzName, bridgeName, strDpnId);
295 // Enqueue 'remove TEP from TZ' operation into DataStoreJobCoordinator
296 jobCoordinator.enqueueJob(jobKey, new OvsdbTepRemoveWorker(localIp, strDpnId, tzName, dataBroker));
300 private boolean isLocalIpRemoved(String oldTepIp, String newTepIp) {
301 return oldTepIp != null && newTepIp == null;
304 private boolean isLocalIpAdded(String oldTepIp, String newTepIp) {
305 return oldTepIp == null && newTepIp != null;
308 private boolean isLocalIpUpdated(String oldTepIp, String newTepIp) {
309 return oldTepIp != null && newTepIp != null && !oldTepIp.equals(newTepIp);
312 private boolean isTzUpdated(String oldTzName, String tzName) {
313 return oldTzName == null && tzName != null || oldTzName != null && tzName == null
314 || oldTzName != null && !oldTzName.equals(tzName);
317 private boolean isDpnUpdated(String oldDpnBridgeName, String dpnBridgeName) {
318 return oldDpnBridgeName == null && dpnBridgeName != null || oldDpnBridgeName != null && dpnBridgeName == null
319 || oldDpnBridgeName != null && !oldDpnBridgeName.equals(dpnBridgeName);
322 private boolean isBridgeDpIdAdded(Node ovsdbNodeOld, Node ovsdbNodeNew) {
323 String oldBridgeName = null;
324 String oldDpId = null;
325 String newDpId = null;
327 OvsdbBridgeAugmentation ovsdbNewBridgeAugmentation =
328 ovsdbNodeNew.augmentation(OvsdbBridgeAugmentation.class);
329 if (ovsdbNewBridgeAugmentation != null) {
330 // Read DPID from OVSDBBridgeAugmentation
331 newDpId = ItmUtils.getStrDatapathId(ovsdbNewBridgeAugmentation);
334 OvsdbBridgeAugmentation ovsdbOldBridgeAugmentation =
335 ovsdbNodeOld.augmentation(OvsdbBridgeAugmentation.class);
336 if (ovsdbOldBridgeAugmentation != null) {
337 oldBridgeName = ovsdbNewBridgeAugmentation.getBridgeName().getValue();
338 // Read DPID from OVSDBBridgeAugmentation
339 oldDpId = ItmUtils.getStrDatapathId(ovsdbOldBridgeAugmentation);
341 if (oldDpId == null && newDpId != null) {
342 LOG.trace("DpId changed to {} for bridge {}", newDpId, oldBridgeName);
348 private OvsdbTepInfo getOvsdbTepInfo(OvsdbNodeAugmentation ovsdbNodeAugmentation) {
349 if (ovsdbNodeAugmentation == null) {
353 List<OpenvswitchOtherConfigs> ovsdbNodeOtherConfigsList = ovsdbNodeAugmentation.getOpenvswitchOtherConfigs();
354 if (ovsdbNodeOtherConfigsList == null) {
355 //Local IP is not configured
356 LOG.debug("OtherConfigs list does not exist in the OVSDB Node Augmentation.");
360 OvsdbTepInfo ovsdbTepInfoObj = new OvsdbTepInfo();
362 for (OpenvswitchOtherConfigs otherConfigs : ovsdbNodeOtherConfigsList) {
363 if (ITMConstants.OTH_CFG_TEP_PARAM_KEY_LOCAL_IP.equals(otherConfigs.getOtherConfigKey())) {
364 String tepIp = otherConfigs.getOtherConfigValue();
365 ovsdbTepInfoObj.setLocalIp(tepIp);
369 List<OpenvswitchExternalIds> ovsdbNodeExternalIdsList = ovsdbNodeAugmentation.getOpenvswitchExternalIds();
370 if (ovsdbNodeExternalIdsList == null) {
371 LOG.debug("ExternalIds list does not exist in the OVSDB Node Augmentation.");
373 for (OpenvswitchExternalIds externalId : ovsdbNodeExternalIdsList) {
374 switch (externalId.getExternalIdKey()) {
375 case ITMConstants.EXT_ID_TEP_PARAM_KEY_TZNAME:
376 ovsdbTepInfoObj.setTzName(externalId.getExternalIdValue());
378 case ITMConstants.EXT_ID_TEP_PARAM_KEY_BR_NAME:
379 ovsdbTepInfoObj.setBrName(externalId.getExternalIdValue());
381 case ITMConstants.EXT_ID_TEP_PARAM_KEY_OF_TUNNEL:
382 ovsdbTepInfoObj.setOfTunnel(Boolean.parseBoolean(externalId.getExternalIdValue()));
389 return ovsdbTepInfoObj;
392 private void processBridgeUpdate(Node ovsdbNodeNew, boolean isBridgeAdd) {
393 OvsdbBridgeAugmentation ovsdbNewBridgeAugmentation =
394 ovsdbNodeNew.augmentation(OvsdbBridgeAugmentation.class);
395 if (ovsdbNewBridgeAugmentation != null) {
396 processBridgeUpdate(ovsdbNewBridgeAugmentation, isBridgeAdd);
400 private void processBridgeUpdate(OvsdbBridgeAugmentation ovsdbNewBridgeAugmentation, boolean isBridgeAdd) {
401 String bridgeName = null;
402 String strDpnId = null;
403 OvsdbNodeAugmentation ovsdbNewNodeAugmentation = null;
405 if (ovsdbNewBridgeAugmentation != null) {
406 bridgeName = ovsdbNewBridgeAugmentation.getBridgeName().getValue();
408 // Read DPID from OVSDBBridgeAugmentation
409 strDpnId = ItmUtils.getStrDatapathId(ovsdbNewBridgeAugmentation);
410 if (strDpnId == null || strDpnId.isEmpty()) {
411 LOG.trace("OvsdbBridgeAugmentation processBridgeUpdate: DPID for bridge {} is NULL.", bridgeName);
415 // TBD: Move this time taking operations into DataStoreJobCoordinator
416 Node ovsdbNodeFromBridge = ItmUtils.getOvsdbNode(ovsdbNewBridgeAugmentation, dataBroker);
417 // check for OVSDB node. NOTE: it can be null during bridge removal notification
418 // when switch is disconnected
419 if (ovsdbNodeFromBridge != null) {
420 ovsdbNewNodeAugmentation = ovsdbNodeFromBridge.augmentation(OvsdbNodeAugmentation.class);
422 LOG.warn("processBridgeUpdate: bridge {} removal case when Switch is disconnected."
423 + "Hence, Ovsdb Node could not be fetched from Oper DS.", bridgeName);
428 if (ovsdbNewNodeAugmentation != null) {
429 OvsdbTepInfo ovsdbTepInfo = getOvsdbTepInfo(ovsdbNewNodeAugmentation);
431 if (ovsdbTepInfo == null) {
432 LOG.trace("processBridgeUpdate: No Tep Info");
435 // store TEP info required parameters
436 String newLocalIp = ovsdbTepInfo.getLocalIp();
437 String tzName = ovsdbTepInfo.getTzName();
438 String newBridgeName = ovsdbTepInfo.getBrName();
439 boolean ofTunnel = ovsdbTepInfo.getOfTunnel();
441 // check if Local IP is configured or not
442 if (newLocalIp != null && !newLocalIp.isEmpty()) {
443 // if bridge received is the one configured for TEPs from OVS side or
444 // if it is br-int, then add TEP into Config DS
445 if (newBridgeName.equals(bridgeName)) {
446 LOG.trace("processBridgeUpdate for bridge {} that is configured with Local IP.", bridgeName);
447 String jobKey = newLocalIp;
448 // add or remove tep based on bridge (br-int) is added or removed
449 addOrRemoveTep(tzName, strDpnId, jobKey, newLocalIp, newBridgeName, ofTunnel, isBridgeAdd);
451 LOG.trace("processBridgeUpdate invoked for bridge {}, nothing to do.", bridgeName);
454 LOG.trace("processBridgeUpdate for bridge {} without Local IP set for ovs node. Nothing to do.",