2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
9 package org.opendaylight.groupbasedpolicy.neutron.ovsdb;
11 import static com.google.common.base.Preconditions.checkNotNull;
12 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.NodeDataChangeListener.getProviderMapping;
13 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.NodeDataChangeListener.processNodeNotification;
14 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.EndpointHelper.lookupEndpoint;
15 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.EndpointHelper.updateEndpointRemoveLocation;
16 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.EndpointHelper.updateEndpointWithLocation;
17 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.InventoryHelper.checkOfOverlayConfig;
18 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.InventoryHelper.getInventoryNodeConnectorIdString;
19 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.InventoryHelper.getInventoryNodeIdString;
20 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.InventoryHelper.removeTunnelsOfOverlayConfig;
21 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.InventoryHelper.updateOfOverlayConfig;
22 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.NeutronHelper.getEpKeyFromNeutronMapper;
23 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.OvsdbHelper.createTunnelPort;
24 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.OvsdbHelper.getManagerNode;
25 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.OvsdbHelper.getOvsdbBridgeFromTerminationPoint;
26 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.OvsdbHelper.getTopologyNode;
27 import static org.opendaylight.groupbasedpolicy.util.DataStoreHelper.readFromDs;
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.List;
34 import java.util.Map.Entry;
36 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
37 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
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.AsyncDataBroker.DataChangeScope;
41 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
42 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
43 import org.opendaylight.ovsdb.southbound.SouthboundConstants;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.UniqueId;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.EndpointService;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointKey;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.port._interface.attributes.InterfaceExternalIds;
54 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
55 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
56 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
57 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
58 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
59 import org.opendaylight.yangtools.concepts.ListenerRegistration;
60 import org.opendaylight.yangtools.yang.binding.DataObject;
61 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
65 import com.google.common.base.Optional;
67 public class TerminationPointDataChangeListener implements DataChangeListener, AutoCloseable {
69 private static final String NEUTRON_EXTERNAL_ID_KEY = "iface-id";
70 private final ListenerRegistration<DataChangeListener> registration;
71 private final DataBroker dataBroker;
72 private final EndpointService epService;
73 private static final Logger LOG = LoggerFactory.getLogger(TerminationPointDataChangeListener.class);
74 private final List<AbstractTunnelType> requiredTunnelTypes;
76 public TerminationPointDataChangeListener(DataBroker dataBroker, EndpointService epService) {
77 this.dataBroker = checkNotNull(dataBroker);
78 this.epService = checkNotNull(epService);
79 InstanceIdentifier<OvsdbTerminationPointAugmentation> iid = InstanceIdentifier.create(NetworkTopology.class)
80 .child(Topology.class, new TopologyKey(SouthboundConstants.OVSDB_TOPOLOGY_ID))
82 .child(TerminationPoint.class)
83 .augmentation(OvsdbTerminationPointAugmentation.class);
85 dataBroker.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, iid, this, DataChangeScope.ONE);
86 requiredTunnelTypes = createSupportedTunnelsList();
89 private List<AbstractTunnelType> createSupportedTunnelsList() {
90 List<AbstractTunnelType> required = new ArrayList<AbstractTunnelType>();
91 required.add(new VxlanTunnelType());
92 required.add(new VxlanGpeTunnelType());
93 return Collections.unmodifiableList(required);
97 public void close() throws Exception {
102 * When vSwitch is deleted, we loose data in operational DS to determine Iid of
103 * corresponding NodeId.
105 private static final Map<InstanceIdentifier<OvsdbTerminationPointAugmentation>, NodeId> nodeIdByTerminPoint =
109 public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
112 * TerminationPoint notifications with OVSDB augmentations
113 * vSwitch ports. Iterate through the list of new ports.
115 for (Entry<InstanceIdentifier<?>, DataObject> entry : change.getCreatedData().entrySet()) {
116 if (entry.getValue() instanceof OvsdbTerminationPointAugmentation) {
117 OvsdbTerminationPointAugmentation ovsdbTp = (OvsdbTerminationPointAugmentation) entry.getValue();
118 @SuppressWarnings("unchecked")
119 InstanceIdentifier<OvsdbTerminationPointAugmentation> ovsdbTpIid =
120 (InstanceIdentifier<OvsdbTerminationPointAugmentation>) entry.getKey();
121 OvsdbBridgeAugmentation ovsdbBridge = getOvsdbBridgeFromTerminationPoint(ovsdbTpIid, dataBroker);
122 nodeIdByTerminPoint.put(ovsdbTpIid,
123 new NodeId(getInventoryNodeIdString(ovsdbBridge, ovsdbTpIid, dataBroker)));
124 processOvsdbBridge(ovsdbBridge, ovsdbTp, ovsdbTpIid);
131 for (Entry<InstanceIdentifier<?>, DataObject> entry : change.getUpdatedData().entrySet()) {
132 if (entry.getValue() instanceof OvsdbTerminationPointAugmentation) {
133 OvsdbTerminationPointAugmentation ovsdbTp = (OvsdbTerminationPointAugmentation) entry.getValue();
134 @SuppressWarnings("unchecked")
135 InstanceIdentifier<OvsdbTerminationPointAugmentation> ovsdbTpIid =
136 (InstanceIdentifier<OvsdbTerminationPointAugmentation>) entry.getKey();
137 OvsdbBridgeAugmentation ovsdbBridge = getOvsdbBridgeFromTerminationPoint(ovsdbTpIid, dataBroker);
138 processOvsdbBridge(ovsdbBridge, ovsdbTp, ovsdbTpIid);
145 for (InstanceIdentifier<?> iid : change.getRemovedPaths()) {
146 DataObject old = change.getOriginalData().get(iid);
147 if (old instanceof OvsdbTerminationPointAugmentation) {
148 OvsdbTerminationPointAugmentation ovsdbTp = (OvsdbTerminationPointAugmentation) old;
149 @SuppressWarnings("unchecked")
150 InstanceIdentifier<OvsdbTerminationPointAugmentation> ovsdbTpIid =
151 (InstanceIdentifier<OvsdbTerminationPointAugmentation>) iid;
152 processRemovedTp(nodeIdByTerminPoint.get(ovsdbTpIid), ovsdbTp, ovsdbTpIid);
157 private void processOvsdbBridge(OvsdbBridgeAugmentation ovsdbBridge, OvsdbTerminationPointAugmentation ovsdbTp,
158 InstanceIdentifier<OvsdbTerminationPointAugmentation> ovsdbTpIid) {
160 checkNotNull(ovsdbBridge);
161 if (ovsdbBridge.getBridgeName().getValue().equals(ovsdbTp.getName())) {
162 LOG.debug("Termination Point {} same as Bridge {}. Not processing", ovsdbTp.getName(),
163 ovsdbBridge.getBridgeName().getValue());
167 String nodeIdString = getInventoryNodeIdString(ovsdbBridge, ovsdbTpIid, dataBroker);
168 if (nodeIdString == null) {
169 LOG.debug("nodeIdString for TerminationPoint {} was null", ovsdbTp);
172 String nodeConnectorIdString = getInventoryNodeConnectorIdString(nodeIdString, ovsdbTp, ovsdbTpIid, dataBroker);
173 if (nodeConnectorIdString == null) {
174 LOG.debug("nodeConnectorIdString for TerminationPoint {} was null", ovsdbTp);
178 InstanceIdentifier<Node> nodeIid = ovsdbTpIid.firstIdentifierOf(Node.class);
179 String externalId = getNeutronPortUuid(ovsdbTp);
181 IpAddress hostIp = getIpFromOvsdb(ovsdbBridge);
184 * Ports created by Nova have an external_id field
185 * in them, which is the Neutron port UUID. If a port
186 * has an external_id, get the EndpointKey for the
187 * Neutron port UUID from neutron-mapper, then look
188 * up the Endpoint in the Endpoint Registry using
189 * that key an update it with the location information
190 * (NodeId and NodeConnectorId from the inventory model)
191 * and the port name, constructed using the port UUID.
194 if (externalId != null) {
195 EndpointKey epKey = getEpKeyFromNeutronMapper(new UniqueId(externalId), dataBroker);
197 LOG.debug("TerminationPoint {} with external ID {} is not in Neutron Map", ovsdbTp, externalId);
200 ReadOnlyTransaction transaction = dataBroker.newReadOnlyTransaction();
201 ep = lookupEndpoint(epKey, transaction);
204 "TerminationPoint {} with external ID {} is in Neutron Map, but corresponding Endpoint {} isn't in Endpoint Repository",
205 ovsdbTp, externalId, epKey);
209 * Look up the Node in Inventory that corresponds to the
210 * Topology Node that owns this Termination Point (port),
211 * and see if it already is configured with a complete
212 * OfOverlay augmentation. If it hasn't, go see if the
213 * tunnel ports exist, and if not, go and create them.
215 if (checkOfOverlayConfig(nodeIdString, requiredTunnelTypes, dataBroker) != true) {
216 checkNotNull(nodeIid);
218 * Check to see if we need to create a
219 * tunnel port on the parent node
221 createTunnelPorts(nodeIid, dataBroker);
224 LOG.debug("TerminationPoint {} had no external ID, not processing for external ID.", ovsdbTp);
229 * Check if Neutron External port was announed in Node before TerminationPoint it refers to
230 * was actually instantiated. This may or may not have external information in the future,
232 * not process as IF/ELSE externalID.
234 ReadOnlyTransaction transaction = dataBroker.newReadOnlyTransaction();
235 Optional<Node> node = readFromDs(LogicalDatastoreType.OPERATIONAL, nodeIid, transaction);
236 if (node.isPresent() && node.get().getAugmentation(OvsdbNodeAugmentation.class) != null) {
237 OvsdbNodeAugmentation ovsdbNodeAug = node.get().getAugmentation(OvsdbNodeAugmentation.class);
238 if (getProviderMapping(ovsdbNodeAug) != null) {
239 processNodeNotification(ovsdbNodeAug);
245 * This may be a notification for a tunnel we just created.
246 * In that case, we need to update the Inventory Node's OfOverlay
247 * augmentation with missing information
249 AbstractTunnelType tunnel = getTunnelType(ovsdbTp, requiredTunnelTypes);
250 if (tunnel != null) {
251 updateOfOverlayConfig(hostIp, nodeIdString, nodeConnectorIdString, tunnel, dataBroker);
253 if (externalId != null) {
254 ReadWriteTransaction rwTx = dataBroker.newReadWriteTransaction();
255 updateEndpointWithLocation(ep, nodeIdString, nodeConnectorIdString, rwTx);
260 * If removed termination point was a tunnel port,
261 * removes attached tunnels (namely Vxlan-type) from OVSDB bridge;
262 * else removes location info from TP
264 * @param nodeId {@link NodeId}
265 * @param ovsdbTp {@link OvsdbTerminationPointAugmentation}
266 * @param ovsdbTpIid termination point's IID {@link InstanceIdentifier}
268 private void processRemovedTp(NodeId nodeId, OvsdbTerminationPointAugmentation ovsdbTp,
269 InstanceIdentifier<OvsdbTerminationPointAugmentation> ovsdbTpIid) {
270 if (isTunnelPort(ovsdbTp, requiredTunnelTypes)) {
271 removeTunnelsOfOverlayConfig(nodeId.getValue(), requiredTunnelTypes, dataBroker);
273 deleteLocationForTp(ovsdbTp);
278 * Delete location on EP for given TP
280 * @param ovsdbTp {@link OvsdbTerminationPointAugmentation}
282 private void deleteLocationForTp(OvsdbTerminationPointAugmentation ovsdbTp) {
283 String externalId = getNeutronPortUuid(ovsdbTp);
284 if (externalId != null) {
285 EndpointKey epKey = getEpKeyFromNeutronMapper(new UniqueId(externalId), dataBroker);
287 LOG.debug("TerminationPoint {} with external ID {} is not in Neutron Map.", ovsdbTp, externalId);
290 ReadOnlyTransaction readOnlyTransaction = dataBroker.newReadOnlyTransaction();
291 Endpoint ep = lookupEndpoint(epKey, readOnlyTransaction);
292 readOnlyTransaction.close();
295 "TerminationPoint {} with external ID {} is in Neutron Map, but corresponding Endpoint {} isn't in Endpoint Repository.",
296 ovsdbTp, externalId, epKey);
299 updateEndpointRemoveLocation(ep, dataBroker.newReadWriteTransaction());
301 LOG.debug("TerminationPoint {} has no external ID, not processing.", ovsdbTp);
306 * Check to see if the {@link OvsdbTerminationPointAugmentation} is also a Tunnel port that we
309 * @param ovsdbTp {@link OvsdbTerminationPointAugmentation}
310 * @param requiredTunnelTypes {@link List} of tunnel types
312 private static AbstractTunnelType getTunnelType(OvsdbTerminationPointAugmentation ovsdbTp,
313 List<AbstractTunnelType> requiredTunnelTypes) {
314 if (ovsdbTp.getInterfaceType() != null) {
315 for (AbstractTunnelType tunnelType : requiredTunnelTypes) {
316 if (tunnelType.isValidTunnelPort(ovsdbTp)) {
325 * Check to see if the {@link OvsdbTerminationPointAugmentation}
326 * is also a Tunnel port that we care about.
328 * @param ovsdbTp {@link OvsdbTerminationPointAugmentation}
330 * @param requiredTunnelTypes {@link List} of tunnel types
332 * @return true if it's a required tunnel port, false if it isn't
334 private boolean isTunnelPort(OvsdbTerminationPointAugmentation ovsdbTp,
335 List<AbstractTunnelType> requiredTunnelTypes) {
336 if (ovsdbTp.getInterfaceType() != null) {
337 for (AbstractTunnelType tunnelType : requiredTunnelTypes) {
338 if (tunnelType.isValidTunnelPort(ovsdbTp)) {
347 * Get the Neutron Port UUID from an {@link OvsdbTerminationPointAugmentation}.
348 * The Neutron Port UUID is stored as an "external-id" in the termination point.
350 * @param ovsdbTp The {@link OvsdbTerminationPointAugmentation}
351 * @return The String representation of the Neutron Port UUID, null if not present
353 private String getNeutronPortUuid(OvsdbTerminationPointAugmentation ovsdbTp) {
354 if (ovsdbTp.getInterfaceExternalIds() == null) {
357 for (InterfaceExternalIds id : ovsdbTp.getInterfaceExternalIds()) {
358 if (id.getExternalIdKey() != null && id.getExternalIdKey().equals(NEUTRON_EXTERNAL_ID_KEY)) {
360 if (id.getExternalIdValue() != null) {
361 return id.getExternalIdValue();
369 * Check to see if all tunnel ports are present, and if not,
372 * @param nodeIid {@link InstanceIdentifier}
373 * @param dataBroker {@link DataBroker}
375 private void createTunnelPorts(InstanceIdentifier<Node> nodeIid, DataBroker dataBroker) {
377 Node node = getTopologyNode(nodeIid, dataBroker);
380 if (node.getAugmentation(OvsdbBridgeAugmentation.class) == null) {
381 LOG.trace("Node {} is not an OVSDB manageable node", nodeIid);
386 * See if this Topology Node has the required tunnel ports,
387 * and if not, go and create them
389 for (AbstractTunnelType tunnelType : requiredTunnelTypes) {
390 boolean tunnelPresent = false;
391 for (TerminationPoint tp : node.getTerminationPoint()) {
392 OvsdbTerminationPointAugmentation tpAug = tp.getAugmentation(OvsdbTerminationPointAugmentation.class);
396 if (tunnelType.isValidTunnelPort(tpAug)) {
397 tunnelPresent = true;
401 if (!tunnelPresent) {
402 createTunnelPort(nodeIid, node, tunnelType, dataBroker);
408 * Get the IP address of the host that owns the {@link OvsdbBridgeAugmentation}.
410 * @param ovsdbBridge The OVSDB bridge node
411 * @return The IP address of the host that the bridge is on
413 private IpAddress getIpFromOvsdb(OvsdbBridgeAugmentation ovsdbBridge) {
415 * The manager Node referenced by this node has the
418 OvsdbNodeAugmentation managerNode = getManagerNode(ovsdbBridge, dataBroker);
420 if (managerNode == null)
423 if (managerNode.getConnectionInfo() != null) {
424 return managerNode.getConnectionInfo().getRemoteIp();