Use Topology Node in place of Inventory Node
[ovsdb.git] / openstack / net-virt / src / main / java / org / opendaylight / ovsdb / openstack / netvirt / SouthboundHandler.java
1 /*
2  * Copyright (C) 2013 Red Hat, Inc.
3  *
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
7  *
8  * Authors : Madhu Venugopal, Brent Salisbury, Sam Hague, Dave Tucker
9  */
10 package org.opendaylight.ovsdb.openstack.netvirt;
11
12 import java.util.Map;
13 import java.util.Set;
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;
26
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 import java.util.List;
31
32 public class SouthboundHandler extends AbstractHandler
33         implements NodeCacheListener, OvsdbInventoryListener {
34     static final Logger logger = LoggerFactory.getLogger(SouthboundHandler.class);
35
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;
45
46     void init() {
47         logger.info(">>>>> init");
48     }
49
50     void start() {
51         logger.info(">>>>> started");
52         this.triggerUpdates(); // TODO SB_MIGRATION
53     }
54
55     @Override
56     public void ovsdbNodeAdded(Node node) {
57         logger.info("nodeAdded: {}", node);
58         this.enqueueEvent(new SouthboundEvent(node, Action.ADD));
59     }
60
61     @Override
62     public void ovsdbNodeRemoved(Node node) {
63         this.enqueueEvent(new SouthboundEvent(node, Action.DELETE));
64     }
65
66     @Override
67     public void rowAdded(Node node, String tableName, String uuid, Row row) {
68         this.enqueueEvent(new SouthboundEvent(node, tableName, uuid, row, Action.ADD));
69     }
70
71     @Override
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));
75         }
76     }
77
78     /*
79      * Ignore unnecessary updates to be even considered for processing.
80      * (Especially stats update are fast and furious).
81      */
82
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);
93                 return false;
94             }
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);
101                 return false;
102             }
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
107                 return false;
108             }
109         }
110         return true;
111     }
112
113     @Override
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));
116     }
117
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);
122     }
123
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);
134                 } else {
135                     network = (NeutronNetwork)context;
136                 }
137                 List<String> phyIfName = bridgeConfigurationManager.getAllPhysicalInterfaceNames(node);
138                 logger.info("Delete interface " + deletedIntf.getName());
139
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());
148                     try {
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;
158                             }
159                             this.handleInterfaceDelete(node, uuid, deletedIntf, isLastInstanceOnNode, network);
160                         }
161                     } catch (Exception e) {
162                         logger.error("Error fetching Interface Rows for node " + node, e);
163                     }
164                 }
165             }
166         }
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);
180                     } else {
181                         logger.trace("Neutron Network {}:{} Created with Internal vlan {} but have no portUUID",
182                                  network.getNetworkUUID(), network.getNetworkName(), vlan);
183                     }
184                 }
185                 this.handleInterfaceUpdate(node, uuid, intf);
186             }
187
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);
194                 try {
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);
205                     } else {
206                         logger.trace("Ignoring update because there is not a neutron network {} for port {}, interface {}",
207                                 network, uuid, intfUUID);
208                     }
209                 } catch (Exception e) {
210                     logger.error("Failed to process row update", e);
211                 }
212             }
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);
215             try {
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);
222                     }
223                 }
224             } catch (Exception e) {
225                 logger.error("Error fetching Interface Rows for node " + node, e);
226             }
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();
231             if (dpids != null &&
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);
237                 }
238             }
239         }
240     }
241
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);
249         } else {
250             logger.debug("No tenant network found on node: {}, uuid: {} for interface: {}", node, uuid, intf);
251         }
252     }
253
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);
258
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");
270                     return;
271                 }
272             }
273             if (isLastInstanceOnNode & networkingProviderManager.getProvider(node).hasPerTenantTunneling()) {
274                 tenantNetworkManager.reclaimInternalVlan(node, uuid, network);
275             }
276             networkingProviderManager.getProvider(node).handleInterfaceDelete(network.getProviderNetworkType(), network, node, intf, isLastInstanceOnNode);
277         }
278     }
279
280     private String getPortIdForInterface (Node node, String uuid, Interface intf) {
281         /* TODO SB_MIGRATION */
282         try {
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);
292                         return portUUID;
293                     }
294                 }
295             }
296         } catch (Exception e) {
297             logger.debug("Failed to get Port tag for for Intf " + intf, e);
298         }
299         return null;
300     }
301
302     private void triggerUpdates() {
303         /* TODO SB_MIGRATION */
304         List<Node> nodes = connectionService.getNodes();
305         if (nodes == null) return;
306         for (Node node : nodes) {
307             try {
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);
316                     }
317                 }
318             } catch (Exception e) {
319                 logger.error("Exception during OVSDB Southbound update trigger", e);
320             }
321         }
322     }
323
324     /**
325      * Process the event.
326      *
327      * @param abstractEvent the {@link org.opendaylight.ovsdb.openstack.netvirt.AbstractEvent} event to be handled.
328      * @see EventDispatcher
329      */
330     @Override
331     public void processEvent(AbstractEvent abstractEvent) {
332         if (!(abstractEvent instanceof SouthboundEvent)) {
333             logger.error("Unable to process abstract event " + abstractEvent);
334             return;
335         }
336         SouthboundEvent ev = (SouthboundEvent) abstractEvent;
337         //logger.info("processEvent: {}", ev);
338         switch (ev.getType()) {
339             case NODE:
340                 try {
341                     processNodeUpdate(ev.getNode(), ev.getAction());
342                 } catch (Exception e) {
343                     logger.error("Exception caught in ProcessNodeUpdate for node " + ev.getNode(), e);
344                 }
345                 break;
346             case ROW:
347                 try {
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);
352                 }
353                 break;
354             default:
355                 logger.warn("Unable to process type " + ev.getType() +
356                             " action " + ev.getAction() + " for node " + ev.getNode());
357                 break;
358         }
359     }
360
361     /**
362      * Notification about an OpenFlow Node
363      *
364      * @param openFlowNode the {@link Node Node} of interest in the notification
365      * @param action the {@link Action}
366      * @see NodeCacheListener#notifyNode
367      */
368     @Override
369     public void notifyNode (org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node openFlowNode,
370                             Action action) {
371         logger.info("notifyNode: Node {} update {} from Controller's inventory Service",
372                 openFlowNode, action);
373
374         if (action.equals(Action.ADD)) {
375             /* TODO SB_MIGRATION
376              * Need to map from ovsdbNode to openflowNode
377              */
378             //networkingProviderManager.getProvider(ovsdbNode).initializeOFFlowRules(openFlowNode);
379         }
380     }
381 }