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