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.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;
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;
32 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
33 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
34 import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
35 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
36 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
37 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
38 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
39 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
40 import org.opendaylight.ovsdb.southbound.SouthboundConstants;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.UniqueId;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.EndpointService;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointKey;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.port._interface.attributes.InterfaceExternalIds;
51 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
52 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
53 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
54 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
55 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
56 import org.opendaylight.yangtools.concepts.ListenerRegistration;
57 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
61 public class TerminationPointDataChangeListener implements DataTreeChangeListener<OvsdbTerminationPointAugmentation>,
64 private static final String NEUTRON_EXTERNAL_ID_KEY = "iface-id";
65 private final ListenerRegistration<?> 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;
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))
77 .child(TerminationPoint.class)
78 .augmentation(OvsdbTerminationPointAugmentation.class);
79 registration = dataBroker.registerDataTreeChangeListener(new DataTreeIdentifier<>(
80 LogicalDatastoreType.OPERATIONAL, iid), this);
81 requiredTunnelTypes = createSupportedTunnelsList();
84 private List<AbstractTunnelType> createSupportedTunnelsList() {
85 List<AbstractTunnelType> required = new ArrayList<>();
86 required.add(new VxlanTunnelType());
87 required.add(new VxlanGpeTunnelType());
88 return Collections.unmodifiableList(required);
92 public void close() throws Exception {
97 * When vSwitch is deleted, we loose data in operational DS to determine Iid of
98 * corresponding NodeId.
100 private static final Map<InstanceIdentifier<OvsdbTerminationPointAugmentation>, NodeId> nodeIdByTerminPoint =
104 public void onDataTreeChanged(Collection<DataTreeModification<OvsdbTerminationPointAugmentation>> changes) {
105 for (DataTreeModification<OvsdbTerminationPointAugmentation> change: changes) {
106 DataObjectModification<OvsdbTerminationPointAugmentation> rootNode = change.getRootNode();
107 InstanceIdentifier<OvsdbTerminationPointAugmentation> ovsdbTpIid = change.getRootPath().getRootIdentifier();
108 OvsdbTerminationPointAugmentation origOvsdbTp = rootNode.getDataBefore();
109 switch (rootNode.getModificationType()) {
110 case SUBTREE_MODIFIED:
112 OvsdbTerminationPointAugmentation updatedOvsdbTp = rootNode.getDataAfter();
113 OvsdbBridgeAugmentation ovsdbBridge = getOvsdbBridgeFromTerminationPoint(ovsdbTpIid, dataBroker);
114 if (origOvsdbTp == null) {
115 nodeIdByTerminPoint.put(ovsdbTpIid,
116 new NodeId(getInventoryNodeIdString(ovsdbBridge, ovsdbTpIid, dataBroker)));
119 processOvsdbBridge(ovsdbBridge, updatedOvsdbTp, ovsdbTpIid);
122 processRemovedTp(nodeIdByTerminPoint.get(ovsdbTpIid), origOvsdbTp, ovsdbTpIid);
130 private void processOvsdbBridge(OvsdbBridgeAugmentation ovsdbBridge, OvsdbTerminationPointAugmentation ovsdbTp,
131 InstanceIdentifier<OvsdbTerminationPointAugmentation> ovsdbTpIid) {
133 checkNotNull(ovsdbBridge);
134 if (ovsdbBridge.getBridgeName().getValue().equals(ovsdbTp.getName())) {
135 LOG.debug("Termination Point {} same as Bridge {}. Not processing", ovsdbTp.getName(),
136 ovsdbBridge.getBridgeName().getValue());
140 String nodeIdString = getInventoryNodeIdString(ovsdbBridge, ovsdbTpIid, dataBroker);
141 if (nodeIdString == null) {
142 LOG.debug("nodeIdString for TerminationPoint {} was null", ovsdbTp);
145 String nodeConnectorIdString = getInventoryNodeConnectorIdString(nodeIdString, ovsdbTp, ovsdbTpIid, dataBroker);
146 if (nodeConnectorIdString == null) {
147 LOG.debug("nodeConnectorIdString for TerminationPoint {} was null", ovsdbTp);
151 InstanceIdentifier<Node> nodeIid = ovsdbTpIid.firstIdentifierOf(Node.class);
152 String externalId = getNeutronPortUuid(ovsdbTp);
154 IpAddress hostIp = getIpFromOvsdb(ovsdbBridge);
157 * Ports created by Nova have an external_id field
158 * in them, which is the Neutron port UUID. If a port
159 * has an external_id, get the EndpointKey for the
160 * Neutron port UUID from neutron-mapper, then look
161 * up the Endpoint in the Endpoint Registry using
162 * that key an update it with the location information
163 * (NodeId and NodeConnectorId from the inventory model)
164 * and the port name, constructed using the port UUID.
167 if (externalId != null) {
168 EndpointKey epKey = getEpKeyFromNeutronMapper(new UniqueId(externalId), dataBroker);
170 LOG.debug("TerminationPoint {} with external ID {} is not in Neutron Map", ovsdbTp, externalId);
173 ReadOnlyTransaction transaction = dataBroker.newReadOnlyTransaction();
174 ep = lookupEndpoint(epKey, transaction);
177 "TerminationPoint {} with external ID {} is in Neutron Map, but corresponding Endpoint {} isn't in Endpoint Repository",
178 ovsdbTp, externalId, epKey);
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.
188 if (!checkOfOverlayConfig(nodeIdString, requiredTunnelTypes, dataBroker)) {
189 checkNotNull(nodeIid);
191 * Check to see if we need to create a
192 * tunnel port on the parent node
194 createTunnelPorts(nodeIid, dataBroker);
197 LOG.debug("TerminationPoint {} had no external ID, not processing for external ID.", ovsdbTp);
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
206 AbstractTunnelType tunnel = getTunnelType(ovsdbTp, requiredTunnelTypes);
207 if (tunnel != null) {
208 updateOfOverlayConfig(hostIp, nodeIdString, nodeConnectorIdString, tunnel, dataBroker);
210 if (externalId != null) {
211 ReadWriteTransaction rwTx = dataBroker.newReadWriteTransaction();
212 updateEndpointWithLocation(ep, nodeIdString, nodeConnectorIdString, rwTx);
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
221 * @param nodeId {@link NodeId}
222 * @param ovsdbTp {@link OvsdbTerminationPointAugmentation}
223 * @param ovsdbTpIid termination point's IID {@link InstanceIdentifier}
225 private void processRemovedTp(NodeId nodeId, OvsdbTerminationPointAugmentation ovsdbTp,
226 InstanceIdentifier<OvsdbTerminationPointAugmentation> ovsdbTpIid) {
227 if (isTunnelPort(ovsdbTp, requiredTunnelTypes)) {
228 removeTunnelsOfOverlayConfig(nodeId.getValue(), requiredTunnelTypes, dataBroker);
230 deleteLocationForTp(ovsdbTp);
235 * Delete location on EP for given TP
237 * @param ovsdbTp {@link OvsdbTerminationPointAugmentation}
239 private void deleteLocationForTp(OvsdbTerminationPointAugmentation ovsdbTp) {
240 String externalId = getNeutronPortUuid(ovsdbTp);
241 if (externalId != null) {
242 EndpointKey epKey = getEpKeyFromNeutronMapper(new UniqueId(externalId), dataBroker);
244 LOG.debug("TerminationPoint {} with external ID {} is not in Neutron Map.", ovsdbTp, externalId);
247 ReadOnlyTransaction readOnlyTransaction = dataBroker.newReadOnlyTransaction();
248 Endpoint ep = lookupEndpoint(epKey, readOnlyTransaction);
249 readOnlyTransaction.close();
252 "TerminationPoint {} with external ID {} is in Neutron Map, but corresponding Endpoint {} isn't in Endpoint Repository.",
253 ovsdbTp, externalId, epKey);
256 updateEndpointRemoveLocation(ep, dataBroker.newReadWriteTransaction());
258 LOG.debug("TerminationPoint {} has no external ID, not processing.", ovsdbTp);
263 * Check to see if the {@link OvsdbTerminationPointAugmentation} is also a Tunnel port that we
266 * @param ovsdbTp {@link OvsdbTerminationPointAugmentation}
267 * @param requiredTunnelTypes {@link List} of tunnel types
269 private static AbstractTunnelType getTunnelType(OvsdbTerminationPointAugmentation ovsdbTp,
270 List<AbstractTunnelType> requiredTunnelTypes) {
271 if (ovsdbTp.getInterfaceType() != null) {
272 for (AbstractTunnelType tunnelType : requiredTunnelTypes) {
273 if (tunnelType.isValidTunnelPort(ovsdbTp)) {
282 * Check to see if the {@link OvsdbTerminationPointAugmentation}
283 * is also a Tunnel port that we care about.
285 * @param ovsdbTp {@link OvsdbTerminationPointAugmentation}
287 * @param requiredTunnelTypes {@link List} of tunnel types
289 * @return true if it's a required tunnel port, false if it isn't
291 private boolean isTunnelPort(OvsdbTerminationPointAugmentation ovsdbTp,
292 List<AbstractTunnelType> requiredTunnelTypes) {
293 if (ovsdbTp.getInterfaceType() != null) {
294 for (AbstractTunnelType tunnelType : requiredTunnelTypes) {
295 if (tunnelType.isValidTunnelPort(ovsdbTp)) {
304 * Get the Neutron Port UUID from an {@link OvsdbTerminationPointAugmentation}.
305 * The Neutron Port UUID is stored as an "external-id" in the termination point.
307 * @param ovsdbTp The {@link OvsdbTerminationPointAugmentation}
308 * @return The String representation of the Neutron Port UUID, null if not present
310 private String getNeutronPortUuid(OvsdbTerminationPointAugmentation ovsdbTp) {
311 if (ovsdbTp.getInterfaceExternalIds() == null) {
314 for (InterfaceExternalIds id : ovsdbTp.getInterfaceExternalIds()) {
315 if (id.getExternalIdKey() != null && id.getExternalIdKey().equals(NEUTRON_EXTERNAL_ID_KEY)) {
317 if (id.getExternalIdValue() != null) {
318 return id.getExternalIdValue();
326 * Check to see if all tunnel ports are present, and if not,
329 * @param nodeIid {@link InstanceIdentifier}
330 * @param dataBroker {@link DataBroker}
332 private void createTunnelPorts(InstanceIdentifier<Node> nodeIid, DataBroker dataBroker) {
334 Node node = getTopologyNode(nodeIid, dataBroker);
337 if (node.getAugmentation(OvsdbBridgeAugmentation.class) == null) {
338 LOG.trace("Node {} is not an OVSDB manageable node", nodeIid);
343 * See if this Topology Node has the required tunnel ports,
344 * and if not, go and create them
346 for (AbstractTunnelType tunnelType : requiredTunnelTypes) {
347 boolean tunnelPresent = false;
348 for (TerminationPoint tp : node.getTerminationPoint()) {
349 OvsdbTerminationPointAugmentation tpAug = tp.getAugmentation(OvsdbTerminationPointAugmentation.class);
353 if (tunnelType.isValidTunnelPort(tpAug)) {
354 tunnelPresent = true;
358 if (!tunnelPresent) {
359 createTunnelPort(nodeIid, node, tunnelType, dataBroker);
365 * Get the IP address of the host that owns the {@link OvsdbBridgeAugmentation}.
367 * @param ovsdbBridge The OVSDB bridge node
368 * @return The IP address of the host that the bridge is on
370 private IpAddress getIpFromOvsdb(OvsdbBridgeAugmentation ovsdbBridge) {
372 * The manager Node referenced by this node has the
375 OvsdbNodeAugmentation managerNode = getManagerNode(ovsdbBridge, dataBroker);
377 if (managerNode == null) {
381 if (managerNode.getConnectionInfo() != null) {
382 return managerNode.getConnectionInfo().getRemoteIp();