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