Bug 4098 - ofoverlay-renderer failure in jdk8
[groupbasedpolicy.git] / neutron-ovsdb / src / main / java / org / opendaylight / groupbasedpolicy / neutron / ovsdb / TerminationPointDataChangeListener.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
9 package org.opendaylight.groupbasedpolicy.neutron.ovsdb;
10
11 import static com.google.common.base.Preconditions.checkNotNull;
12 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.NodeDataChangeListener.getProviderMapping;
13 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.NodeDataChangeListener.processNodeNotification;
14 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.EndpointHelper.lookupEndpoint;
15 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.EndpointHelper.updateEndpointRemoveLocation;
16 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.EndpointHelper.updateEndpointWithLocation;
17 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.InventoryHelper.checkOfOverlayConfig;
18 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.InventoryHelper.getInventoryNodeConnectorIdString;
19 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.InventoryHelper.getInventoryNodeIdString;
20 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.InventoryHelper.removeTunnelsOfOverlayConfig;
21 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.InventoryHelper.updateOfOverlayConfig;
22 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.NeutronHelper.getEpKeyFromNeutronMapper;
23 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.OvsdbHelper.createTunnelPort;
24 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.OvsdbHelper.getManagerNode;
25 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.OvsdbHelper.getOvsdbBridgeFromTerminationPoint;
26 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.OvsdbHelper.getTopologyNode;
27 import static org.opendaylight.groupbasedpolicy.util.DataStoreHelper.readFromDs;
28
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Map.Entry;
35
36 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
37 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
38 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
39 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
40 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
41 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
42 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
43 import org.opendaylight.ovsdb.southbound.SouthboundConstants;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.Uuid;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.EndpointService;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointKey;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.port._interface.attributes.InterfaceExternalIds;
54 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
55 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
56 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
57 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
58 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
59 import org.opendaylight.yangtools.concepts.ListenerRegistration;
60 import org.opendaylight.yangtools.yang.binding.DataObject;
61 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
64
65 import com.google.common.base.Optional;
66
67 public class TerminationPointDataChangeListener implements DataChangeListener, AutoCloseable {
68
69     private static final String NEUTRON_EXTERNAL_ID_KEY = "iface-id";
70     private final ListenerRegistration<DataChangeListener> registration;
71     private final DataBroker dataBroker;
72     private final EndpointService epService;
73     private static final Logger LOG = LoggerFactory.getLogger(TerminationPointDataChangeListener.class);
74     private final List<AbstractTunnelType> requiredTunnelTypes;
75
76     public TerminationPointDataChangeListener(DataBroker dataBroker, EndpointService epService) {
77         this.dataBroker = checkNotNull(dataBroker);
78         this.epService = checkNotNull(epService);
79         InstanceIdentifier<OvsdbTerminationPointAugmentation> iid = InstanceIdentifier.create(NetworkTopology.class)
80             .child(Topology.class, new TopologyKey(SouthboundConstants.OVSDB_TOPOLOGY_ID))
81             .child(Node.class)
82             .child(TerminationPoint.class)
83             .augmentation(OvsdbTerminationPointAugmentation.class);
84         registration = dataBroker.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, iid, this,
85                 DataChangeScope.ONE);
86         requiredTunnelTypes = createSupportedTunnelsList();
87     }
88
89     private List<AbstractTunnelType> createSupportedTunnelsList() {
90         List<AbstractTunnelType> required = new ArrayList<AbstractTunnelType>();
91         required.add(new VxlanTunnelType());
92         required.add(new VxlanGpeTunnelType());
93         return Collections.unmodifiableList(required);
94     }
95
96     @Override
97     public void close() throws Exception {
98         registration.close();
99     }
100
101     /*
102      * When vSwitch is deleted, we loose data in operational DS to determine Iid of
103      * corresponding NodeId.
104      */
105     private static final Map<InstanceIdentifier<OvsdbTerminationPointAugmentation>, NodeId> nodeIdByTerminPoint = new HashMap<>();
106
107     @Override
108     public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
109
110         /*
111          * TerminationPoint notifications with OVSDB augmentations
112          * vSwitch ports. Iterate through the list of new ports.
113          */
114         for (Entry<InstanceIdentifier<?>, DataObject> entry : change.getCreatedData().entrySet()) {
115             if (entry.getValue() instanceof OvsdbTerminationPointAugmentation) {
116                 OvsdbTerminationPointAugmentation ovsdbTp = (OvsdbTerminationPointAugmentation) entry.getValue();
117                 @SuppressWarnings("unchecked")
118                 InstanceIdentifier<OvsdbTerminationPointAugmentation> ovsdbTpIid = (InstanceIdentifier<OvsdbTerminationPointAugmentation>) entry.getKey();
119                 OvsdbBridgeAugmentation ovsdbBridge = getOvsdbBridgeFromTerminationPoint(ovsdbTpIid, dataBroker);
120                 nodeIdByTerminPoint.put(ovsdbTpIid,
121                         new NodeId(getInventoryNodeIdString(ovsdbBridge, ovsdbTpIid, dataBroker)));
122                 processOvsdbBridge(ovsdbBridge, ovsdbTp, ovsdbTpIid);
123             }
124         }
125
126         /*
127          * Updates
128          */
129         for (Entry<InstanceIdentifier<?>, DataObject> entry : change.getUpdatedData().entrySet()) {
130             if (entry.getValue() instanceof OvsdbTerminationPointAugmentation) {
131                 OvsdbTerminationPointAugmentation ovsdbTp = (OvsdbTerminationPointAugmentation) entry.getValue();
132                 @SuppressWarnings("unchecked")
133                 InstanceIdentifier<OvsdbTerminationPointAugmentation> ovsdbTpIid = (InstanceIdentifier<OvsdbTerminationPointAugmentation>) entry.getKey();
134                 OvsdbBridgeAugmentation ovsdbBridge = getOvsdbBridgeFromTerminationPoint(ovsdbTpIid, dataBroker);
135                 processOvsdbBridge(ovsdbBridge, ovsdbTp, ovsdbTpIid);
136             }
137         }
138
139         /*
140          * Deletions
141          */
142         for (InstanceIdentifier<?> iid : change.getRemovedPaths()) {
143             DataObject old = change.getOriginalData().get(iid);
144             if (old instanceof OvsdbTerminationPointAugmentation) {
145                 OvsdbTerminationPointAugmentation ovsdbTp = (OvsdbTerminationPointAugmentation) old;
146                 @SuppressWarnings("unchecked")
147                 InstanceIdentifier<OvsdbTerminationPointAugmentation> ovsdbTpIid = (InstanceIdentifier<OvsdbTerminationPointAugmentation>) iid;
148                 processRemovedTp(nodeIdByTerminPoint.get(ovsdbTpIid) , ovsdbTp, ovsdbTpIid);
149             }
150         }
151     }
152
153     private void processOvsdbBridge(OvsdbBridgeAugmentation ovsdbBridge, OvsdbTerminationPointAugmentation ovsdbTp,
154             InstanceIdentifier<OvsdbTerminationPointAugmentation> ovsdbTpIid) {
155
156         checkNotNull(ovsdbBridge);
157         if (ovsdbBridge.getBridgeName().getValue().equals(ovsdbTp.getName())) {
158             LOG.debug("Termination Point {} same as Bridge {}. Not processing", ovsdbTp.getName(),
159                     ovsdbBridge.getBridgeName().getValue());
160             return;
161         }
162
163         String nodeIdString = getInventoryNodeIdString(ovsdbBridge, ovsdbTpIid, dataBroker);
164         if (nodeIdString == null) {
165             LOG.debug("nodeIdString for TerminationPoint {} was null", ovsdbTp);
166             return;
167         }
168         String nodeConnectorIdString = getInventoryNodeConnectorIdString(nodeIdString, ovsdbTp, ovsdbTpIid, dataBroker);
169         if (nodeConnectorIdString == null) {
170             LOG.debug("nodeConnectorIdString for TerminationPoint {} was null", ovsdbTp);
171             return;
172         }
173
174         InstanceIdentifier<Node> nodeIid = ovsdbTpIid.firstIdentifierOf(Node.class);
175         String externalId = getNeutronPortUuid(ovsdbTp);
176         Endpoint ep = null;
177         IpAddress hostIp = getIpFromOvsdb(ovsdbBridge);
178
179         /*
180          * Ports created by Nova have an external_id field
181          * in them, which is the Neutron port UUID. If a port
182          * has an external_id, get the EndpointKey for the
183          * Neutron port UUID from neutron-mapper, then look
184          * up the Endpoint in the Endpoint Registry using
185          * that key an update it with the location information
186          * (NodeId and NodeConnectorId from the inventory model)
187          * and the port name, constructed using the port UUID.
188          */
189
190         if (externalId != null) {
191             EndpointKey epKey = getEpKeyFromNeutronMapper(new Uuid(externalId), dataBroker);
192             if (epKey == null) {
193                 LOG.debug("TerminationPoint {} with external ID {} is not in Neutron Map", ovsdbTp, externalId);
194                 return;
195             }
196             ReadOnlyTransaction transaction = dataBroker.newReadOnlyTransaction();
197             ep = lookupEndpoint(epKey, transaction);
198             if (ep == null) {
199                 LOG.warn(
200                         "TerminationPoint {} with external ID {} is in Neutron Map, but corresponding Endpoint {} isn't in Endpoint Repository",
201                         ovsdbTp, externalId, epKey);
202                 return;
203             }
204             /*
205              * Look up the Node in Inventory that corresponds to the
206              * Topology Node that owns this Termination Point (port),
207              * and see if it already is configured with a complete
208              * OfOverlay augmentation. If it hasn't, go see if the
209              * tunnel ports exist, and if not, go and create them.
210              */
211             if (checkOfOverlayConfig(nodeIdString, requiredTunnelTypes, dataBroker) != true) {
212                 checkNotNull(nodeIid);
213                 /*
214                  * Check to see if we need to create a
215                  * tunnel port on the parent node
216                  */
217                 createTunnelPorts(nodeIid, dataBroker);
218             }
219         } else {
220             LOG.debug("TerminationPoint {} had no external ID, not processing for external ID.", ovsdbTp);
221
222         }
223
224         /*
225          * Check if Neutron External port was announed in Node before TerminationPoint it refers to
226          * was actually instantiated. This may or may not have external information in the future,
227          * hence
228          * not process as IF/ELSE externalID.
229          */
230         ReadOnlyTransaction transaction = dataBroker.newReadOnlyTransaction();
231         Optional<Node> node = readFromDs(LogicalDatastoreType.OPERATIONAL, nodeIid, transaction);
232         if (node.isPresent() && node.get().getAugmentation(OvsdbNodeAugmentation.class) != null) {
233             OvsdbNodeAugmentation ovsdbNodeAug = node.get().getAugmentation(OvsdbNodeAugmentation.class);
234             if (getProviderMapping(ovsdbNodeAug) != null) {
235                 processNodeNotification(ovsdbNodeAug);
236
237             }
238         } else {
239         }
240         /*
241          * This may be a notification for a tunnel we just created.
242          * In that case, we need to update the Inventory Node's OfOverlay
243          * augmentation with missing information
244          */
245         AbstractTunnelType tunnel = getTunnelType(ovsdbTp, requiredTunnelTypes);
246         if (tunnel != null) {
247             updateOfOverlayConfig(hostIp, nodeIdString, nodeConnectorIdString, tunnel, dataBroker);
248         }
249         if (externalId != null) {
250             ReadWriteTransaction rwTx = dataBroker.newReadWriteTransaction();
251             updateEndpointWithLocation(ep, nodeIdString, nodeConnectorIdString, rwTx);
252         }
253     }
254
255     /**
256      * If removed termination point was a tunnel port,
257      * removes attached tunnels (namely Vxlan-type) from OVSDB bridge;
258      * else removes location info from TP
259      *
260      * @param nodeId {@link NodeId}
261      * @param ovsdbTp {@link OvsdbTerminationPointAugmentation}
262      * @param ovsdbTpIid termination point's IID {@link InstanceIdentifier}
263      */
264     private void processRemovedTp(NodeId nodeId, OvsdbTerminationPointAugmentation ovsdbTp,
265             InstanceIdentifier<OvsdbTerminationPointAugmentation> ovsdbTpIid) {
266         if (isTunnelPort(ovsdbTp, requiredTunnelTypes)) {
267             removeTunnelsOfOverlayConfig(nodeId.getValue(), requiredTunnelTypes, dataBroker);
268         } else {
269             deleteLocationForTp(ovsdbTp);
270         }
271     }
272
273     /**
274      * Delete location on EP for given TP
275      *
276      * @param ovsdbTp {@link OvsdbTerminationPointAugmentation}
277      */
278     private void deleteLocationForTp(OvsdbTerminationPointAugmentation ovsdbTp) {
279         String externalId = getNeutronPortUuid(ovsdbTp);
280         if (externalId != null) {
281             EndpointKey epKey = getEpKeyFromNeutronMapper(new Uuid(externalId), dataBroker);
282             if (epKey == null) {
283                 LOG.debug("TerminationPoint {} with external ID {} is not in Neutron Map.", ovsdbTp, externalId);
284                 return;
285             }
286             ReadOnlyTransaction readOnlyTransaction = dataBroker.newReadOnlyTransaction();
287             Endpoint ep = lookupEndpoint(epKey, readOnlyTransaction);
288             readOnlyTransaction.close();
289             if (ep == null) {
290                 LOG.warn(
291                         "TerminationPoint {} with external ID {} is in Neutron Map, but corresponding Endpoint {} isn't in Endpoint Repository.",
292                         ovsdbTp, externalId, epKey);
293                 return;
294             }
295             updateEndpointRemoveLocation(ep, dataBroker.newReadWriteTransaction());
296         } else {
297             LOG.debug("TerminationPoint {} has no external ID, not processing.", ovsdbTp);
298         }
299     }
300
301     /**
302      * Check to see if the {@link OvsdbTerminationPointAugmentation} is also a Tunnel port that we
303      * care about.
304      *
305      * @param ovsdbTp {@link OvsdbTerminationPointAugmentation}
306      * @param requiredTunnelTypes {@link List} of tunnel types
307      */
308     private static AbstractTunnelType getTunnelType(OvsdbTerminationPointAugmentation ovsdbTp,
309             List<AbstractTunnelType> requiredTunnelTypes) {
310         if (ovsdbTp.getInterfaceType() != null) {
311             for (AbstractTunnelType tunnelType : requiredTunnelTypes) {
312                 if (tunnelType.isValidTunnelPort(ovsdbTp)) {
313                     return tunnelType;
314                 }
315             }
316         }
317         return null;
318     }
319
320     /*
321      * Check to see if the {@link OvsdbTerminationPointAugmentation}
322      * is also a Tunnel port that we care about.
323      *
324      * @param ovsdbTp {@link OvsdbTerminationPointAugmentation}
325      *
326      * @param requiredTunnelTypes {@link List} of tunnel types
327      *
328      * @return true if it's a required tunnel port, false if it isn't
329      */
330     private boolean isTunnelPort(OvsdbTerminationPointAugmentation ovsdbTp, List<AbstractTunnelType> requiredTunnelTypes) {
331         if (ovsdbTp.getInterfaceType() != null) {
332             for (AbstractTunnelType tunnelType : requiredTunnelTypes) {
333                 if (tunnelType.isValidTunnelPort(ovsdbTp)) {
334                     return true;
335                 }
336             }
337         }
338         return false;
339     }
340
341     /**
342      * Get the Neutron Port UUID from an {@link OvsdbTerminationPointAugmentation}.
343      * The Neutron Port UUID is stored as an "external-id" in the termination point.
344      *
345      * @param ovsdbTp The {@link OvsdbTerminationPointAugmentation}
346      * @return The String representation of the Neutron Port UUID, null if not present
347      */
348     private String getNeutronPortUuid(OvsdbTerminationPointAugmentation ovsdbTp) {
349         if (ovsdbTp.getInterfaceExternalIds() == null) {
350             return null;
351         }
352         for (InterfaceExternalIds id : ovsdbTp.getInterfaceExternalIds()) {
353             if (id.getExternalIdKey() != null && id.getExternalIdKey().equals(NEUTRON_EXTERNAL_ID_KEY)) {
354
355                 if (id.getExternalIdValue() != null) {
356                     return id.getExternalIdValue();
357                 }
358             }
359         }
360         return null;
361     }
362
363     /**
364      * Check to see if all tunnel ports are present, and if not,
365      * create them.
366      *
367      * @param nodeIid {@link InstanceIdentifier}
368      * @param dataBroker {@link DataBroker}
369      */
370     private void createTunnelPorts(InstanceIdentifier<Node> nodeIid, DataBroker dataBroker) {
371
372         Node node = getTopologyNode(nodeIid, dataBroker);
373         checkNotNull(node);
374
375         if (node.getAugmentation(OvsdbBridgeAugmentation.class) == null) {
376             LOG.trace("Node {} is not an OVSDB manageable node", nodeIid);
377             return;
378         }
379
380         /*
381          * See if this Topology Node has the required tunnel ports,
382          * and if not, go and create them
383          */
384         for (AbstractTunnelType tunnelType : requiredTunnelTypes) {
385             boolean tunnelPresent = false;
386             for (TerminationPoint tp : node.getTerminationPoint()) {
387                 OvsdbTerminationPointAugmentation tpAug = tp.getAugmentation(OvsdbTerminationPointAugmentation.class);
388
389                 checkNotNull(tpAug);
390
391                 if (tunnelType.isValidTunnelPort(tpAug)) {
392                     tunnelPresent = true;
393                     break;
394                 }
395             }
396             if (!tunnelPresent) {
397                 createTunnelPort(nodeIid, node, tunnelType, dataBroker);
398             }
399         }
400     }
401
402     /**
403      * Get the IP address of the host that owns the {@link OvsdbBridgeAugmentation}.
404      *
405      * @param ovsdbBridge The OVSDB bridge node
406      * @return The IP address of the host that the bridge is on
407      */
408     private IpAddress getIpFromOvsdb(OvsdbBridgeAugmentation ovsdbBridge) {
409         /*
410          * The manager Node referenced by this node has the
411          * IP address.
412          */
413         OvsdbNodeAugmentation managerNode = getManagerNode(ovsdbBridge, dataBroker);
414
415         if (managerNode == null)
416             return null;
417
418         if (managerNode.getConnectionInfo() != null) {
419             return managerNode.getConnectionInfo().getRemoteIp();
420         }
421         return null;
422     }
423 }