Merge "Adding EIC."
[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
13 import java.util.ArrayList;
14 import java.util.Collections;
15 import java.util.List;
16 import java.util.Map.Entry;
17
18 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
19 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
20 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
21 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
22 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
23 import org.opendaylight.ovsdb.southbound.SouthboundConstants;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.Uuid;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.EndpointService;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointKey;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.port._interface.attributes.InterfaceExternalIds;
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.concepts.ListenerRegistration;
39 import org.opendaylight.yangtools.yang.binding.DataObject;
40 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.OvsdbHelper.getManagerNode;
45 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.OvsdbHelper.getTopologyNode;
46 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.OvsdbHelper.createTunnelPort;
47 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.OvsdbHelper.getOvsdbBridgeFromTerminationPoint;
48 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.EndpointHelper.lookupEndpoint;
49 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.EndpointHelper.updateEndpointWithLocation;
50 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.NeutronHelper.getEpKeyFromNeutronMapper;
51 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.InventoryHelper.checkOfOverlayConfig;
52 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.InventoryHelper.getInventoryNodeIdString;
53 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.InventoryHelper.getInventoryNodeConnectorIdString;
54 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.InventoryHelper.updateOfOverlayConfig;
55
56 public class TerminationPointDataChangeListener implements DataChangeListener, AutoCloseable {
57
58     private static final String NEUTRON_EXTERNAL_ID_KEY = "iface-id";
59     private final ListenerRegistration<DataChangeListener> registration;
60     private final DataBroker dataBroker;
61     private final EndpointService epService;
62     private static final Logger LOG = LoggerFactory.getLogger(TerminationPointDataChangeListener.class);
63     private final List<AbstractTunnelType> requiredTunnelTypes;
64
65     public TerminationPointDataChangeListener(DataBroker dataBroker, EndpointService epService) {
66         this.dataBroker = checkNotNull(dataBroker);
67         this.epService = checkNotNull(epService);
68         InstanceIdentifier<OvsdbTerminationPointAugmentation> iid = InstanceIdentifier
69                 .create(NetworkTopology.class)
70                 .child(Topology.class, new TopologyKey(SouthboundConstants.OVSDB_TOPOLOGY_ID))
71                 .child(Node.class)
72                 .child(TerminationPoint.class)
73                 .augmentation(OvsdbTerminationPointAugmentation.class);
74         registration = dataBroker.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, iid, this, DataChangeScope.ONE);
75         requiredTunnelTypes = createSupportedTunnelsList();
76     }
77
78     private List<AbstractTunnelType> createSupportedTunnelsList() {
79         List<AbstractTunnelType> required = new ArrayList<AbstractTunnelType>();
80 //        required.add(new VxlanGpeTunnelType());
81         required.add(new VxlanTunnelType());
82         return Collections.unmodifiableList(required);
83     }
84
85     @Override
86     public void close() throws Exception {
87         registration.close();
88     }
89
90     @Override
91     public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
92
93         /*
94          * TerminationPoint notifications with OVSDB augmentations
95          * vSwitch ports. Iterate through the list of new ports.
96          */
97         for (Entry<InstanceIdentifier<?>, DataObject> entry: change.getCreatedData().entrySet()) {
98             if(entry.getValue() instanceof OvsdbTerminationPointAugmentation) {
99                 OvsdbTerminationPointAugmentation ovsdbTp = (OvsdbTerminationPointAugmentation)entry.getValue();
100                 InstanceIdentifier<OvsdbTerminationPointAugmentation> ovsdbTpIid =
101                         (InstanceIdentifier<OvsdbTerminationPointAugmentation>) entry.getKey();
102                 OvsdbBridgeAugmentation ovsdbBridge = getOvsdbBridgeFromTerminationPoint(ovsdbTpIid, dataBroker);
103                 processOvsdbBridge(ovsdbBridge, ovsdbTp,ovsdbTpIid);
104             }
105         }
106
107         /*
108          * Updates
109          */
110         for (Entry<InstanceIdentifier<?>, DataObject> entry: change.getUpdatedData().entrySet()) {
111             if(entry.getValue() instanceof OvsdbTerminationPointAugmentation) {
112                 OvsdbTerminationPointAugmentation ovsdbTp = (OvsdbTerminationPointAugmentation)entry.getValue();
113                 InstanceIdentifier<OvsdbTerminationPointAugmentation> ovsdbTpIid =
114                         (InstanceIdentifier<OvsdbTerminationPointAugmentation>) entry.getKey();
115                 OvsdbBridgeAugmentation ovsdbBridge = getOvsdbBridgeFromTerminationPoint(ovsdbTpIid, dataBroker);
116                 processOvsdbBridge(ovsdbBridge, ovsdbTp,ovsdbTpIid);
117             }
118         }
119
120         /*
121          * Deletions
122          */
123         for (InstanceIdentifier<?> iid: change.getRemovedPaths()) {
124             if (iid instanceof OvsdbTerminationPointAugmentation) {
125                 /*
126                  * Remove the state from OfOverlay?
127                  */
128             }
129         }
130     }
131
132     private void processOvsdbBridge(OvsdbBridgeAugmentation ovsdbBridge, OvsdbTerminationPointAugmentation ovsdbTp, 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(),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         /*
152          * Ports created by Nova have an external_id field
153          * in them, which is the Neutron port UUID. If a port
154          * has an external_id, get the EndpointKey for the
155          * Neutron port UUID from neutron-mapper, then look
156          * up the Endpoint in the Endpoint Registry using
157          * that key an update it with the location information
158          * (NodeId and NodeConnectorId from the inventory model)
159          * and the port name, constructed using the port UUID.
160          */
161         String externalId = getNeutronPortUuid(ovsdbTp);
162         Endpoint ep = null;
163         if (externalId != null) {
164             EndpointKey epKey = getEpKeyFromNeutronMapper(new Uuid(externalId), dataBroker);
165             if (epKey == null) {
166                 LOG.debug("TerminationPoint {} with external ID {} is not in Neutron Map", ovsdbTp,externalId);
167                 return;
168             }
169             ep = lookupEndpoint(epKey, dataBroker);
170             if (ep == null) {
171                 LOG.warn("TerminationPoint {} with external ID {} is in Neutron Map, but corresponding Endpoint {} isn't in Endpoint Repository", ovsdbTp,externalId,epKey);
172                 return;
173             }
174             /*
175              * Look up the Node in Inventory that corresponds to the
176              * Topology Node that owns this Termination Point (port),
177              * and see if it already is configured with a complete
178              * OfOverlay augmentation. If it hasn't, go see if the
179              * tunnel ports exist, and if not, go and create them.
180              */
181             if (checkOfOverlayConfig(nodeIdString, requiredTunnelTypes, dataBroker) != true) {
182
183                 InstanceIdentifier<Node> nodeIid = ovsdbTpIid.firstIdentifierOf(Node.class);
184                 checkNotNull(nodeIid);
185
186                 /*
187                  * Check to see if we need to create a
188                  * tunnel port on the parent node
189                  */
190                 createTunnelPorts(nodeIid, dataBroker);
191             }
192         } else {
193             LOG.debug("TerminationPoint {} has no external ID, not processing.",ovsdbTp);
194         }
195         IpAddress hostIp = getIpFromOvsdb(ovsdbBridge);
196         /*
197          * This may be a notification for a tunnel we just created.
198          * In that case, we need to update the Inventory Node's OfOverlay
199          * augmentation with missing information
200          */
201         if (isTunnelPort(ovsdbTp, requiredTunnelTypes)) {
202             updateOfOverlayConfig(hostIp, nodeIdString, nodeConnectorIdString, requiredTunnelTypes, dataBroker);
203         }
204         if (externalId != null) {
205             updateEndpointWithLocation(ep, nodeIdString, nodeConnectorIdString, ovsdbTp.getName(), epService);
206         }
207     }
208
209     /**
210      * Check to see if the {@link OvsdbTerminationPointAugmentation}
211      * is also a Tunnel port that we care about.
212      *
213      * @param ovsdbTp
214      * @param requiredTunnelTypes
215      * @return true if it's a required tunnel port, false if it isn't
216      */
217     private boolean isTunnelPort(OvsdbTerminationPointAugmentation ovsdbTp,
218                                  List<AbstractTunnelType> requiredTunnelTypes) {
219         if (ovsdbTp.getInterfaceType() != null) {
220             for (AbstractTunnelType tunnelType: requiredTunnelTypes) {
221                 if (tunnelType.isValidTunnelPort(ovsdbTp)) {
222                     return true;
223                 }
224             }
225         }
226         return false;
227     }
228
229
230     /**
231      * Get the Neutron Port UUID from an {@link OvsdbTerminationPointAugmentation}.
232      * The Neutron Port UUID is stored as an "external-id" in the termination point.
233      *
234      * @param ovsdbTp The OVSDB Termination Point augmentation
235      * @return The String representation of the Neutron Port UUID, null if not present
236      */
237     private String getNeutronPortUuid(OvsdbTerminationPointAugmentation ovsdbTp) {
238         if (ovsdbTp.getInterfaceExternalIds() == null) {
239             return null;
240         }
241         for (InterfaceExternalIds id: ovsdbTp.getInterfaceExternalIds()) {
242             if (id.getExternalIdKey() != null
243                     && id.getExternalIdKey().equals(NEUTRON_EXTERNAL_ID_KEY)) {
244
245                 if (id.getExternalIdValue() != null) {
246                     return id.getExternalIdValue();
247                 }
248             }
249         }
250         return null;
251     }
252
253     /**
254      * Check to see if all tunnel ports are present, and if not,
255      * create them.
256      *
257      * @param tpIid
258      * @return
259      */
260     private void createTunnelPorts(InstanceIdentifier<Node> nodeIid, DataBroker dataBroker) {
261
262         Node node = getTopologyNode(nodeIid, dataBroker);
263         checkNotNull(node);
264
265         if (node.getAugmentation(OvsdbBridgeAugmentation.class) == null) {
266             LOG.trace("Node {} is not an OVSDB manageable node", nodeIid);
267             return;
268         }
269
270         /*
271          * See if this Topology Node has the required tunnel ports,
272          * and if not, go and create them
273          */
274         for (AbstractTunnelType tunnelType: requiredTunnelTypes) {
275             boolean tunnelPresent = false;
276             for (TerminationPoint tp: node.getTerminationPoint()) {
277                 OvsdbTerminationPointAugmentation tpAug =
278                     tp.getAugmentation(OvsdbTerminationPointAugmentation.class);
279
280                 checkNotNull(tpAug);
281
282                 if (tunnelType.isValidTunnelPort(tpAug)) {
283                     tunnelPresent = true;
284                 }
285             }
286             if (tunnelPresent == false) {
287                 createTunnelPort(nodeIid, node, tunnelType, dataBroker);
288             }
289         }
290     }
291
292     /**
293      * Get the IP address of the host that owns the {@link OvsdbBridgeAugmentation}.
294      *
295      * @param ovsdbBridge The OVSDB bridge node
296      * @return The IP address of the host that the bridge is on
297      */
298     private IpAddress getIpFromOvsdb(OvsdbBridgeAugmentation ovsdbBridge) {
299         /*
300          * The manager Node referenced by this node has the
301          * IP address.
302          */
303         OvsdbNodeAugmentation managerNode = getManagerNode(ovsdbBridge, dataBroker);
304
305         if (managerNode == null) return null;
306
307         if (managerNode.getConnectionInfo() != null) {
308             return managerNode.getConnectionInfo().getRemoteIp();
309         }
310         return null;
311     }
312 }
313