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