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 * Authors : Madhu Venugopal, Brent Salisbury, Sam Hague, Dave Tucker
10 package org.opendaylight.ovsdb.openstack.netvirt;
14 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.NeutronL3Adapter;
20 import org.opendaylight.ovsdb.schema.openvswitch.Bridge;
21 import org.opendaylight.ovsdb.schema.openvswitch.Interface;
22 import org.opendaylight.ovsdb.schema.openvswitch.OpenVSwitch;
23 import org.opendaylight.ovsdb.schema.openvswitch.Port;
24 import org.opendaylight.ovsdb.utils.mdsal.node.StringConvertor;
25 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
30 import java.util.List;
32 public class SouthboundHandler extends AbstractHandler
33 implements NodeCacheListener, OvsdbInventoryListener {
34 static final Logger logger = LoggerFactory.getLogger(SouthboundHandler.class);
36 // The implementation for each of these services is resolved by the OSGi Service Manager
37 private volatile ConfigurationService configurationService;
38 private volatile BridgeConfigurationManager bridgeConfigurationManager;
39 private volatile TenantNetworkManager tenantNetworkManager;
40 private volatile NetworkingProviderManager networkingProviderManager;
41 private volatile OvsdbConfigurationService ovsdbConfigurationService;
42 private volatile OvsdbConnectionService connectionService;
43 private volatile MdsalConsumer mdsalConsumer; // TODO SB_MIGRATION
44 private volatile NeutronL3Adapter neutronL3Adapter;
47 logger.info(">>>>> init");
51 logger.info(">>>>> started");
52 this.triggerUpdates(); // TODO SB_MIGRATION
56 public void ovsdbNodeAdded(Node node) {
57 logger.info("nodeAdded: {}", node);
58 this.enqueueEvent(new SouthboundEvent(node, Action.ADD));
62 public void ovsdbNodeRemoved(Node node) {
63 this.enqueueEvent(new SouthboundEvent(node, Action.DELETE));
67 public void rowAdded(Node node, String tableName, String uuid, Row row) {
68 this.enqueueEvent(new SouthboundEvent(node, tableName, uuid, row, Action.ADD));
72 public void rowUpdated(Node node, String tableName, String uuid, Row oldRow, Row newRow) {
73 if (this.isUpdateOfInterest(node, oldRow, newRow)) {
74 this.enqueueEvent(new SouthboundEvent(node, tableName, uuid, newRow, Action.UPDATE));
79 * Ignore unnecessary updates to be even considered for processing.
80 * (Especially stats update are fast and furious).
83 private boolean isUpdateOfInterest(Node node, Row oldRow, Row newRow) {
84 /* TODO SB_MIGRATION */
85 if (oldRow == null) return true;
86 if (newRow.getTableSchema().getName().equals(ovsdbConfigurationService.getTableName(node, Interface.class))) {
87 // We are NOT interested in Stats only updates
88 Interface oldIntf = ovsdbConfigurationService.getTypedRow(node, Interface.class, oldRow);
89 if (oldIntf.getName() == null && oldIntf.getExternalIdsColumn() == null && oldIntf.getMacColumn() == null &&
90 oldIntf.getOpenFlowPortColumn() == null && oldIntf.getOptionsColumn() == null && oldIntf.getOtherConfigColumn() == null &&
91 oldIntf.getTypeColumn() == null) {
92 logger.trace("IGNORING Interface Update: node {}, row: {}", node, newRow);
95 } else if (newRow.getTableSchema().getName().equals(ovsdbConfigurationService.getTableName(node, Port.class))) {
96 // We are NOT interested in Stats only updates
97 Port oldPort = ovsdbConfigurationService.getTypedRow(node, Port.class, oldRow);
98 if (oldPort.getName() == null && oldPort.getExternalIdsColumn() == null && oldPort.getMacColumn() == null &&
99 oldPort.getInterfacesColumn() == null && oldPort.getTagColumn() == null && oldPort.getTrunksColumn() == null) {
100 logger.trace("IGNORING Port Update: node {}, row: {}", node, newRow);
103 } else if (newRow.getTableSchema().getName().equals(ovsdbConfigurationService.getTableName(node, OpenVSwitch.class))) {
104 OpenVSwitch oldOpenvSwitch = ovsdbConfigurationService.getTypedRow(node, OpenVSwitch.class, oldRow);
105 if (oldOpenvSwitch.getOtherConfigColumn()== null) {
106 // we are only interested in other_config field change
114 public void rowRemoved(Node node, String tableName, String uuid, Row row, Object context) {
115 this.enqueueEvent(new SouthboundEvent(node, tableName, uuid, row, context, Action.DELETE));
118 public void processNodeUpdate(Node node, Action action) {
119 if (action == Action.DELETE) return;
120 logger.trace("Process Node added {}", node);
121 bridgeConfigurationManager.prepareNode(node);
124 private void processRowUpdate(Node node, String tableName, String uuid, Row row,
125 Object context, Action action) {
126 /* TODO SB_MIGRATION */
127 if (action == Action.DELETE) {
128 if (tableName.equalsIgnoreCase(ovsdbConfigurationService.getTableName(node, Interface.class))) {
129 logger.debug("Processing update of {}. Deleted node: {}, uuid: {}, row: {}", tableName, node, uuid, row);
130 Interface deletedIntf = ovsdbConfigurationService.getTypedRow(node, Interface.class, row);
131 NeutronNetwork network = null;
132 if (context == null) {
133 network = tenantNetworkManager.getTenantNetwork(deletedIntf);
135 network = (NeutronNetwork)context;
137 List<String> phyIfName = bridgeConfigurationManager.getAllPhysicalInterfaceNames(node);
138 logger.info("Delete interface " + deletedIntf.getName());
140 if (deletedIntf.getTypeColumn().getData().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
141 deletedIntf.getTypeColumn().getData().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE) ||
142 phyIfName.contains(deletedIntf.getName())) {
143 /* delete tunnel interfaces or physical interfaces */
144 this.handleInterfaceDelete(node, uuid, deletedIntf, false, null);
145 } else if (network != null && !network.getRouterExternal()) {
146 logger.debug("Processing update of {}:{} node {} intf {} network {}",
147 tableName, action, node, uuid, network.getNetworkUUID());
149 ConcurrentMap<String, Row> interfaces = this.ovsdbConfigurationService
150 .getRows(node, ovsdbConfigurationService.getTableName(node, Interface.class));
151 if (interfaces != null) {
152 boolean isLastInstanceOnNode = true;
153 for (String intfUUID : interfaces.keySet()) {
154 if (intfUUID.equals(uuid)) continue;
155 Interface intf = this.ovsdbConfigurationService.getTypedRow(node, Interface.class, interfaces.get(intfUUID));
156 NeutronNetwork neutronNetwork = tenantNetworkManager.getTenantNetwork(intf);
157 if (neutronNetwork != null && neutronNetwork.equals(network)) isLastInstanceOnNode = false;
159 this.handleInterfaceDelete(node, uuid, deletedIntf, isLastInstanceOnNode, network);
161 } catch (Exception e) {
162 logger.error("Error fetching Interface Rows for node " + node, e);
167 else if (tableName.equalsIgnoreCase(ovsdbConfigurationService.getTableName(node, Interface.class))) {
168 logger.debug("Processing update of {}:{} node: {}, interface uuid: {}, row: {}",
169 tableName, action, node, uuid, row);
170 Interface intf = this.ovsdbConfigurationService.getTypedRow(node, Interface.class, row);
171 NeutronNetwork network = tenantNetworkManager.getTenantNetwork(intf);
172 if (network != null && !network.getRouterExternal()) {
173 if (networkingProviderManager.getProvider(node).hasPerTenantTunneling()) {
174 int vlan = tenantNetworkManager.networkCreated(node, network.getID());
175 String portUUID = this.getPortIdForInterface(node, uuid, intf);
176 if (portUUID != null) {
177 logger.debug("Neutron Network {}:{} Created with Internal vlan {} port {}",
178 network.getNetworkUUID(), network.getNetworkName(), vlan, portUUID);
179 tenantNetworkManager.programInternalVlan(node, portUUID, network);
181 logger.trace("Neutron Network {}:{} Created with Internal vlan {} but have no portUUID",
182 network.getNetworkUUID(), network.getNetworkName(), vlan);
185 this.handleInterfaceUpdate(node, uuid, intf);
188 } else if (tableName.equalsIgnoreCase(ovsdbConfigurationService.getTableName(node, Port.class))) {
189 logger.debug("Processing update of {}:{} node: {}, port uuid: {}, row: {}", tableName, action, node, uuid, row);
190 Port port = this.ovsdbConfigurationService.getTypedRow(node, Port.class, row);
191 Set<UUID> interfaceUUIDs = port.getInterfacesColumn().getData();
192 for (UUID intfUUID : interfaceUUIDs) {
193 logger.trace("Scanning interface "+intfUUID);
195 Row intfRow = this.ovsdbConfigurationService
196 .getRow(node, ovsdbConfigurationService.getTableName(node, Interface.class),
197 intfUUID.toString());
198 Interface intf = this.ovsdbConfigurationService.getTypedRow(node, Interface.class, intfRow);
199 NeutronNetwork network = tenantNetworkManager.getTenantNetwork(intf);
200 if (network != null && !network.getRouterExternal()) {
201 logger.debug("Processing update of {}:{} node {} intf {} network {}",
202 tableName, action, node, intfUUID, network.getNetworkUUID());
203 tenantNetworkManager.programInternalVlan(node, uuid, network);
204 this.handleInterfaceUpdate(node, intfUUID.toString(), intf);
206 logger.trace("Ignoring update because there is not a neutron network {} for port {}, interface {}",
207 network, uuid, intfUUID);
209 } catch (Exception e) {
210 logger.error("Failed to process row update", e);
213 } else if (tableName.equalsIgnoreCase(ovsdbConfigurationService.getTableName(node, OpenVSwitch.class))) {
214 logger.debug("Processing update of {}:{} node: {}, ovs uuid: {}, row: {}", tableName, action, node, uuid, row);
216 ConcurrentMap<String, Row> interfaces = this.ovsdbConfigurationService
217 .getRows(node, ovsdbConfigurationService.getTableName(node, Interface.class));
218 if (interfaces != null) {
219 for (String intfUUID : interfaces.keySet()) {
220 Interface intf = ovsdbConfigurationService.getTypedRow(node, Interface.class, interfaces.get(intfUUID));
221 this.handleInterfaceUpdate(node, intfUUID, intf);
224 } catch (Exception e) {
225 logger.error("Error fetching Interface Rows for node " + node, e);
227 } else if (tableName.equalsIgnoreCase(ovsdbConfigurationService.getTableName(node, Bridge.class))) {
228 logger.debug("Processing update of {}:{} node: {}, bridge uuid: {}, row: {}", tableName, action, node, uuid, row);
229 Bridge bridge = ovsdbConfigurationService.getTypedRow(node, Bridge.class, row);
230 final Set<String> dpids = bridge.getDatapathIdColumn().getData();
232 (bridge.getName().equals(configurationService.getIntegrationBridgeName()) ||
233 bridge.getName().equals(configurationService.getExternalBridgeName()))) {
234 NetworkingProvider networkingProvider = networkingProviderManager.getProvider(node);
235 for (String dpid : dpids) {
236 networkingProvider.notifyFlowCapableNodeEvent(StringConvertor.dpidStringToLong(dpid), action);
242 private void handleInterfaceUpdate (Node node, String uuid, Interface intf) {
243 logger.trace("Interface update of node: {}, uuid: {}", node, uuid);
244 NeutronNetwork network = tenantNetworkManager.getTenantNetwork(intf);
245 if (network != null) {
246 neutronL3Adapter.handleInterfaceEvent(node, intf, network, Action.UPDATE);
247 if (bridgeConfigurationManager.createLocalNetwork(node, network))
248 networkingProviderManager.getProvider(node).handleInterfaceUpdate(network, node, intf);
250 logger.debug("No tenant network found on node: {}, uuid: {} for interface: {}", node, uuid, intf);
254 private void handleInterfaceDelete (Node node, String uuid, Interface intf, boolean isLastInstanceOnNode,
255 NeutronNetwork network) {
256 logger.debug("handleInterfaceDelete: node: {}, uuid: {}, isLastInstanceOnNode: {}, interface: {}",
257 node, uuid, isLastInstanceOnNode, intf);
259 neutronL3Adapter.handleInterfaceEvent(node, intf, network, Action.DELETE);
260 List<String> phyIfName = bridgeConfigurationManager.getAllPhysicalInterfaceNames(node);
261 if (intf.getTypeColumn().getData().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
262 intf.getTypeColumn().getData().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE) ||
263 phyIfName.contains(intf.getName())) {
264 /* delete tunnel or physical interfaces */
265 networkingProviderManager.getProvider(node).handleInterfaceDelete(intf.getTypeColumn().getData(), null, node, intf, isLastInstanceOnNode);
266 } else if (network != null) {
267 if (!network.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN)) { /* vlan doesn't need a tunnel endpoint */
268 if (configurationService.getTunnelEndPoint(node) == null) {
269 logger.error("Tunnel end-point configuration missing. Please configure it in OpenVSwitch Table");
273 if (isLastInstanceOnNode & networkingProviderManager.getProvider(node).hasPerTenantTunneling()) {
274 tenantNetworkManager.reclaimInternalVlan(node, uuid, network);
276 networkingProviderManager.getProvider(node).handleInterfaceDelete(network.getProviderNetworkType(), network, node, intf, isLastInstanceOnNode);
280 private String getPortIdForInterface (Node node, String uuid, Interface intf) {
281 /* TODO SB_MIGRATION */
283 Map<String, Row> ports = this.ovsdbConfigurationService.getRows(node, ovsdbConfigurationService.getTableName(node, Port.class));
284 if (ports == null) return null;
285 for (String portUUID : ports.keySet()) {
286 Port port = ovsdbConfigurationService.getTypedRow(node, Port.class, ports.get(portUUID));
287 Set<UUID> interfaceUUIDs = port.getInterfacesColumn().getData();
288 logger.trace("Scanning Port {} to identify interface : {} ",port, uuid);
289 for (UUID intfUUID : interfaceUUIDs) {
290 if (intfUUID.toString().equalsIgnoreCase(uuid)) {
291 logger.trace("Found Interface {} -> {}", uuid, portUUID);
296 } catch (Exception e) {
297 logger.debug("Failed to get Port tag for for Intf " + intf, e);
302 private void triggerUpdates() {
303 /* TODO SB_MIGRATION */
304 List<Node> nodes = connectionService.getNodes();
305 if (nodes == null) return;
306 for (Node node : nodes) {
308 List<String> tableNames = ovsdbConfigurationService.getTables(node);
309 if (tableNames == null) continue;
310 for (String tableName : tableNames) {
311 Map<String, Row> rows = ovsdbConfigurationService.getRows(node, tableName);
312 if (rows == null) continue;
313 for (String uuid : rows.keySet()) {
314 Row row = rows.get(uuid);
315 this.rowAdded(node, tableName, uuid, row);
318 } catch (Exception e) {
319 logger.error("Exception during OVSDB Southbound update trigger", e);
327 * @param abstractEvent the {@link org.opendaylight.ovsdb.openstack.netvirt.AbstractEvent} event to be handled.
328 * @see EventDispatcher
331 public void processEvent(AbstractEvent abstractEvent) {
332 if (!(abstractEvent instanceof SouthboundEvent)) {
333 logger.error("Unable to process abstract event " + abstractEvent);
336 SouthboundEvent ev = (SouthboundEvent) abstractEvent;
337 //logger.info("processEvent: {}", ev);
338 switch (ev.getType()) {
341 processNodeUpdate(ev.getNode(), ev.getAction());
342 } catch (Exception e) {
343 logger.error("Exception caught in ProcessNodeUpdate for node " + ev.getNode(), e);
348 processRowUpdate(ev.getNode(), ev.getTableName(), ev.getUuid(), ev.getRow(),
349 ev.getContext(),ev.getAction());
350 } catch (Exception e) {
351 logger.error("Exception caught in ProcessRowUpdate for node " + ev.getNode(), e);
355 logger.warn("Unable to process type " + ev.getType() +
356 " action " + ev.getAction() + " for node " + ev.getNode());
362 * Notification about an OpenFlow Node
364 * @param openFlowNode the {@link Node Node} of interest in the notification
365 * @param action the {@link Action}
366 * @see NodeCacheListener#notifyNode
369 public void notifyNode (org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node openFlowNode,
371 logger.info("notifyNode: Node {} update {} from Controller's inventory Service",
372 openFlowNode, action);
374 if (action.equals(Action.ADD)) {
376 * Need to map from ovsdbNode to openflowNode
378 //networkingProviderManager.getProvider(ovsdbNode).initializeOFFlowRules(openFlowNode);