2 * Copyright (C) 2013 Red Hat, Inc.
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
8 package org.opendaylight.ovsdb.openstack.netvirt;
10 import java.util.List;
13 import java.util.concurrent.ConcurrentMap;
15 import org.opendaylight.neutron.spi.NeutronNetwork;
16 import org.opendaylight.ovsdb.lib.notation.Row;
17 import org.opendaylight.ovsdb.lib.notation.UUID;
18 import org.opendaylight.ovsdb.openstack.netvirt.api.*;
19 import org.opendaylight.ovsdb.openstack.netvirt.impl.MdsalUtils;
20 import org.opendaylight.ovsdb.openstack.netvirt.impl.NeutronL3Adapter;
21 import org.opendaylight.ovsdb.schema.openvswitch.Bridge;
22 import org.opendaylight.ovsdb.schema.openvswitch.Interface;
23 import org.opendaylight.ovsdb.schema.openvswitch.OpenVSwitch;
24 import org.opendaylight.ovsdb.schema.openvswitch.Port;
25 import org.opendaylight.ovsdb.southbound.SouthboundMapper;
26 //import org.opendaylight.ovsdb.utils.mdsal.node.StringConvertor;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
30 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
31 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
36 * @author Madhu Venugopal
37 * @author Brent Salisbury
39 * @author Sam Hague (shague@redhat.com)
41 public class SouthboundHandler extends AbstractHandler
42 implements NodeCacheListener, OvsdbInventoryListener {
43 static final Logger logger = LoggerFactory.getLogger(SouthboundHandler.class);
45 // The implementation for each of these services is resolved by the OSGi Service Manager
46 private volatile ConfigurationService configurationService;
47 private volatile BridgeConfigurationManager bridgeConfigurationManager;
48 private volatile TenantNetworkManager tenantNetworkManager;
49 private volatile NetworkingProviderManager networkingProviderManager;
50 private volatile OvsdbConfigurationService ovsdbConfigurationService;
51 private volatile OvsdbConnectionService connectionService;
52 private volatile OvsdbInventoryService mdsalConsumer; // TODO SB_MIGRATION
53 private volatile NeutronL3Adapter neutronL3Adapter;
56 //this.triggerUpdates(); // TODO SB_MIGRATION
60 public void ovsdbNodeAdded(Node node) {
61 logger.info("ovsdbNodeAdded: {}", node);
62 this.enqueueEvent(new SouthboundEvent(node, Action.ADD));
66 public void ovsdbNodeRemoved(Node node) {
67 this.enqueueEvent(new SouthboundEvent(node, Action.DELETE));
71 public void rowAdded(Node node, String tableName, String uuid, Row row) {
72 this.enqueueEvent(new SouthboundEvent(node, tableName, uuid, row, Action.ADD));
76 public void rowUpdated(Node node, String tableName, String uuid, Row oldRow, Row newRow) {
77 if (this.isUpdateOfInterest(node, oldRow, newRow)) {
78 this.enqueueEvent(new SouthboundEvent(node, tableName, uuid, newRow, Action.UPDATE));
83 * Ignore unnecessary updates to be even considered for processing.
84 * (Especially stats update are fast and furious).
87 private boolean isUpdateOfInterest(Node node, Row oldRow, Row newRow) {
88 /* TODO SB_MIGRATION */
89 if (oldRow == null) return true;
90 if (newRow.getTableSchema().getName().equals(ovsdbConfigurationService.getTableName(node, Interface.class))) {
91 // We are NOT interested in Stats only updates
92 Interface oldIntf = ovsdbConfigurationService.getTypedRow(node, Interface.class, oldRow);
93 if (oldIntf.getName() == null && oldIntf.getExternalIdsColumn() == null && oldIntf.getMacColumn() == null &&
94 oldIntf.getOpenFlowPortColumn() == null && oldIntf.getOptionsColumn() == null && oldIntf.getOtherConfigColumn() == null &&
95 oldIntf.getTypeColumn() == null) {
96 logger.trace("IGNORING Interface Update: node {}, row: {}", node, newRow);
99 } else if (newRow.getTableSchema().getName().equals(ovsdbConfigurationService.getTableName(node, Port.class))) {
100 // We are NOT interested in Stats only updates
101 Port oldPort = ovsdbConfigurationService.getTypedRow(node, Port.class, oldRow);
102 if (oldPort.getName() == null && oldPort.getExternalIdsColumn() == null && oldPort.getMacColumn() == null &&
103 oldPort.getInterfacesColumn() == null && oldPort.getTagColumn() == null && oldPort.getTrunksColumn() == null) {
104 logger.trace("IGNORING Port Update: node {}, row: {}", node, newRow);
107 } else if (newRow.getTableSchema().getName().equals(ovsdbConfigurationService.getTableName(node, OpenVSwitch.class))) {
108 OpenVSwitch oldOpenvSwitch = ovsdbConfigurationService.getTypedRow(node, OpenVSwitch.class, oldRow);
109 if (oldOpenvSwitch.getOtherConfigColumn()== null) {
110 // we are only interested in other_config field change
118 public void rowRemoved(Node node, String tableName, String uuid, Row row, Object context) {
119 this.enqueueEvent(new SouthboundEvent(node, tableName, uuid, row, context, Action.DELETE));
122 private SouthboundEvent.Type ovsdbTypeToSouthboundEventType(OvsdbType ovsdbType) {
123 SouthboundEvent.Type type = SouthboundEvent.Type.NODE;
127 type = SouthboundEvent.Type.NODE;
130 type = SouthboundEvent.Type.BRIDGE;
133 type = SouthboundEvent.Type.PORT;
136 type = SouthboundEvent.Type.CONTROLLER;
139 type = SouthboundEvent.Type.OPENVSWITCH;
142 logger.warn("Invalid OvsdbType: {}", ovsdbType);
149 public void ovsdbUpdate(Node node, OvsdbType ovsdbType, Action action) {
150 logger.info("ovsdbUpdate: {} - {} - {}", node, ovsdbType, action);
151 this.enqueueEvent(new SouthboundEvent(node, ovsdbTypeToSouthboundEventType(ovsdbType), action));
154 public void processOvsdbNodeUpdate(Node node, Action action) {
155 if (action == Action.ADD) {
156 logger.info("processOvsdbNodeUpdate {}", node);
157 bridgeConfigurationManager.prepareNode(node);
159 logger.info("Not implemented yet: {}", action);
163 private void processRowUpdate(Node node, String tableName, String uuid, Row row,
164 Object context, Action action) {
165 /* TODO SB_MIGRATION */
166 if (action == Action.DELETE) {
167 if (tableName.equalsIgnoreCase(ovsdbConfigurationService.getTableName(node, Interface.class))) {
168 logger.debug("Processing update of {}. Deleted node: {}, uuid: {}, row: {}", tableName, node, uuid, row);
169 Interface deletedIntf = ovsdbConfigurationService.getTypedRow(node, Interface.class, row);
170 NeutronNetwork network = null;
171 if (context == null) {
172 network = tenantNetworkManager.getTenantNetwork(deletedIntf);
174 network = (NeutronNetwork)context;
176 List<String> phyIfName = bridgeConfigurationManager.getAllPhysicalInterfaceNames(node);
177 logger.info("Delete interface " + deletedIntf.getName());
179 if (deletedIntf.getTypeColumn().getData().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
180 deletedIntf.getTypeColumn().getData().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE) ||
181 phyIfName.contains(deletedIntf.getName())) {
182 /* delete tunnel interfaces or physical interfaces */
183 //this.handleInterfaceDelete(node, uuid, deletedIntf, false, null);
184 } else if (network != null && !network.getRouterExternal()) {
185 logger.debug("Processing update of {}:{} node {} intf {} network {}",
186 tableName, action, node, uuid, network.getNetworkUUID());
188 ConcurrentMap<String, Row> interfaces = this.ovsdbConfigurationService
189 .getRows(node, ovsdbConfigurationService.getTableName(node, Interface.class));
190 if (interfaces != null) {
191 boolean isLastInstanceOnNode = true;
192 for (String intfUUID : interfaces.keySet()) {
193 if (intfUUID.equals(uuid)) continue;
194 Interface intf = this.ovsdbConfigurationService.getTypedRow(node, Interface.class, interfaces.get(intfUUID));
195 NeutronNetwork neutronNetwork = tenantNetworkManager.getTenantNetwork(intf);
196 if (neutronNetwork != null && neutronNetwork.equals(network)) isLastInstanceOnNode = false;
198 //this.handleInterfaceDelete(node, uuid, deletedIntf, isLastInstanceOnNode, network);
200 } catch (Exception e) {
201 logger.error("Error fetching Interface Rows for node " + node, e);
206 else if (tableName.equalsIgnoreCase(ovsdbConfigurationService.getTableName(node, Interface.class))) {
207 logger.debug("Processing update of {}:{} node: {}, interface uuid: {}, row: {}",
208 tableName, action, node, uuid, row);
209 Interface intf = this.ovsdbConfigurationService.getTypedRow(node, Interface.class, row);
210 NeutronNetwork network = tenantNetworkManager.getTenantNetwork(intf);
211 if (network != null && !network.getRouterExternal()) {
212 if (networkingProviderManager.getProvider(node).hasPerTenantTunneling()) {
213 int vlan = tenantNetworkManager.networkCreated(node, network.getID());
214 String portUUID = this.getPortIdForInterface(node, uuid, intf);
215 if (portUUID != null) {
216 logger.debug("Neutron Network {}:{} Created with Internal vlan {} port {}",
217 network.getNetworkUUID(), network.getNetworkName(), vlan, portUUID);
218 tenantNetworkManager.programInternalVlan(node, portUUID, network);
220 logger.trace("Neutron Network {}:{} Created with Internal vlan {} but have no portUUID",
221 network.getNetworkUUID(), network.getNetworkName(), vlan);
224 //this.handleInterfaceUpdate(node, uuid, intf);
227 } else if (tableName.equalsIgnoreCase(ovsdbConfigurationService.getTableName(node, Port.class))) {
228 logger.debug("Processing update of {}:{} node: {}, port uuid: {}, row: {}", tableName, action, node, uuid, row);
229 Port port = this.ovsdbConfigurationService.getTypedRow(node, Port.class, row);
230 Set<UUID> interfaceUUIDs = port.getInterfacesColumn().getData();
231 for (UUID intfUUID : interfaceUUIDs) {
232 logger.trace("Scanning interface "+intfUUID);
234 Row intfRow = this.ovsdbConfigurationService
235 .getRow(node, ovsdbConfigurationService.getTableName(node, Interface.class),
236 intfUUID.toString());
237 Interface intf = this.ovsdbConfigurationService.getTypedRow(node, Interface.class, intfRow);
238 NeutronNetwork network = tenantNetworkManager.getTenantNetwork(intf);
239 if (network != null && !network.getRouterExternal()) {
240 logger.debug("Processing update of {}:{} node {} intf {} network {}",
241 tableName, action, node, intfUUID, network.getNetworkUUID());
242 tenantNetworkManager.programInternalVlan(node, uuid, network);
243 //this.handleInterfaceUpdate(node, intfUUID.toString(), intf);
245 logger.trace("Ignoring update because there is not a neutron network {} for port {}, interface {}",
246 network, uuid, intfUUID);
248 } catch (Exception e) {
249 logger.error("Failed to process row update", e);
252 } else if (tableName.equalsIgnoreCase(ovsdbConfigurationService.getTableName(node, OpenVSwitch.class))) {
253 logger.debug("Processing update of {}:{} node: {}, ovs uuid: {}, row: {}", tableName, action, node, uuid, row);
255 ConcurrentMap<String, Row> interfaces = this.ovsdbConfigurationService
256 .getRows(node, ovsdbConfigurationService.getTableName(node, Interface.class));
257 if (interfaces != null) {
258 for (String intfUUID : interfaces.keySet()) {
259 Interface intf = ovsdbConfigurationService.getTypedRow(node, Interface.class, interfaces.get(intfUUID));
260 //this.handleInterfaceUpdate(node, intfUUID, intf);
263 } catch (Exception e) {
264 logger.error("Error fetching Interface Rows for node " + node, e);
266 } else if (tableName.equalsIgnoreCase(ovsdbConfigurationService.getTableName(node, Bridge.class))) {
267 logger.debug("Processing update of {}:{} node: {}, bridge uuid: {}, row: {}", tableName, action, node, uuid, row);
268 Bridge bridge = ovsdbConfigurationService.getTypedRow(node, Bridge.class, row);
269 final Set<String> dpids = bridge.getDatapathIdColumn().getData();
271 (bridge.getName().equals(configurationService.getIntegrationBridgeName()) ||
272 bridge.getName().equals(configurationService.getExternalBridgeName()))) {
273 NetworkingProvider networkingProvider = networkingProviderManager.getProvider(node);
274 for (String dpid : dpids) {
276 // I don't think this is used anymore since adding NodeCacheManager to service dependency
277 //networkingProvider.notifyFlowCapableNodeEvent(StringConvertor.dpidStringToLong(dpid), action);
283 private void handleInterfaceUpdate (Node node, String portName) {
284 OvsdbTerminationPointAugmentation ovsdbTerminationPointAugmentation =
285 MdsalUtils.getTerminationPointAugmentation(node, portName);
286 logger.trace("Interface update of node: {}, portName: {}", node, portName);
287 NeutronNetwork network = tenantNetworkManager.getTenantNetwork(ovsdbTerminationPointAugmentation);
288 if (network != null) {
289 neutronL3Adapter.handleInterfaceEvent(node, ovsdbTerminationPointAugmentation, network, Action.UPDATE);
290 if (bridgeConfigurationManager.createLocalNetwork(node, network))
291 networkingProviderManager.getProvider(node).handleInterfaceUpdate(network, node,
292 ovsdbTerminationPointAugmentation);
294 logger.debug("No tenant network found on node: {}, portName: {} for interface: {}",
295 node, portName, ovsdbTerminationPointAugmentation);
299 private void handleInterfaceDelete (Node node, OvsdbTerminationPointAugmentation intf, boolean isLastInstanceOnNode,
300 NeutronNetwork network) {
301 logger.debug("handleInterfaceDelete: node: {}, isLastInstanceOnNode: {}, interface: {}",
302 node, isLastInstanceOnNode, intf);
304 neutronL3Adapter.handleInterfaceEvent(node, intf, network, Action.DELETE);
305 List<String> phyIfName = bridgeConfigurationManager.getAllPhysicalInterfaceNames(node);
306 if (isInterfaceOfInterest(intf, phyIfName)) {
307 /* delete tunnel or physical interfaces */
308 //networkingProviderManager.getProvider(node).handleInterfaceDelete(intf.getTypeColumn().getData(), null,
309 // node, intf, isLastInstanceOnNode);
310 } else if (network != null) {
311 if (!network.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN)) { /* vlan doesn't need a tunnel endpoint */
312 if (configurationService.getTunnelEndPoint(node) == null) {
313 logger.error("Tunnel end-point configuration missing. Please configure it in OpenVSwitch Table");
317 if (isLastInstanceOnNode & networkingProviderManager.getProvider(node).hasPerTenantTunneling()) {
318 tenantNetworkManager.reclaimInternalVlan(node, network);
320 networkingProviderManager.getProvider(node).handleInterfaceDelete(network.getProviderNetworkType(), network, node, intf, isLastInstanceOnNode);
324 private String getPortIdForInterface (Node node, String uuid, Interface intf) {
325 /* TODO SB_MIGRATION */
327 Map<String, Row> ports = this.ovsdbConfigurationService.getRows(node, ovsdbConfigurationService.getTableName(node, Port.class));
328 if (ports == null) return null;
329 for (String portUUID : ports.keySet()) {
330 Port port = ovsdbConfigurationService.getTypedRow(node, Port.class, ports.get(portUUID));
331 Set<UUID> interfaceUUIDs = port.getInterfacesColumn().getData();
332 logger.trace("Scanning Port {} to identify interface : {} ",port, uuid);
333 for (UUID intfUUID : interfaceUUIDs) {
334 if (intfUUID.toString().equalsIgnoreCase(uuid)) {
335 logger.trace("Found Interface {} -> {}", uuid, portUUID);
340 } catch (Exception e) {
341 logger.debug("Failed to get Port tag for for Intf " + intf, e);
346 private void triggerUpdates() {
347 /* TODO SB_MIGRATION */
348 List<Node> nodes = connectionService.getNodes();
349 if (nodes == null) return;
350 for (Node node : nodes) {
352 List<String> tableNames = ovsdbConfigurationService.getTables(node);
353 if (tableNames == null) continue;
354 for (String tableName : tableNames) {
355 Map<String, Row> rows = ovsdbConfigurationService.getRows(node, tableName);
356 if (rows == null) continue;
357 for (String uuid : rows.keySet()) {
358 Row row = rows.get(uuid);
359 this.rowAdded(node, tableName, uuid, row);
362 } catch (Exception e) {
363 logger.error("Exception during OVSDB Southbound update trigger", e);
368 private void processBridgeUpdate(Node node, OvsdbBridgeAugmentation bridge, Action action) {
369 logger.debug("processBridgeUpdate {}: {}, {}", action, node, bridge);
370 final String dpid = bridge.getDatapathId().getValue();
372 && (bridge.getBridgeName().equals(configurationService.getIntegrationBridgeName())
373 || bridge.getBridgeName().equals(configurationService.getExternalBridgeName()))) {
374 NetworkingProvider networkingProvider = networkingProviderManager.getProvider(node);
376 // I don't think this is used anymore since adding NodeCacheManager to service dependency
377 //networkingProvider.notifyFlowCapableNodeEvent(StringConvertor.dpidStringToLong(dpid), action);
381 private void processInterfaceDelete(Node node, String portName, Object context, Action action) {
382 OvsdbTerminationPointAugmentation ovsdbTerminationPointAugmentation =
383 MdsalUtils.getTerminationPointAugmentation(node, portName);
384 logger.debug("processInterfaceDelete {}: {}", node, portName);
385 NeutronNetwork network = null;
386 if (context == null) {
387 network = tenantNetworkManager.getTenantNetwork(ovsdbTerminationPointAugmentation);
389 network = (NeutronNetwork)context;
391 List<String> phyIfName = bridgeConfigurationManager.getAllPhysicalInterfaceNames(node);
392 if (isInterfaceOfInterest(ovsdbTerminationPointAugmentation, phyIfName)) {
393 this.handleInterfaceDelete(node, ovsdbTerminationPointAugmentation, false, null);
394 } else if (network != null && !network.getRouterExternal()) {
395 logger.debug("Network {} : Delete interface {} attached to bridge {}", network.getNetworkUUID(),
396 ovsdbTerminationPointAugmentation.getInterfaceUuid(), node);
398 OvsdbBridgeAugmentation ovsdbBridgeAugmentation = node.getAugmentation(OvsdbBridgeAugmentation.class);
399 if (ovsdbBridgeAugmentation != null) {
400 List<TerminationPoint> terminationPoints = node.getTerminationPoint();
401 if(!terminationPoints.isEmpty()){
402 boolean isLastInstanceOnNode = true;
403 for(TerminationPoint terminationPoint : terminationPoints) {
404 OvsdbTerminationPointAugmentation tpAugmentation =
405 terminationPoint.getAugmentation( OvsdbTerminationPointAugmentation.class);
406 if(tpAugmentation.getInterfaceUuid().equals(ovsdbTerminationPointAugmentation.getInterfaceUuid())) continue;
407 NeutronNetwork neutronNetwork = tenantNetworkManager.getTenantNetwork(tpAugmentation);
408 if (neutronNetwork != null && neutronNetwork.equals(network)) {
409 isLastInstanceOnNode = false;
413 this.handleInterfaceDelete(node, ovsdbTerminationPointAugmentation, isLastInstanceOnNode, network);
416 } catch (Exception e) {
417 logger.error("Error fetching Interface Rows for node " + node, e);
422 private void processInterfaceUpdate(Node node, OvsdbTerminationPointAugmentation terminationPoint,
423 String portName, Object context, Action action) {
424 if (action == Action.DELETE) {
425 processInterfaceDelete(node, portName, context, action);
431 private boolean isInterfaceOfInterest(OvsdbTerminationPointAugmentation terminationPoint, List<String> phyIfName) {
432 return (SouthboundMapper.createOvsdbInterfaceType(
433 terminationPoint.getInterfaceType()).equals(NetworkHandler.NETWORK_TYPE_VXLAN)
435 SouthboundMapper.createOvsdbInterfaceType(
436 terminationPoint.getInterfaceType()).equals(NetworkHandler.NETWORK_TYPE_GRE)
438 phyIfName.contains(terminationPoint.getName()));
442 * Notification about an OpenFlow Node
444 * @param openFlowNode the {@link Node Node} of interest in the notification
445 * @param action the {@link Action}
446 * @see NodeCacheListener#notifyNode
449 public void notifyNode (Node openFlowNode, Action action) {
450 logger.info("notifyNode: Node {} update {}", openFlowNode, action);
452 if (action.equals(Action.ADD)) {
453 networkingProviderManager.getProvider(openFlowNode).initializeOFFlowRules(openFlowNode);
460 * @param abstractEvent the {@link org.opendaylight.ovsdb.openstack.netvirt.AbstractEvent} event to be handled.
461 * @see EventDispatcher
464 public void processEvent(AbstractEvent abstractEvent) {
465 if (!(abstractEvent instanceof SouthboundEvent)) {
466 logger.error("Unable to process abstract event " + abstractEvent);
469 SouthboundEvent ev = (SouthboundEvent) abstractEvent;
470 logger.info("processEvent: {}", ev);
471 switch (ev.getType()) {
473 processOvsdbNodeUpdate(ev.getNode(), ev.getAction());
476 processBridgeUpdate(ev.getNode(), ev.getAction());
480 processPortUpdate(ev.getNode(), ev.getAction());
484 processOpenVSwitchUpdate(ev.getNode(), ev.getAction());
488 processRowUpdate(ev.getNode(), ev.getTableName(), ev.getUuid(), ev.getRow(),
489 ev.getContext(), ev.getAction());
490 } catch (Exception e) {
491 logger.error("Exception caught in ProcessRowUpdate for node " + ev.getNode(), e);
495 logger.warn("Unable to process type " + ev.getType() +
496 " action " + ev.getAction() + " for node " + ev.getNode());
501 private void processPortUpdate(Node node, Action action) {
505 processPortUpdate(node);
508 processPortDelete(node);
513 private void processPortDelete(Node node) {
516 private void processPortUpdate(Node node) {
519 private void processBridgeUpdate(Node node, Action action) {
522 private void processOpenVSwitchUpdate(Node node, Action action) {
523 //do the work that rowUpdate(table=openvswith) would have done