Merge "Bug 5117 - DOS chars in shell script"
[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                 if (ovsdbBridge.getBridgeName().getValue().equals(newOvsdbTp.getName())) {
147                     LOG.trace("Termination Point {} same as Bridge {}. Not processing", newOvsdbTp.getName(),
148                             ovsdbBridge.getBridgeName().getValue());
149                     continue;
150                 }
151                 String portName = newOvsdbTp.getName();
152                 Long ofport = newOvsdbTp.getOfport();
153                 if (isOfportOrNameChanged(ovsdbTpModification) && portName != null && ofport != null) {
154                     NeutronBridgeWithExtPort bridge = getBridge(rootIdentifier);
155                     bridge.ofportByName.put(ofport, portName);
156                     LOG.trace("OVSDB termination point with ofport {} and port-name {} created.", ofport, portName);
157                     // port name is same as provider port name so the termination point represents
158                     // external port
159                     if (portName.equals(providerPortNameByBridgeRef.get(new OvsdbBridgeRef(rootIdentifier)))) {
160                         NodeConnectorId ofNcId = buildOfNodeConnectorId(newOvsdbTp, ofNodeId);
161                         bridge.externalIfaces.add(ofNcId);
162                         InventoryHelper.addOfOverlayExternalPort(bridge.ofNodeId, ofNcId, dataProvider);
163                         LOG.debug("Added of-overlay external-interface {} to node {}", ofNcId.getValue(),
164                                 bridge.ofNodeId);
165                         traceBridge(rootIdentifier);
166                     }
167                 }
168             }
169         }
170     }
171
172     private NeutronBridgeWithExtPort getBridge(InstanceIdentifier<Node> nodeIid) {
173         NeutronBridgeWithExtPort bridge = bridgeByNodeIid.get(nodeIid);
174         if (bridge == null) {
175             bridge = new NeutronBridgeWithExtPort();
176             bridgeByNodeIid.put(nodeIid, bridge);
177         }
178         return bridge;
179     }
180
181     @SuppressWarnings("unchecked")
182     private static Set<DataObjectModification<OvsdbTerminationPointAugmentation>> getOvsdbTpModifications(
183             DataObjectModification<Node> rootNode) {
184         Set<DataObjectModification<OvsdbTerminationPointAugmentation>> modifications = new HashSet<>();
185         for (DataObjectModification<? extends DataObject> modifiedChild : rootNode.getModifiedChildren()) {
186             if (TerminationPoint.class.isAssignableFrom(modifiedChild.getDataType())) {
187                 DataObjectModification<OvsdbTerminationPointAugmentation> modifiedAugmentation =
188                         ((DataObjectModification<TerminationPoint>) modifiedChild)
189                             .getModifiedAugmentation(OvsdbTerminationPointAugmentation.class);
190                 if (modifiedAugmentation != null) {
191                     modifications.add(modifiedAugmentation);
192                 }
193             }
194         }
195         return modifications;
196     }
197
198     private static boolean isOfportOrNameChanged(
199             DataObjectModification<OvsdbTerminationPointAugmentation> ovsdbTpModification) {
200         if (ovsdbTpModification == null) {
201             return false;
202         }
203         OvsdbTerminationPointAugmentation oldTp = ovsdbTpModification.getDataBefore();
204         OvsdbTerminationPointAugmentation newTp = ovsdbTpModification.getDataAfter();
205         if (oldTp != null && newTp != null) {
206             if (oldTp.getOfport() != null && newTp.getOfport() != null && oldTp.getOfport() != newTp.getOfport()) {
207                 return true;
208             }
209             if (!(Strings.nullToEmpty(oldTp.getName())).equals(Strings.nullToEmpty(newTp.getName()))) {
210                 return true;
211             }
212         }
213         if (isOfportOrNameNotNull(oldTp)) {
214             return true;
215         }
216         if (isOfportOrNameNotNull(newTp)) {
217             return true;
218         }
219         return false;
220     }
221
222     private static boolean isOfportOrNameNotNull(OvsdbTerminationPointAugmentation tp) {
223         if (tp != null) {
224             if (tp.getOfport() != null) {
225                 return true;
226             }
227             if (tp.getName() != null) {
228                 return true;
229             }
230         }
231         return false;
232     }
233
234     private static DataObjectModification<OpenvswitchOtherConfigs> getProviderMappingsModification(
235             DataObjectModification<Node> rootNode) {
236         DataObjectModification<OvsdbNodeAugmentation> modifiedOvsdbNode =
237                 rootNode.getModifiedAugmentation(OvsdbNodeAugmentation.class);
238         if (modifiedOvsdbNode == null) {
239             return null;
240         }
241         return modifiedOvsdbNode.getModifiedChildListItem(OpenvswitchOtherConfigs.class,
242                 new OpenvswitchOtherConfigsKey(NEUTRON_PROVIDER_MAPPINGS_KEY));
243     }
244
245     private static boolean isProviderPortNameChanged(DataObjectModification<OpenvswitchOtherConfigs> ovsConfig) {
246         if (ovsConfig == null) {
247             return false;
248         }
249         OpenvswitchOtherConfigs oldConfig = ovsConfig.getDataBefore();
250         OpenvswitchOtherConfigs newConfig = ovsConfig.getDataAfter();
251         if (oldConfig != null && newConfig != null) {
252             if (!(Strings.nullToEmpty(oldConfig.getOtherConfigValue())
253                 .equals(Strings.nullToEmpty(newConfig.getOtherConfigValue())))) {
254                 return true;
255             }
256         } else if (oldConfig != null && !Strings.isNullOrEmpty(oldConfig.getOtherConfigValue())) {
257             return true;
258         } else if (newConfig != null && !Strings.isNullOrEmpty(newConfig.getOtherConfigValue())) {
259             return true;
260         }
261         return false;
262     }
263
264     private static @Nonnull String getProviderPortName(OpenvswitchOtherConfigs config) {
265         if (NEUTRON_PROVIDER_MAPPINGS_KEY.equals(config.getOtherConfigKey()) && config.getOtherConfigValue() != null) {
266             String otherConfig = config.getOtherConfigValue();
267             String[] elements = otherConfig.split(":");
268             if (elements.length == 2) {
269                 return elements[1];
270             }
271         }
272         return "";
273     }
274
275     private static NodeId buildOfNodeId(OvsdbBridgeAugmentation ovsdbBridge) {
276         if (ovsdbBridge.getDatapathId() == null) {
277             return null;
278         }
279         Long macLong = InventoryHelper.getLongFromDpid(ovsdbBridge.getDatapathId().getValue());
280         return new NodeId(OF_INVENTORY_PREFIX + OF_SEPARATOR + String.valueOf(macLong));
281     }
282
283     private static NodeConnectorId buildOfNodeConnectorId(OvsdbTerminationPointAugmentation terminationPoint,
284             NodeId nodeId) {
285         if (terminationPoint.getOfport() == null) {
286             return null;
287         }
288         return new NodeConnectorId(nodeId.getValue() + OF_SEPARATOR + String.valueOf(terminationPoint.getOfport()));
289     }
290
291     private void traceBridge(InstanceIdentifier<Node> identifier) {
292         if (LOG.isTraceEnabled()) {
293             NeutronBridgeWithExtPort bridge = bridgeByNodeIid.get(identifier);
294             if (bridge == null) {
295                 LOG.trace("Bridge does not exist: {}", identifier);
296                 return;
297             }
298             String providerPortName = providerPortNameByBridgeRef.get(new OvsdbBridgeRef(identifier));
299             LOG.trace("State of bridge:\n  ID: {} \n  providerPortName: {} \n  {}", identifier, providerPortName,
300                     bridge);
301         }
302     }
303
304     private class NeutronBridgeWithExtPort {
305
306         NodeId ofNodeId;
307         Set<NodeConnectorId> externalIfaces = new HashSet<>();
308         Map<Long, String> ofportByName = new HashMap<>();
309
310         @Override
311         public String toString() {
312             return "NeutronBridgeWithExtPort:\n  ofNodeId=" + ofNodeId + "\n  externalIfaces=" + externalIfaces
313                     + ",\n  ofportByName=" + ofportByName;
314         }
315     }
316
317 }