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
10 package org.opendaylight.ovsdb.neutron;
12 import java.util.ArrayList;
13 import java.util.List;
16 import java.util.concurrent.BlockingQueue;
17 import java.util.concurrent.ConcurrentMap;
18 import java.util.concurrent.ExecutorService;
19 import java.util.concurrent.Executors;
20 import java.util.concurrent.LinkedBlockingQueue;
22 import org.opendaylight.controller.networkconfig.neutron.NeutronNetwork;
23 import org.opendaylight.controller.sal.core.Node;
24 import org.opendaylight.controller.sal.core.NodeConnector;
25 import org.opendaylight.controller.sal.core.Property;
26 import org.opendaylight.controller.sal.core.UpdateType;
27 import org.opendaylight.controller.switchmanager.IInventoryListener;
28 import org.opendaylight.ovsdb.lib.notation.UUID;
29 import org.opendaylight.ovsdb.lib.table.Interface;
30 import org.opendaylight.ovsdb.lib.table.Open_vSwitch;
31 import org.opendaylight.ovsdb.lib.table.Port;
32 import org.opendaylight.ovsdb.lib.table.internal.Table;
33 import org.opendaylight.ovsdb.neutron.provider.ProviderNetworkManager;
34 import org.opendaylight.ovsdb.plugin.OVSDBInventoryListener;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
38 public class SouthboundHandler extends BaseHandler implements OVSDBInventoryListener, IInventoryListener {
39 static final Logger logger = LoggerFactory.getLogger(SouthboundHandler.class);
40 //private Thread eventThread;
41 private ExecutorService eventHandler;
42 private BlockingQueue<SouthboundEvent> events;
46 eventHandler = Executors.newSingleThreadExecutor();
47 this.events = new LinkedBlockingQueue<SouthboundEvent>();
48 nodeCache = new ArrayList<>();
52 eventHandler.submit(new Runnable() {
59 } catch (InterruptedException e) {
60 logger.info("The event handler thread was interrupted, shutting down", e);
63 switch (ev.getType()) {
66 processNodeUpdate(ev.getNode(), ev.getAction());
67 } catch (Exception e) {
68 logger.error("Exception caught in ProcessNodeUpdate for node " + ev.getNode(), e);
73 processRowUpdate(ev.getNode(), ev.getTableName(), ev.getUuid(), ev.getRow(), ev.getAction());
74 } catch (Exception e) {
75 logger.error("Exception caught in ProcessRowUpdate for node " + ev.getNode(), e);
79 logger.warn("Unable to process action " + ev.getAction() + " for node " + ev.getNode());
84 this.triggerUpdates();
88 eventHandler.shutdownNow();
92 public void nodeAdded(Node node) {
93 this.enqueueEvent(new SouthboundEvent(node, SouthboundEvent.Action.ADD));
97 public void nodeRemoved(Node node) {
98 this.enqueueEvent(new SouthboundEvent(node, SouthboundEvent.Action.DELETE));
102 public void rowAdded(Node node, String tableName, String uuid, Table<?> row) {
103 this.enqueueEvent(new SouthboundEvent(node, tableName, uuid, row, SouthboundEvent.Action.ADD));
107 public void rowUpdated(Node node, String tableName, String uuid, Table<?> oldRow, Table<?> newRow) {
108 if (this.isUpdateOfInterest(oldRow, newRow)) {
109 this.enqueueEvent(new SouthboundEvent(node, tableName, uuid, newRow, SouthboundEvent.Action.UPDATE));
114 * Ignore unneccesary updates to be even considered for processing.
115 * (Especially stats update are fast and furious).
118 private boolean isUpdateOfInterest(Table<?> oldRow, Table<?> newRow) {
119 if (oldRow == null) return true;
120 if (newRow.getTableName().equals(Interface.NAME)) {
121 // We are NOT interested in Stats only updates
122 Interface oldIntf = (Interface)oldRow;
123 if (oldIntf.getName() == null && oldIntf.getExternal_ids() == null && oldIntf.getMac() == null &&
124 oldIntf.getOfport() == null && oldIntf.getOptions() == null && oldIntf.getOther_config() == null &&
125 oldIntf.getType() == null) {
126 logger.trace("IGNORING Interface Update : "+newRow.toString());
129 } else if (newRow.getTableName().equals(Port.NAME)) {
130 // We are NOT interested in Stats only updates
131 Port oldPort = (Port)oldRow;
132 if (oldPort.getName() == null && oldPort.getExternal_ids() == null && oldPort.getMac() == null &&
133 oldPort.getInterfaces() == null && oldPort.getTag() == null && oldPort.getTrunks() == null) {
134 logger.trace("IGNORING Port Update : "+newRow.toString());
143 public void rowRemoved(Node node, String tableName, String uuid, Table<?> row) {
144 this.enqueueEvent(new SouthboundEvent(node, tableName, uuid, row, SouthboundEvent.Action.DELETE));
147 private void enqueueEvent (SouthboundEvent event) {
150 } catch (InterruptedException e) {
151 logger.error("Thread was interrupted while trying to enqueue event ", e);
156 public void processNodeUpdate(Node node, SouthboundEvent.Action action) {
157 if (action == SouthboundEvent.Action.DELETE) return;
158 logger.trace("Process Node added {}", node);
159 InternalNetworkManager.getManager().prepareInternalNetwork(node);
162 private void processRowUpdate(Node node, String tableName, String uuid, Table<?> row,
163 SouthboundEvent.Action action) {
164 if (action == SouthboundEvent.Action.DELETE) {
165 if (Interface.NAME.getName().equalsIgnoreCase(tableName)) {
166 Interface deletedIntf = (Interface)row;
167 NeutronNetwork network = TenantNetworkManager.getManager().getTenantNetworkForInterface(deletedIntf);
168 if (network != null && !network.getRouterExternal()) {
170 ConcurrentMap<String, Table<?>> interfaces = this.ovsdbConfigService.getRows(node, Interface.NAME.getName());
171 if (interfaces != null) {
172 boolean isLastInstanceOnNode = true;
173 for (String intfUUID : interfaces.keySet()) {
174 if (intfUUID.equals(uuid)) continue;
175 Interface intf = (Interface) interfaces.get(intfUUID);
176 NeutronNetwork neutronNetwork = TenantNetworkManager.getManager().getTenantNetworkForInterface(intf);
177 if (neutronNetwork != null && neutronNetwork.equals(network)) isLastInstanceOnNode = false;
179 this.handleInterfaceDelete(node, uuid, deletedIntf, isLastInstanceOnNode);
181 } catch (Exception e) {
182 logger.error("Error fetching Interface Rows for node " + node, e);
187 else if (Interface.NAME.getName().equalsIgnoreCase(tableName)) {
188 logger.debug("{} Added / Updated {} , {}, {}", tableName, node, uuid, row);
189 Interface intf = (Interface)row;
190 NeutronNetwork network = TenantNetworkManager.getManager().getTenantNetworkForInterface(intf);
191 if (network != null && !network.getRouterExternal()) {
192 if (ProviderNetworkManager.getManager().hasPerTenantTunneling()) {
193 int vlan = TenantNetworkManager.getManager().networkCreated(node, network.getID());
194 logger.trace("Neutron Network {} Created with Internal Vlan : {}", network.toString(), vlan);
196 String portUUID = this.getPortIdForInterface(node, uuid, intf);
197 if (portUUID != null) {
198 TenantNetworkManager.getManager().programTenantNetworkInternalVlan(node, portUUID, network);
201 this.handleInterfaceUpdate(node, uuid, intf);
203 } else if (Port.NAME.getName().equalsIgnoreCase(tableName)) {
204 logger.debug("{} Added / Updated {} , {}, {}", tableName, node, uuid, row);
205 Port port = (Port)row;
206 Set<UUID> interfaceUUIDs = port.getInterfaces();
207 for (UUID intfUUID : interfaceUUIDs) {
208 logger.trace("Scanning interface "+intfUUID);
210 Interface intf = (Interface)this.ovsdbConfigService.getRow(node, Interface.NAME.getName(), intfUUID.toString());
211 NeutronNetwork network = TenantNetworkManager.getManager().getTenantNetworkForInterface(intf);
212 if (network != null && !network.getRouterExternal()) {
213 TenantNetworkManager.getManager().programTenantNetworkInternalVlan(node, uuid, network);
215 } catch (Exception e) {
216 logger.error("Failed to process row update", e);
219 } else if (Open_vSwitch.NAME.getName().equalsIgnoreCase(tableName)) {
220 logger.debug("{} Added / Updated {} , {}, {}", tableName, node, uuid, row);
222 ConcurrentMap<String, Table<?>> interfaces = this.ovsdbConfigService.getRows(node, Interface.NAME.getName());
223 if (interfaces != null) {
224 for (String intfUUID : interfaces.keySet()) {
225 Interface intf = (Interface) interfaces.get(intfUUID);
226 this.handleInterfaceUpdate(node, intfUUID, intf);
229 } catch (Exception e) {
230 logger.error("Error fetching Interface Rows for node " + node, e);
235 private void handleInterfaceUpdate (Node node, String uuid, Interface intf) {
236 if (AdminConfigManager.getManager().getTunnelEndPoint(node) == null) {
237 logger.error("Tunnel end-point configuration missing. Please configure it in Open_vSwitch Table");
240 NeutronNetwork network = TenantNetworkManager.getManager().getTenantNetworkForInterface(intf);
241 if (network != null) {
242 ProviderNetworkManager.getManager().handleInterfaceUpdate(network.getProviderNetworkType(),
243 network.getProviderSegmentationID(), node, intf);
246 private void handleInterfaceDelete (Node node, String uuid, Interface intf, boolean isLastInstanceOnNode) {
247 if (AdminConfigManager.getManager().getTunnelEndPoint(node) == null) {
248 logger.error("Tunnel end-point configuration missing. Please configure it in Open_vSwitch Table");
251 NeutronNetwork network = TenantNetworkManager.getManager().getTenantNetworkForInterface(intf);
252 if (network != null) {
253 if (isLastInstanceOnNode) {
254 TenantNetworkManager.getManager().reclaimTennantNetworkInternalVlan(node, uuid, network);
256 ProviderNetworkManager.getManager().handleInterfaceDelete(network.getProviderNetworkType(),
257 network.getProviderSegmentationID(), node, intf, isLastInstanceOnNode);
261 private String getPortIdForInterface (Node node, String uuid, Interface intf) {
263 Map<String, Table<?>> ports = this.ovsdbConfigService.getRows(node, Port.NAME.getName());
264 if (ports == null) return null;
265 for (String portUUID : ports.keySet()) {
266 Port port = (Port)ports.get(portUUID);
267 Set<UUID> interfaceUUIDs = port.getInterfaces();
268 logger.trace("Scanning Port {} to identify interface : {} ",port, uuid);
269 for (UUID intfUUID : interfaceUUIDs) {
270 if (intfUUID.toString().equalsIgnoreCase(uuid)) {
271 logger.trace("Found Interafce {} -> {}", uuid, portUUID);
276 } catch (Exception e) {
277 logger.debug("Failed to add Port tag for for Intf {}",intf, e);
283 public void notifyNode(Node node, UpdateType type, Map<String, Property> propMap) {
284 logger.debug("Node {} update {} from Controller's inventory Service", node, type);
286 // Add the Node Type check back once the Consistency issue is resolved between MD-SAL and AD-SAL
287 if (!type.equals(UpdateType.REMOVED) && !nodeCache.contains(node)) {
289 ProviderNetworkManager.getManager().initializeOFFlowRules(node);
290 } else if (type.equals(UpdateType.REMOVED)){
291 nodeCache.remove(node);
296 public void notifyNodeConnector(NodeConnector nodeConnector, UpdateType type, Map<String, Property> propMap) {
297 //We are not interested in the nodeConnectors at this moment
300 private void triggerUpdates() {
301 List<Node> nodes = this.getConnectionService().getNodes();
302 if (nodes == null) return;
303 for (Node node : nodes) {
305 List<String> tableNames = this.getOVSDBConfigService().getTables(node);
306 if (tableNames == null) continue;
307 for (String tableName : tableNames) {
308 Map<String, Table<?>> rows = this.getOVSDBConfigService().getRows(node, tableName);
309 if (rows == null) continue;
310 for (String uuid : rows.keySet()) {
311 Table<?> row = rows.get(uuid);
312 this.rowAdded(node, tableName, uuid, row);
315 } catch (Exception e) {
316 logger.error("Exception during OVSDB Southbound update trigger", e);