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 com.google.common.base.Optional;
12 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
13 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
14 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
15 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
16 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
17 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
18 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
19 import org.opendaylight.ovsdb.southbound.SouthboundConstants;
20 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.Uuid;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.EndpointService;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointKey;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.port._interface.attributes.InterfaceExternalIds;
29 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
30 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
31 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
32 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
33 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
34 import org.opendaylight.yangtools.concepts.ListenerRegistration;
35 import org.opendaylight.yangtools.yang.binding.DataObject;
36 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
40 import java.util.ArrayList;
41 import java.util.Collections;
42 import java.util.List;
43 import java.util.Map.Entry;
45 import static com.google.common.base.Preconditions.checkNotNull;
46 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.NodeDataChangeListener.getProviderMapping;
47 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.NodeDataChangeListener.processNodeNotification;
48 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.EndpointHelper.lookupEndpoint;
49 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.EndpointHelper.updateEndpointRemoveLocation;
50 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.EndpointHelper.updateEndpointWithLocation;
51 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.InventoryHelper.checkOfOverlayConfig;
52 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.InventoryHelper.getInventoryNodeConnectorIdString;
53 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.InventoryHelper.getInventoryNodeIdString;
54 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.InventoryHelper.removeTunnelsOfOverlayConfig;
55 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.InventoryHelper.updateOfOverlayConfig;
56 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.NeutronHelper.getEpKeyFromNeutronMapper;
57 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.OvsdbHelper.createTunnelPort;
58 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.OvsdbHelper.getManagerNode;
59 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.OvsdbHelper.getOvsdbBridgeFromTerminationPoint;
60 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.OvsdbHelper.getTopologyNode;
61 import static org.opendaylight.groupbasedpolicy.util.DataStoreHelper.readFromDs;
63 public class TerminationPointDataChangeListener implements DataChangeListener, AutoCloseable {
65 private static final String NEUTRON_EXTERNAL_ID_KEY = "iface-id";
66 private final ListenerRegistration<DataChangeListener> registration;
67 private final DataBroker dataBroker;
68 private final EndpointService epService;
69 private static final Logger LOG = LoggerFactory.getLogger(TerminationPointDataChangeListener.class);
70 private final List<AbstractTunnelType> requiredTunnelTypes;
72 public TerminationPointDataChangeListener(DataBroker dataBroker, EndpointService epService) {
73 this.dataBroker = checkNotNull(dataBroker);
74 this.epService = checkNotNull(epService);
75 InstanceIdentifier<OvsdbTerminationPointAugmentation> iid = InstanceIdentifier.create(NetworkTopology.class)
76 .child(Topology.class, new TopologyKey(SouthboundConstants.OVSDB_TOPOLOGY_ID))
78 .child(TerminationPoint.class)
79 .augmentation(OvsdbTerminationPointAugmentation.class);
80 registration = dataBroker.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, iid, this,
82 requiredTunnelTypes = createSupportedTunnelsList();
85 private List<AbstractTunnelType> createSupportedTunnelsList() {
86 List<AbstractTunnelType> required = new ArrayList<AbstractTunnelType>();
87 // required.add(new VxlanGpeTunnelType());
88 required.add(new VxlanTunnelType());
89 return Collections.unmodifiableList(required);
93 public void close() throws Exception {
98 public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
101 * TerminationPoint notifications with OVSDB augmentations
102 * vSwitch ports. Iterate through the list of new ports.
104 for (Entry<InstanceIdentifier<?>, DataObject> entry : change.getCreatedData().entrySet()) {
105 if (entry.getValue() instanceof OvsdbTerminationPointAugmentation) {
106 OvsdbTerminationPointAugmentation ovsdbTp = (OvsdbTerminationPointAugmentation) entry.getValue();
107 InstanceIdentifier<OvsdbTerminationPointAugmentation> ovsdbTpIid = (InstanceIdentifier<OvsdbTerminationPointAugmentation>) entry.getKey();
108 OvsdbBridgeAugmentation ovsdbBridge = getOvsdbBridgeFromTerminationPoint(ovsdbTpIid, dataBroker);
109 processOvsdbBridge(ovsdbBridge, ovsdbTp, ovsdbTpIid);
116 for (Entry<InstanceIdentifier<?>, DataObject> entry : change.getUpdatedData().entrySet()) {
117 if (entry.getValue() instanceof OvsdbTerminationPointAugmentation) {
118 OvsdbTerminationPointAugmentation ovsdbTp = (OvsdbTerminationPointAugmentation) entry.getValue();
119 InstanceIdentifier<OvsdbTerminationPointAugmentation> ovsdbTpIid = (InstanceIdentifier<OvsdbTerminationPointAugmentation>) entry.getKey();
120 OvsdbBridgeAugmentation ovsdbBridge = getOvsdbBridgeFromTerminationPoint(ovsdbTpIid, dataBroker);
121 processOvsdbBridge(ovsdbBridge, ovsdbTp, ovsdbTpIid);
128 for (InstanceIdentifier<?> iid : change.getRemovedPaths()) {
129 DataObject old = change.getOriginalData().get(iid);
130 if (old instanceof OvsdbTerminationPointAugmentation) {
131 OvsdbTerminationPointAugmentation ovsdbTp = (OvsdbTerminationPointAugmentation) old;
132 @SuppressWarnings("unchecked")
133 InstanceIdentifier<OvsdbTerminationPointAugmentation> ovsdbTpIid =
134 (InstanceIdentifier<OvsdbTerminationPointAugmentation>) iid;
135 OvsdbBridgeAugmentation ovsdbBridge = getOvsdbBridgeFromTerminationPoint(ovsdbTpIid, dataBroker);
136 if (ovsdbBridge != null) {
137 processRemovedTp(ovsdbBridge, ovsdbTp, ovsdbTpIid);
143 private void processOvsdbBridge(OvsdbBridgeAugmentation ovsdbBridge, OvsdbTerminationPointAugmentation ovsdbTp,
144 InstanceIdentifier<OvsdbTerminationPointAugmentation> ovsdbTpIid) {
146 checkNotNull(ovsdbBridge);
147 if (ovsdbBridge.getBridgeName().getValue().equals(ovsdbTp.getName())) {
148 LOG.debug("Termination Point {} same as Bridge {}. Not processing", ovsdbTp.getName(),
149 ovsdbBridge.getBridgeName().getValue());
153 String nodeIdString = getInventoryNodeIdString(ovsdbBridge, ovsdbTpIid, dataBroker);
154 if (nodeIdString == null) {
155 LOG.debug("nodeIdString for TerminationPoint {} was null", ovsdbTp);
158 String nodeConnectorIdString = getInventoryNodeConnectorIdString(nodeIdString, ovsdbTp, ovsdbTpIid, dataBroker);
159 if (nodeConnectorIdString == null) {
160 LOG.debug("nodeConnectorIdString for TerminationPoint {} was null", ovsdbTp);
164 InstanceIdentifier<Node> nodeIid = ovsdbTpIid.firstIdentifierOf(Node.class);
165 String externalId = getNeutronPortUuid(ovsdbTp);
167 IpAddress hostIp = getIpFromOvsdb(ovsdbBridge);
170 * Ports created by Nova have an external_id field
171 * in them, which is the Neutron port UUID. If a port
172 * has an external_id, get the EndpointKey for the
173 * Neutron port UUID from neutron-mapper, then look
174 * up the Endpoint in the Endpoint Registry using
175 * that key an update it with the location information
176 * (NodeId and NodeConnectorId from the inventory model)
177 * and the port name, constructed using the port UUID.
180 if (externalId != null) {
181 EndpointKey epKey = getEpKeyFromNeutronMapper(new Uuid(externalId), dataBroker);
183 LOG.debug("TerminationPoint {} with external ID {} is not in Neutron Map", ovsdbTp, externalId);
186 ReadOnlyTransaction transaction = dataBroker.newReadOnlyTransaction();
187 ep = lookupEndpoint(epKey, transaction);
190 "TerminationPoint {} with external ID {} is in Neutron Map, but corresponding Endpoint {} isn't in Endpoint Repository",
191 ovsdbTp, externalId, epKey);
195 * Look up the Node in Inventory that corresponds to the
196 * Topology Node that owns this Termination Point (port),
197 * and see if it already is configured with a complete
198 * OfOverlay augmentation. If it hasn't, go see if the
199 * tunnel ports exist, and if not, go and create them.
201 if (checkOfOverlayConfig(nodeIdString, requiredTunnelTypes, dataBroker) != true) {
202 checkNotNull(nodeIid);
204 * Check to see if we need to create a
205 * tunnel port on the parent node
207 createTunnelPorts(nodeIid, dataBroker);
210 LOG.debug("TerminationPoint {} had no external ID, not processing for external ID.", ovsdbTp);
215 * Check if Neutron External port was announed in Node before TerminationPoint it refers to
216 * was actually instantiated. This may or may not have external information in the future, hence
217 * not process as IF/ELSE externalID.
219 ReadOnlyTransaction transaction = dataBroker.newReadOnlyTransaction();
220 Optional<Node> node = readFromDs(LogicalDatastoreType.OPERATIONAL, nodeIid, transaction);
222 && node.get().getAugmentation(OvsdbNodeAugmentation.class) != null) {
223 OvsdbNodeAugmentation ovsdbNodeAug = node.get().getAugmentation(OvsdbNodeAugmentation.class);
224 if (getProviderMapping(ovsdbNodeAug) != null) {
225 processNodeNotification(ovsdbNodeAug);
231 * This may be a notification for a tunnel we just created.
232 * In that case, we need to update the Inventory Node's OfOverlay
233 * augmentation with missing information
235 if (isTunnelPort(ovsdbTp, requiredTunnelTypes)) {
236 updateOfOverlayConfig(hostIp, nodeIdString, nodeConnectorIdString, requiredTunnelTypes, dataBroker);
238 if (externalId != null) {
239 ReadWriteTransaction rwTx = dataBroker.newReadWriteTransaction();
240 updateEndpointWithLocation(ep, nodeIdString, nodeConnectorIdString, rwTx);
245 * If removed termination point was a tunnel port,
246 * removes attached tunnels (namely Vxlan-type) from OVSDB bridge;
247 * else removes location info from TP
249 * @param ovsdbBridge {@link OvsdbBridgeAugmentation}
250 * @param ovsdbTp {@link OvsdbTerminationPointAugmentation}
251 * @param ovsdbTpIid termination point's IID {@link InstanceIdentifier}
253 private void processRemovedTp(OvsdbBridgeAugmentation ovsdbBridge, OvsdbTerminationPointAugmentation ovsdbTp, InstanceIdentifier<OvsdbTerminationPointAugmentation> ovsdbTpIid) {
255 checkNotNull(ovsdbBridge);
256 if (ovsdbBridge.getBridgeName().getValue().equals(ovsdbTp.getName())) {
257 LOG.debug("Termination Point {} same as Bridge {}. Not processing.", ovsdbTp.getName(), ovsdbBridge.getBridgeName().getValue());
261 String nodeIdString = getInventoryNodeIdString(ovsdbBridge, ovsdbTpIid, dataBroker);
262 if (nodeIdString == null) {
263 LOG.debug("nodeIdString for TerminationPoint {} was null.", ovsdbTp);
267 if (isTunnelPort(ovsdbTp, requiredTunnelTypes)) {
268 removeTunnelsOfOverlayConfig(nodeIdString, requiredTunnelTypes, dataBroker);
270 deleteLocationForTp(ovsdbTp);
275 * Delete location on EP for given TP
277 * @param ovsdbTp {@link OvsdbTerminationPointAugmentation}
279 private void deleteLocationForTp(OvsdbTerminationPointAugmentation ovsdbTp){
280 String externalId = getNeutronPortUuid(ovsdbTp);
281 if (externalId != null) {
282 EndpointKey epKey = getEpKeyFromNeutronMapper(new Uuid(externalId), dataBroker);
284 LOG.debug("TerminationPoint {} with external ID {} is not in Neutron Map.", ovsdbTp, externalId);
287 ReadOnlyTransaction readOnlyTransaction = dataBroker.newReadOnlyTransaction();
288 Endpoint ep = lookupEndpoint(epKey, readOnlyTransaction);
289 readOnlyTransaction.close();
291 LOG.warn("TerminationPoint {} with external ID {} is in Neutron Map, but corresponding Endpoint {} isn't in Endpoint Repository.", ovsdbTp, externalId, epKey);
294 updateEndpointRemoveLocation(ep, dataBroker.newReadWriteTransaction());
296 LOG.debug("TerminationPoint {} has no external ID, not processing.", ovsdbTp);
301 * Check to see if the {@link OvsdbTerminationPointAugmentation}
302 * is also a Tunnel port that we care about.
304 * @param ovsdbTp {@link OvsdbTerminationPointAugmentation}
305 * @param requiredTunnelTypes {@link List} of tunnel types
306 * @return true if it's a required tunnel port, false if it isn't
308 private boolean isTunnelPort(OvsdbTerminationPointAugmentation ovsdbTp, List<AbstractTunnelType> requiredTunnelTypes) {
309 if (ovsdbTp.getInterfaceType() != null) {
310 for (AbstractTunnelType tunnelType : requiredTunnelTypes) {
311 if (tunnelType.isValidTunnelPort(ovsdbTp)) {
320 * Get the Neutron Port UUID from an {@link OvsdbTerminationPointAugmentation}.
321 * The Neutron Port UUID is stored as an "external-id" in the termination point.
323 * @param ovsdbTp The OVSDB Termination Point augmentation
324 * @return The String representation of the Neutron Port UUID, null if not present
326 private String getNeutronPortUuid(OvsdbTerminationPointAugmentation ovsdbTp) {
327 if (ovsdbTp.getInterfaceExternalIds() == null) {
330 for (InterfaceExternalIds id : ovsdbTp.getInterfaceExternalIds()) {
331 if (id.getExternalIdKey() != null && id.getExternalIdKey().equals(NEUTRON_EXTERNAL_ID_KEY)) {
333 if (id.getExternalIdValue() != null) {
334 return id.getExternalIdValue();
342 * Check to see if all tunnel ports are present, and if not,
345 * @param nodeIid {@link InstanceIdentifier}
346 * @param dataBroker {@link DataBroker}
348 private void createTunnelPorts(InstanceIdentifier<Node> nodeIid, DataBroker dataBroker) {
350 Node node = getTopologyNode(nodeIid, dataBroker);
353 if (node.getAugmentation(OvsdbBridgeAugmentation.class) == null) {
354 LOG.trace("Node {} is not an OVSDB manageable node", nodeIid);
359 * See if this Topology Node has the required tunnel ports,
360 * and if not, go and create them
362 for (AbstractTunnelType tunnelType : requiredTunnelTypes) {
363 boolean tunnelPresent = false;
364 for (TerminationPoint tp : node.getTerminationPoint()) {
365 OvsdbTerminationPointAugmentation tpAug = tp.getAugmentation(OvsdbTerminationPointAugmentation.class);
369 if (tunnelType.isValidTunnelPort(tpAug)) {
370 tunnelPresent = true;
374 if (!tunnelPresent) {
375 createTunnelPort(nodeIid, node, tunnelType, dataBroker);
381 * Get the IP address of the host that owns the {@link OvsdbBridgeAugmentation}.
383 * @param ovsdbBridge The OVSDB bridge node
384 * @return The IP address of the host that the bridge is on
386 private IpAddress getIpFromOvsdb(OvsdbBridgeAugmentation ovsdbBridge) {
388 * The manager Node referenced by this node has the
391 OvsdbNodeAugmentation managerNode = getManagerNode(ovsdbBridge, dataBroker);
393 if (managerNode == null)
396 if (managerNode.getConnectionInfo() != null) {
397 return managerNode.getConnectionInfo().getRemoteIp();