SouthboundHandler#triggerUpdates
[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 package org.opendaylight.ovsdb.openstack.netvirt;
9
10 import java.util.List;
11 import java.util.concurrent.ConcurrentMap;
12
13 import org.opendaylight.neutron.spi.NeutronNetwork;
14 import org.opendaylight.ovsdb.lib.notation.Row;
15 import org.opendaylight.ovsdb.openstack.netvirt.api.*;
16 import org.opendaylight.ovsdb.openstack.netvirt.impl.MdsalUtils;
17 import org.opendaylight.ovsdb.openstack.netvirt.impl.NeutronL3Adapter;
18 import org.opendaylight.ovsdb.schema.openvswitch.Interface;
19 import org.opendaylight.ovsdb.schema.openvswitch.OpenVSwitch;
20 import org.opendaylight.ovsdb.southbound.SouthboundMapper;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
23 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
24 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27
28 /**
29  * @author Madhu Venugopal
30  * @author Brent Salisbury
31  * @author Dave Tucker
32  * @author Sam Hague (shague@redhat.com)
33  */
34 public class SouthboundHandler extends AbstractHandler
35         implements NodeCacheListener, OvsdbInventoryListener {
36     static final Logger logger = LoggerFactory.getLogger(SouthboundHandler.class);
37
38     // The implementation for each of these services is resolved by the OSGi Service Manager
39     private volatile ConfigurationService configurationService;
40     private volatile BridgeConfigurationManager bridgeConfigurationManager;
41     private volatile TenantNetworkManager tenantNetworkManager;
42     private volatile NetworkingProviderManager networkingProviderManager;
43     private volatile OvsdbConfigurationService ovsdbConfigurationService;
44     private volatile OvsdbConnectionService connectionService;
45     private volatile NeutronL3Adapter neutronL3Adapter;
46
47     void start() {
48         this.triggerUpdates();
49     }
50
51     private SouthboundEvent.Type ovsdbTypeToSouthboundEventType(OvsdbType ovsdbType) {
52         SouthboundEvent.Type type = SouthboundEvent.Type.NODE;
53
54         switch (ovsdbType) {
55             case NODE:
56                 type = SouthboundEvent.Type.NODE;
57                 break;
58             case BRIDGE:
59                 type = SouthboundEvent.Type.BRIDGE;
60                 break;
61             case PORT:
62                 type = SouthboundEvent.Type.PORT;
63                 break;
64             case CONTROLLER:
65                 type = SouthboundEvent.Type.CONTROLLER;
66                 break;
67             case OPENVSWITCH:
68                 type = SouthboundEvent.Type.OPENVSWITCH;
69                 break;
70             default:
71                 logger.warn("Invalid OvsdbType: {}", ovsdbType);
72                 break;
73         }
74         return type;
75     }
76
77     @Override
78     public void ovsdbUpdate(Node node, OvsdbType ovsdbType, Action action) {
79         logger.info("ovsdbUpdate: {} - {} - {}", node, ovsdbType, action);
80         this.enqueueEvent(new SouthboundEvent(node, ovsdbTypeToSouthboundEventType(ovsdbType), action));
81     }
82
83     public void processOvsdbNodeUpdate(Node node, Action action) {
84         if (action == Action.ADD) {
85             logger.info("processOvsdbNodeUpdate {}", node);
86             bridgeConfigurationManager.prepareNode(node);
87         } else {
88             logger.info("Not implemented yet: {}", action);
89         }
90     }
91
92     private void processRowUpdate(Node node, String tableName, String uuid, Row row,
93                                   Object context, Action action) {
94         /* TODO SB_MIGRATION */
95         if (action == Action.DELETE) {
96             if (tableName.equalsIgnoreCase(ovsdbConfigurationService.getTableName(node, Interface.class))) {
97                 logger.debug("Processing update of {}. Deleted node: {}, uuid: {}, row: {}", tableName, node, uuid, row);
98                 Interface deletedIntf = ovsdbConfigurationService.getTypedRow(node, Interface.class, row);
99                 NeutronNetwork network = null;
100                 if (context == null) {
101                     network = tenantNetworkManager.getTenantNetwork(deletedIntf);
102                 } else {
103                     network = (NeutronNetwork)context;
104                 }
105                 List<String> phyIfName = bridgeConfigurationManager.getAllPhysicalInterfaceNames(node);
106                 logger.info("Delete interface " + deletedIntf.getName());
107
108                 if (deletedIntf.getTypeColumn().getData().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
109                     deletedIntf.getTypeColumn().getData().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE) ||
110                     phyIfName.contains(deletedIntf.getName())) {
111                     /* delete tunnel interfaces or physical interfaces */
112                     //this.handleInterfaceDelete(node, uuid, deletedIntf, false, null);
113                 } else if (network != null && !network.getRouterExternal()) {
114                     logger.debug("Processing update of {}:{} node {} intf {} network {}",
115                             tableName, action, node, uuid, network.getNetworkUUID());
116                     try {
117                         ConcurrentMap<String, Row> interfaces = this.ovsdbConfigurationService
118                                 .getRows(node, ovsdbConfigurationService.getTableName(node, Interface.class));
119                         if (interfaces != null) {
120                             boolean isLastInstanceOnNode = true;
121                             for (String intfUUID : interfaces.keySet()) {
122                                 if (intfUUID.equals(uuid)) continue;
123                                 Interface intf = this.ovsdbConfigurationService.getTypedRow(node, Interface.class, interfaces.get(intfUUID));
124                                 NeutronNetwork neutronNetwork = tenantNetworkManager.getTenantNetwork(intf);
125                                 if (neutronNetwork != null && neutronNetwork.equals(network)) isLastInstanceOnNode = false;
126                             }
127                             //this.handleInterfaceDelete(node, uuid, deletedIntf, isLastInstanceOnNode, network);
128                         }
129                     } catch (Exception e) {
130                         logger.error("Error fetching Interface Rows for node " + node, e);
131                     }
132                 }
133             }
134         } else if (tableName.equalsIgnoreCase(ovsdbConfigurationService.getTableName(node, OpenVSwitch.class))) {
135             logger.debug("Processing update of {}:{} node: {}, ovs uuid: {}, row: {}", tableName, action, node, uuid, row);
136             try {
137                 ConcurrentMap<String, Row> interfaces = this.ovsdbConfigurationService
138                         .getRows(node, ovsdbConfigurationService.getTableName(node, Interface.class));
139                 if (interfaces != null) {
140                     for (String intfUUID : interfaces.keySet()) {
141                         Interface intf = ovsdbConfigurationService.getTypedRow(node, Interface.class, interfaces.get(intfUUID));
142                         //this.handleInterfaceUpdate(node, intfUUID, intf);
143                     }
144                 }
145             } catch (Exception e) {
146                 logger.error("Error fetching Interface Rows for node " + node, e);
147             }
148         }
149     }
150
151     private void handleInterfaceUpdate (Node node, OvsdbTerminationPointAugmentation tp) {
152         logger.trace("handleInterfaceUpdate node: {}, tp: {}", node, tp);
153         NeutronNetwork network = tenantNetworkManager.getTenantNetwork(tp);
154         if (network != null && !network.getRouterExternal()) {
155             logger.trace("handleInterfaceUpdate node: {}, tp: {}, network: {}", node, tp, network.getNetworkUUID());
156             tenantNetworkManager.programInternalVlan(node, tp, network);
157             neutronL3Adapter.handleInterfaceEvent(node, tp, network, Action.UPDATE);
158             if (bridgeConfigurationManager.createLocalNetwork(node, network)) {
159                 networkingProviderManager.getProvider(node).handleInterfaceUpdate(network, node, tp);
160             }
161         } else {
162             logger.debug("No tenant network found on node: {} for interface: {}", node, tp);
163         }
164     }
165
166     private void handleInterfaceDelete (Node node, OvsdbTerminationPointAugmentation intf, boolean isLastInstanceOnNode,
167                                         NeutronNetwork network) {
168         logger.debug("handleInterfaceDelete: node: {}, isLastInstanceOnNode: {}, interface: {}",
169                 node, isLastInstanceOnNode, intf);
170
171         neutronL3Adapter.handleInterfaceEvent(node, intf, network, Action.DELETE);
172         List<String> phyIfName = bridgeConfigurationManager.getAllPhysicalInterfaceNames(node);
173         if (isInterfaceOfInterest(intf, phyIfName)) {
174             /* delete tunnel or physical interfaces */
175             //networkingProviderManager.getProvider(node).handleInterfaceDelete(intf.getTypeColumn().getData(), null,
176             //        node, intf, isLastInstanceOnNode);
177         } else if (network != null) {
178             if (!network.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN)) { /* vlan doesn't need a tunnel endpoint */
179                 if (configurationService.getTunnelEndPoint(node) == null) {
180                     logger.error("Tunnel end-point configuration missing. Please configure it in OpenVSwitch Table");
181                     return;
182                 }
183             }
184             if (isLastInstanceOnNode & networkingProviderManager.getProvider(node).hasPerTenantTunneling()) {
185                 tenantNetworkManager.reclaimInternalVlan(node, network);
186             }
187             networkingProviderManager.getProvider(node).handleInterfaceDelete(network.getProviderNetworkType(), network, node, intf, isLastInstanceOnNode);
188         }
189     }
190
191     private void triggerUpdates() {
192         List<Node> nodes = connectionService.getNodes();
193         if (nodes == null) return;
194         for (Node node : nodes) {
195             OvsdbBridgeAugmentation bridge = node.getAugmentation(OvsdbBridgeAugmentation.class);
196             if (bridge != null) {
197                 processBridgeUpdate(node, bridge);
198             }
199
200             List<TerminationPoint> tps = MdsalUtils.getTerminationPoints(node);
201             for (TerminationPoint tp : tps) {
202                 OvsdbTerminationPointAugmentation port = tp.getAugmentation(OvsdbTerminationPointAugmentation.class);
203                 if (port != null) {
204                     processPortUpdate(node, port);
205                 }
206             }
207         }
208     }
209
210     private void processInterfaceDelete(Node node, String portName, Object context, Action action) {
211         OvsdbTerminationPointAugmentation ovsdbTerminationPointAugmentation =
212                 MdsalUtils.getTerminationPointAugmentation(node, portName);
213         logger.debug("processInterfaceDelete {}: {}", node, portName);
214         NeutronNetwork network = null;
215         if (context == null) {
216             network = tenantNetworkManager.getTenantNetwork(ovsdbTerminationPointAugmentation);
217         } else {
218             network = (NeutronNetwork)context;
219         }
220         List<String> phyIfName = bridgeConfigurationManager.getAllPhysicalInterfaceNames(node);
221         if (isInterfaceOfInterest(ovsdbTerminationPointAugmentation, phyIfName)) {
222             this.handleInterfaceDelete(node, ovsdbTerminationPointAugmentation, false, null);
223         } else if (network != null && !network.getRouterExternal()) {
224             logger.debug("Network {} : Delete interface {} attached to bridge {}", network.getNetworkUUID(),
225                     ovsdbTerminationPointAugmentation.getInterfaceUuid(), node);
226             try {
227                 OvsdbBridgeAugmentation ovsdbBridgeAugmentation = node.getAugmentation(OvsdbBridgeAugmentation.class);
228                 if (ovsdbBridgeAugmentation != null) {
229                     List<TerminationPoint> terminationPoints = node.getTerminationPoint();
230                     if(!terminationPoints.isEmpty()){
231                         boolean isLastInstanceOnNode = true;
232                         for(TerminationPoint terminationPoint : terminationPoints) {
233                             OvsdbTerminationPointAugmentation tpAugmentation =
234                                     terminationPoint.getAugmentation( OvsdbTerminationPointAugmentation.class);
235                             if(tpAugmentation.getInterfaceUuid().equals(ovsdbTerminationPointAugmentation.getInterfaceUuid())) continue;
236                             NeutronNetwork neutronNetwork = tenantNetworkManager.getTenantNetwork(tpAugmentation);
237                             if (neutronNetwork != null && neutronNetwork.equals(network)) {
238                                 isLastInstanceOnNode = false;
239                                 break;
240                             }
241                         }
242                         this.handleInterfaceDelete(node, ovsdbTerminationPointAugmentation, isLastInstanceOnNode, network);
243                     }
244                 }
245             } catch (Exception e) {
246                 logger.error("Error fetching Interface Rows for node " + node, e);
247             }
248         }
249     }
250
251     private boolean isInterfaceOfInterest(OvsdbTerminationPointAugmentation terminationPoint, List<String> phyIfName) {
252         return (SouthboundMapper.createOvsdbInterfaceType(
253                 terminationPoint.getInterfaceType()).equals(NetworkHandler.NETWORK_TYPE_VXLAN)
254                 ||
255                 SouthboundMapper.createOvsdbInterfaceType(
256                         terminationPoint.getInterfaceType()).equals(NetworkHandler.NETWORK_TYPE_GRE)
257                 ||
258                 phyIfName.contains(terminationPoint.getName()));
259     }
260
261     /**
262      * Notification about an OpenFlow Node
263      *
264      * @param openFlowNode the {@link Node Node} of interest in the notification
265      * @param action the {@link Action}
266      * @see NodeCacheListener#notifyNode
267      */
268     @Override
269     public void notifyNode (Node openFlowNode, Action action) {
270         logger.info("notifyNode: Node {} update {}", openFlowNode, action);
271
272         if (action.equals(Action.ADD)) {
273             networkingProviderManager.getProvider(openFlowNode).initializeOFFlowRules(openFlowNode);
274         }
275     }
276
277     /**
278      * Process the event.
279      *
280      * @param abstractEvent the {@link org.opendaylight.ovsdb.openstack.netvirt.AbstractEvent} event to be handled.
281      * @see EventDispatcher
282      */
283     @Override
284     public void processEvent(AbstractEvent abstractEvent) {
285         if (!(abstractEvent instanceof SouthboundEvent)) {
286             logger.error("Unable to process abstract event " + abstractEvent);
287             return;
288         }
289         SouthboundEvent ev = (SouthboundEvent) abstractEvent;
290         logger.info("processEvent: {}", ev);
291         switch (ev.getType()) {
292             case NODE:
293                 processOvsdbNodeUpdate(ev.getNode(), ev.getAction());
294                 break;
295             case BRIDGE:
296                 processBridgeUpdate(ev.getNode(), ev.getAction());
297                 break;
298
299             case PORT:
300                 processPortUpdate(ev.getNode(), ev.getAction());
301                 break;
302
303             case OPENVSWITCH:
304                 processOpenVSwitchUpdate(ev.getNode(), ev.getAction());
305                 break;
306
307             case ROW:
308                 try {
309                     processRowUpdate(ev.getNode(), ev.getTableName(), ev.getUuid(), ev.getRow(),
310                             ev.getContext(), ev.getAction());
311                 } catch (Exception e) {
312                     logger.error("Exception caught in ProcessRowUpdate for node " + ev.getNode(), e);
313                 }
314                 break;
315             default:
316                 logger.warn("Unable to process type " + ev.getType() +
317                         " action " + ev.getAction() + " for node " + ev.getNode());
318                 break;
319         }
320     }
321
322     private void processPortUpdate(Node node, Action action) {
323         switch (action) {
324             case ADD:
325             case UPDATE:
326                 processPortUpdate(node);
327                 break;
328             case DELETE:
329                 processPortDelete(node);
330                 break;
331         }
332     }
333
334     private void processPortDelete(Node node) {
335     }
336
337     private void processPortUpdate(Node node) {
338         List<TerminationPoint> terminationPoints = MdsalUtils.getTerminationPoints(node);
339         for (TerminationPoint terminationPoint : terminationPoints) {
340             processPortUpdate(node, terminationPoint.getAugmentation(OvsdbTerminationPointAugmentation.class));
341         }
342     }
343
344     private void processOpenVSwitchUpdate(Node node, Action action) {
345         //do the work that rowUpdate(table=openvswith) would have done
346     }
347
348     private void processPortUpdate(Node node, OvsdbTerminationPointAugmentation port) {
349         logger.debug("processPortUpdate {} - {}", node, port);
350         NeutronNetwork network = tenantNetworkManager.getTenantNetwork(port);
351         if (network != null && !network.getRouterExternal()) {
352             this.handleInterfaceUpdate(node, port);
353         }
354
355     }
356
357     private void processBridgeUpdate(Node node, Action action) {
358         OvsdbBridgeAugmentation bridge = MdsalUtils.getBridge(node);
359         switch (action) {
360             case ADD:
361             case UPDATE:
362                 processBridgeUpdate(node, bridge);
363                 break;
364             case DELETE:
365                 processBridgeDelete(node, bridge);
366                 break;
367         }
368     }
369
370     private void processBridgeDelete(Node node, OvsdbBridgeAugmentation bridge) {
371         logger.debug("processBridgeUpdate {}, {}", node, bridge);
372     }
373
374     private void processBridgeUpdate(Node node, OvsdbBridgeAugmentation bridge) {
375         logger.debug("processBridgeUpdate {}, {}", node, bridge);
376     }
377 }