Bug 5428: Ovsdb Termination Point is null issue
[groupbasedpolicy.git] / neutron-ovsdb / src / main / java / org / opendaylight / groupbasedpolicy / neutron / ovsdb / OvsdbNodeListener.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. 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 package org.opendaylight.groupbasedpolicy.neutron.ovsdb;
9
10 import java.util.HashMap;
11 import java.util.HashSet;
12 import java.util.Map;
13 import java.util.Set;
14
15 import javax.annotation.Nonnull;
16
17 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
18 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
19 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
20 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
21 import org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.InventoryHelper;
22 import org.opendaylight.groupbasedpolicy.util.DataTreeChangeHandler;
23 import org.opendaylight.ovsdb.southbound.SouthboundConstants;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeRef;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ManagedNodeEntry;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.OpenvswitchOtherConfigs;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.OpenvswitchOtherConfigsKey;
33 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
34 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
35 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
36 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
37 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
38 import org.opendaylight.yangtools.yang.binding.DataObject;
39 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 import com.google.common.base.Strings;
44
45 public class OvsdbNodeListener extends DataTreeChangeHandler<Node> {
46
47     private static final Logger LOG = LoggerFactory.getLogger(OvsdbNodeListener.class);
48     public static final String NEUTRON_PROVIDER_MAPPINGS_KEY = "provider_mappings";
49     private static final String OF_SEPARATOR = ":";
50     private static final String OF_INVENTORY_PREFIX = "openflow";
51
52     private final Map<OvsdbBridgeRef, String> providerPortNameByBridgeRef = new HashMap<>();
53     private final Map<InstanceIdentifier<Node>, NeutronBridgeWithExtPort> bridgeByNodeIid = new HashMap<>();
54
55     public OvsdbNodeListener(DataBroker dataProvider) {
56         super(dataProvider,
57                 new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL,
58                         InstanceIdentifier.create(NetworkTopology.class)
59                             .child(Topology.class, new TopologyKey(SouthboundConstants.OVSDB_TOPOLOGY_ID))
60                             .child(Node.class)));
61     }
62
63     @Override
64     protected void onWrite(DataObjectModification<Node> rootNode, InstanceIdentifier<Node> rootIdentifier) {
65         Node node = rootNode.getDataAfter();
66         OvsdbNodeAugmentation ovsdbNode = node.getAugmentation(OvsdbNodeAugmentation.class);
67         if (ovsdbNode != null) {
68             LOG.trace("OVSDB node created: {} \n {}", rootIdentifier, node);
69             DataObjectModification<OpenvswitchOtherConfigs> ovsOtherConfigModification =
70                     getProviderMappingsModification(rootNode);
71             if (isProviderPortNameChanged(ovsOtherConfigModification) && ovsdbNode.getManagedNodeEntry() != null) {
72                 String newProviderPortName = getProviderPortName(ovsOtherConfigModification.getDataAfter());
73                 LOG.debug("provider_mappings created {} on node {}", newProviderPortName, node.getNodeId().getValue());
74                 for (ManagedNodeEntry mngdNodeEntry : ovsdbNode.getManagedNodeEntry()) {
75                     providerPortNameByBridgeRef.put(mngdNodeEntry.getBridgeRef(), newProviderPortName);
76                     LOG.trace("Added Provider port name {} by OVSDB bridge ref {}", newProviderPortName,
77                             mngdNodeEntry.getBridgeRef());
78                 }
79             }
80         }
81
82         OvsdbBridgeAugmentation ovsdbBridge = node.getAugmentation(OvsdbBridgeAugmentation.class);
83         if (ovsdbBridge != null) {
84             LOG.trace("OVSDB bridge created: {} \n {}", rootIdentifier, node);
85             Set<DataObjectModification<OvsdbTerminationPointAugmentation>> ovsdbTpModifications =
86                     getOvsdbTpModifications(rootNode);
87             NodeId ofNodeId = buildOfNodeId(ovsdbBridge);
88             if (!ovsdbTpModifications.isEmpty() && ofNodeId != null) {
89                 NeutronBridgeWithExtPort bridge = getBridge(rootIdentifier);
90                 bridge.ofNodeId = ofNodeId;
91                 LOG.trace("OF node {} representing OVSDB bridge {}", ofNodeId.getValue(), node.getNodeId().getValue());
92             }
93             for (DataObjectModification<OvsdbTerminationPointAugmentation> ovsdbTpModification : ovsdbTpModifications) {
94                 OvsdbTerminationPointAugmentation newOvsdbTp = ovsdbTpModification.getDataAfter();
95                 if (ovsdbBridge.getBridgeName().getValue().equals(newOvsdbTp.getName())) {
96                     LOG.trace("Termination Point {} same as Bridge {}. Not processing", newOvsdbTp.getName(),
97                             ovsdbBridge.getBridgeName().getValue());
98                     continue;
99                 }
100                 String portName = newOvsdbTp.getName();
101                 Long ofport = newOvsdbTp.getOfport();
102                 if (isOfportOrNameChanged(ovsdbTpModification) && portName != null && ofport != null) {
103                     NeutronBridgeWithExtPort bridge = getBridge(rootIdentifier);
104                     bridge.ofportByName.put(ofport, portName);
105                     LOG.trace("OVSDB termination point with ofport {} and port-name {} created.", ofport, portName);
106                     // port name is same as provider port name so the termination point represents
107                     // external port
108                     if (portName.equals(providerPortNameByBridgeRef.get(new OvsdbBridgeRef(rootIdentifier)))) {
109                         NodeConnectorId ofNcId = buildOfNodeConnectorId(newOvsdbTp, ofNodeId);
110                         bridge.externalIfaces.add(ofNcId);
111                         InventoryHelper.addOfOverlayExternalPort(bridge.ofNodeId, ofNcId, dataProvider);
112                         LOG.debug("Added of-overlay external-interface {} to node {}", ofNcId.getValue(),
113                                 bridge.ofNodeId);
114                         traceBridge(rootIdentifier);
115                     }
116                 }
117             }
118         }
119     }
120
121     @Override
122     protected void onDelete(DataObjectModification<Node> rootNode, InstanceIdentifier<Node> rootIdentifier) {
123         LOG.trace("Not implemented - OVSDB element deleted: {} \n {}", rootIdentifier, rootNode.getDataBefore());
124     }
125
126     @Override
127     protected void onSubtreeModified(DataObjectModification<Node> rootNode, InstanceIdentifier<Node> rootIdentifier) {
128         Node node = rootNode.getDataAfter();
129         OvsdbBridgeAugmentation ovsdbBridge = node.getAugmentation(OvsdbBridgeAugmentation.class);
130         if (ovsdbBridge != null) {
131             LOG.trace("OVSDB bridge updated: {} \n before {} \n after {}", rootIdentifier, rootNode.getDataBefore(),
132                     rootNode.getDataAfter());
133             Set<DataObjectModification<OvsdbTerminationPointAugmentation>> ovsdbTpModifications =
134                     getOvsdbTpModifications(rootNode);
135             NodeId ofNodeId = buildOfNodeId(ovsdbBridge);
136             if (!ovsdbTpModifications.isEmpty() && ofNodeId != null) {
137                 NeutronBridgeWithExtPort bridge = getBridge(rootIdentifier);
138                 if (bridge.ofNodeId != null && !bridge.ofNodeId.equals(ofNodeId)) {
139                     LOG.debug("OVSDB bridge {} has changed datapath-id. \n  Old: {} \n  New: {}",
140                             node.getNodeId().getValue(), bridge.ofNodeId.getValue(), ofNodeId.getValue());
141                     bridge.ofNodeId = ofNodeId;
142                 }
143             }
144             for (DataObjectModification<OvsdbTerminationPointAugmentation> ovsdbTpModification : ovsdbTpModifications) {
145                 OvsdbTerminationPointAugmentation newOvsdbTp = ovsdbTpModification.getDataAfter();
146
147                 if (newOvsdbTp == null) {
148                     LOG.trace("Termination Point is null. Not processing");
149                     continue;
150                 }
151
152                 if (ovsdbBridge.getBridgeName().getValue().equals(newOvsdbTp.getName())) {
153                     LOG.trace("Termination Point {} same as Bridge {}. Not processing", newOvsdbTp.getName(),
154                             ovsdbBridge.getBridgeName().getValue());
155                     continue;
156                 }
157                 String portName = newOvsdbTp.getName();
158                 Long ofport = newOvsdbTp.getOfport();
159                 if (isOfportOrNameChanged(ovsdbTpModification) && portName != null && ofport != null) {
160                     NeutronBridgeWithExtPort bridge = getBridge(rootIdentifier);
161                     bridge.ofportByName.put(ofport, portName);
162                     LOG.trace("OVSDB termination point with ofport {} and port-name {} created.", ofport, portName);
163                     // port name is same as provider port name so the termination point represents
164                     // external port
165                     if (portName.equals(providerPortNameByBridgeRef.get(new OvsdbBridgeRef(rootIdentifier)))) {
166                         NodeConnectorId ofNcId = buildOfNodeConnectorId(newOvsdbTp, ofNodeId);
167                         bridge.externalIfaces.add(ofNcId);
168                         InventoryHelper.addOfOverlayExternalPort(bridge.ofNodeId, ofNcId, dataProvider);
169                         LOG.debug("Added of-overlay external-interface {} to node {}", ofNcId.getValue(),
170                                 bridge.ofNodeId);
171                         traceBridge(rootIdentifier);
172                     }
173                 }
174             }
175         }
176     }
177
178     private NeutronBridgeWithExtPort getBridge(InstanceIdentifier<Node> nodeIid) {
179         NeutronBridgeWithExtPort bridge = bridgeByNodeIid.get(nodeIid);
180         if (bridge == null) {
181             bridge = new NeutronBridgeWithExtPort();
182             bridgeByNodeIid.put(nodeIid, bridge);
183         }
184         return bridge;
185     }
186
187     @SuppressWarnings("unchecked")
188     private static Set<DataObjectModification<OvsdbTerminationPointAugmentation>> getOvsdbTpModifications(
189             DataObjectModification<Node> rootNode) {
190         Set<DataObjectModification<OvsdbTerminationPointAugmentation>> modifications = new HashSet<>();
191         for (DataObjectModification<? extends DataObject> modifiedChild : rootNode.getModifiedChildren()) {
192             if (TerminationPoint.class.isAssignableFrom(modifiedChild.getDataType())) {
193                 DataObjectModification<OvsdbTerminationPointAugmentation> modifiedAugmentation =
194                         ((DataObjectModification<TerminationPoint>) modifiedChild)
195                             .getModifiedAugmentation(OvsdbTerminationPointAugmentation.class);
196                 if (modifiedAugmentation != null) {
197                     modifications.add(modifiedAugmentation);
198                 }
199             }
200         }
201         return modifications;
202     }
203
204     private static boolean isOfportOrNameChanged(
205             DataObjectModification<OvsdbTerminationPointAugmentation> ovsdbTpModification) {
206         if (ovsdbTpModification == null) {
207             return false;
208         }
209         OvsdbTerminationPointAugmentation oldTp = ovsdbTpModification.getDataBefore();
210         OvsdbTerminationPointAugmentation newTp = ovsdbTpModification.getDataAfter();
211         if (oldTp != null && newTp != null) {
212             if (oldTp.getOfport() != null && newTp.getOfport() != null && oldTp.getOfport() != newTp.getOfport()) {
213                 return true;
214             }
215             if (!(Strings.nullToEmpty(oldTp.getName())).equals(Strings.nullToEmpty(newTp.getName()))) {
216                 return true;
217             }
218         }
219         if (isOfportOrNameNotNull(oldTp)) {
220             return true;
221         }
222         if (isOfportOrNameNotNull(newTp)) {
223             return true;
224         }
225         return false;
226     }
227
228     private static boolean isOfportOrNameNotNull(OvsdbTerminationPointAugmentation tp) {
229         if (tp != null) {
230             if (tp.getOfport() != null) {
231                 return true;
232             }
233             if (tp.getName() != null) {
234                 return true;
235             }
236         }
237         return false;
238     }
239
240     private static DataObjectModification<OpenvswitchOtherConfigs> getProviderMappingsModification(
241             DataObjectModification<Node> rootNode) {
242         DataObjectModification<OvsdbNodeAugmentation> modifiedOvsdbNode =
243                 rootNode.getModifiedAugmentation(OvsdbNodeAugmentation.class);
244         if (modifiedOvsdbNode == null) {
245             return null;
246         }
247         return modifiedOvsdbNode.getModifiedChildListItem(OpenvswitchOtherConfigs.class,
248                 new OpenvswitchOtherConfigsKey(NEUTRON_PROVIDER_MAPPINGS_KEY));
249     }
250
251     private static boolean isProviderPortNameChanged(DataObjectModification<OpenvswitchOtherConfigs> ovsConfig) {
252         if (ovsConfig == null) {
253             return false;
254         }
255         OpenvswitchOtherConfigs oldConfig = ovsConfig.getDataBefore();
256         OpenvswitchOtherConfigs newConfig = ovsConfig.getDataAfter();
257         if (oldConfig != null && newConfig != null) {
258             if (!(Strings.nullToEmpty(oldConfig.getOtherConfigValue())
259                 .equals(Strings.nullToEmpty(newConfig.getOtherConfigValue())))) {
260                 return true;
261             }
262         } else if (oldConfig != null && !Strings.isNullOrEmpty(oldConfig.getOtherConfigValue())) {
263             return true;
264         } else if (newConfig != null && !Strings.isNullOrEmpty(newConfig.getOtherConfigValue())) {
265             return true;
266         }
267         return false;
268     }
269
270     private static @Nonnull String getProviderPortName(OpenvswitchOtherConfigs config) {
271         if (NEUTRON_PROVIDER_MAPPINGS_KEY.equals(config.getOtherConfigKey()) && config.getOtherConfigValue() != null) {
272             String otherConfig = config.getOtherConfigValue();
273             String[] elements = otherConfig.split(":");
274             if (elements.length == 2) {
275                 return elements[1];
276             }
277         }
278         return "";
279     }
280
281     private static NodeId buildOfNodeId(OvsdbBridgeAugmentation ovsdbBridge) {
282         if (ovsdbBridge.getDatapathId() == null) {
283             return null;
284         }
285         Long macLong = InventoryHelper.getLongFromDpid(ovsdbBridge.getDatapathId().getValue());
286         return new NodeId(OF_INVENTORY_PREFIX + OF_SEPARATOR + String.valueOf(macLong));
287     }
288
289     private static NodeConnectorId buildOfNodeConnectorId(OvsdbTerminationPointAugmentation terminationPoint,
290             NodeId nodeId) {
291         if (terminationPoint.getOfport() == null) {
292             return null;
293         }
294         return new NodeConnectorId(nodeId.getValue() + OF_SEPARATOR + String.valueOf(terminationPoint.getOfport()));
295     }
296
297     private void traceBridge(InstanceIdentifier<Node> identifier) {
298         if (LOG.isTraceEnabled()) {
299             NeutronBridgeWithExtPort bridge = bridgeByNodeIid.get(identifier);
300             if (bridge == null) {
301                 LOG.trace("Bridge does not exist: {}", identifier);
302                 return;
303             }
304             String providerPortName = providerPortNameByBridgeRef.get(new OvsdbBridgeRef(identifier));
305             LOG.trace("State of bridge:\n  ID: {} \n  providerPortName: {} \n  {}", identifier, providerPortName,
306                     bridge);
307         }
308     }
309
310     private class NeutronBridgeWithExtPort {
311
312         NodeId ofNodeId;
313         Set<NodeConnectorId> externalIfaces = new HashSet<>();
314         Map<Long, String> ofportByName = new HashMap<>();
315
316         @Override
317         public String toString() {
318             return "NeutronBridgeWithExtPort:\n  ofNodeId=" + ofNodeId + "\n  externalIfaces=" + externalIfaces
319                     + ",\n  ofportByName=" + ofportByName;
320         }
321     }
322
323 }