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