Merge "Added Vagrant images to run CSIT."
[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
12 import org.opendaylight.neutron.spi.NeutronNetwork;
13 import org.opendaylight.ovsdb.openstack.netvirt.api.*;
14 import org.opendaylight.ovsdb.openstack.netvirt.impl.NeutronL3Adapter;
15 import org.opendaylight.ovsdb.utils.servicehelper.ServiceHelper;
16 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
17 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
19 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
20 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
21 import org.opendaylight.yangtools.yang.binding.DataObject;
22 import org.osgi.framework.BundleContext;
23 import org.osgi.framework.ServiceReference;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 /**
28  * @author Madhu Venugopal
29  * @author Brent Salisbury
30  * @author Dave Tucker
31  * @author Sam Hague (shague@redhat.com)
32  */
33 public class SouthboundHandler extends AbstractHandler
34         implements ConfigInterface, NodeCacheListener, OvsdbInventoryListener {
35     static final Logger logger = LoggerFactory.getLogger(SouthboundHandler.class);
36
37     // The implementation for each of these services is resolved by the OSGi Service Manager
38     private volatile ConfigurationService configurationService;
39     private volatile BridgeConfigurationManager bridgeConfigurationManager;
40     private volatile TenantNetworkManager tenantNetworkManager;
41     private volatile NetworkingProviderManager networkingProviderManager;
42     private volatile NeutronL3Adapter neutronL3Adapter;
43     private volatile NodeCacheManager nodeCacheManager;
44     private volatile OvsdbInventoryService ovsdbInventoryService;
45     private volatile Southbound southbound;
46
47     private SouthboundEvent.Type ovsdbTypeToSouthboundEventType(OvsdbType ovsdbType) {
48         SouthboundEvent.Type type = SouthboundEvent.Type.NODE;
49
50         switch (ovsdbType) {
51             case NODE:
52                 type = SouthboundEvent.Type.NODE;
53                 break;
54             case BRIDGE:
55                 type = SouthboundEvent.Type.BRIDGE;
56                 break;
57             case PORT:
58                 type = SouthboundEvent.Type.PORT;
59                 break;
60             case CONTROLLER:
61                 type = SouthboundEvent.Type.CONTROLLER;
62                 break;
63             case OPENVSWITCH:
64                 type = SouthboundEvent.Type.OPENVSWITCH;
65                 break;
66             default:
67                 logger.warn("Invalid OvsdbType: {}", ovsdbType);
68                 break;
69         }
70         return type;
71     }
72
73     @Override
74     public void ovsdbUpdate(Node node, DataObject resourceAugmentationData, OvsdbType ovsdbType, Action action) {
75         logger.info("ovsdbUpdate: {} - {} - <<{}>> <<{}>>", ovsdbType, action, node, resourceAugmentationData);
76         enqueueEvent(new SouthboundEvent(node, resourceAugmentationData,
77                 ovsdbTypeToSouthboundEventType(ovsdbType), action));
78     }
79
80     private void handleInterfaceUpdate (Node node, OvsdbTerminationPointAugmentation tp) {
81         logger.debug("handleInterfaceUpdate <{}> <{}>", node, tp);
82         NeutronNetwork network = tenantNetworkManager.getTenantNetwork(tp);
83         if (network != null && !network.getRouterExternal()) {
84             logger.trace("handleInterfaceUpdate <{}> <{}> network: {}", node, tp, network.getNetworkUUID());
85             if (bridgeConfigurationManager.createLocalNetwork(node, network)) {
86                 networkingProviderManager.getProvider(node).handleInterfaceUpdate(network, node, tp);
87             }
88         } else {
89             logger.debug("No tenant network found on node: <{}> for interface: <{}>", node, tp);
90         }
91         neutronL3Adapter.handleInterfaceEvent(node, tp, network, Action.UPDATE);
92     }
93
94     private void handleInterfaceDelete (Node node, OvsdbTerminationPointAugmentation intf,
95                                         boolean isLastInstanceOnNode, NeutronNetwork network) {
96         logger.debug("handleInterfaceDelete: node: <{}>, isLastInstanceOnNode: {}, interface: <{}>",
97                 node, isLastInstanceOnNode, intf);
98
99         neutronL3Adapter.handleInterfaceEvent(node, intf, network, Action.DELETE);
100         List<String> phyIfName = bridgeConfigurationManager.getAllPhysicalInterfaceNames(node);
101         if (isInterfaceOfInterest(intf, phyIfName)) {
102             // delete tunnel or physical interfaces
103             networkingProviderManager.getProvider(node).handleInterfaceDelete(network.getProviderNetworkType(),
104                     network, node, intf, isLastInstanceOnNode);
105         } else if (network != null) {
106             // vlan doesn't need a tunnel endpoint
107             if (!network.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN)) {
108                 if (configurationService.getTunnelEndPoint(node) == null) {
109                     logger.error("Tunnel end-point configuration missing. Please configure it in OpenVSwitch Table");
110                     return;
111                 }
112             }
113             networkingProviderManager.getProvider(node).handleInterfaceDelete(network.getProviderNetworkType(),
114                     network, node, intf, isLastInstanceOnNode);
115         }
116     }
117
118     @Override
119     public void triggerUpdates() {
120         logger.info("triggerUpdates");
121         List<Node> ovsdbNodes = southbound.readOvsdbTopologyNodes();
122         for (Node node : ovsdbNodes) {
123             ovsdbUpdate(node, node.getAugmentation(OvsdbNodeAugmentation.class),
124                     OvsdbInventoryListener.OvsdbType.NODE, Action.ADD);
125         }
126     }
127
128     private void processPortDelete(Node node, OvsdbTerminationPointAugmentation ovsdbTerminationPointAugmentation,
129                                    Object context) {
130         logger.debug("processPortDelete <{}> <{}>", node, ovsdbTerminationPointAugmentation);
131         NeutronNetwork network = null;
132         if (context == null) {
133             network = tenantNetworkManager.getTenantNetwork(ovsdbTerminationPointAugmentation);
134         } else {
135             network = (NeutronNetwork)context;
136         }
137         List<String> phyIfName = bridgeConfigurationManager.getAllPhysicalInterfaceNames(node);
138         if (isInterfaceOfInterest(ovsdbTerminationPointAugmentation, phyIfName)) {
139             if (network != null) {
140                 this.handleInterfaceDelete(node, ovsdbTerminationPointAugmentation, false, network);
141             } else {
142                 logger.warn("processPortDelete: network was null, ignoring update");
143             }
144         } else if (network != null && !network.getRouterExternal()) {
145             logger.debug("Network {}: Delete interface {} attached to bridge {}", network.getNetworkUUID(),
146                     ovsdbTerminationPointAugmentation.getInterfaceUuid(), node.getNodeId());
147             try {
148                 OvsdbBridgeAugmentation ovsdbBridgeAugmentation = southbound.getBridge(node);
149                 if (ovsdbBridgeAugmentation != null) {
150                     List<TerminationPoint> terminationPoints = node.getTerminationPoint();
151                     if (!terminationPoints.isEmpty()){
152                         boolean isLastInstanceOnNode = true;
153                         for (TerminationPoint terminationPoint : terminationPoints) {
154                             OvsdbTerminationPointAugmentation tpAugmentation =
155                                     terminationPoint.getAugmentation( OvsdbTerminationPointAugmentation.class);
156                             if (tpAugmentation.getInterfaceUuid().equals(
157                                     ovsdbTerminationPointAugmentation.getInterfaceUuid())) {
158                                 continue;
159                             }
160                             NeutronNetwork neutronNetwork = tenantNetworkManager.getTenantNetwork(tpAugmentation);
161                             if (neutronNetwork != null && neutronNetwork.equals(network)) {
162                                 isLastInstanceOnNode = false;
163                                 break;
164                             }
165                         }
166                         this.handleInterfaceDelete(node, ovsdbTerminationPointAugmentation,
167                                 isLastInstanceOnNode, network);
168                     }
169                 }
170             } catch (Exception e) {
171                 logger.error("Error fetching Interface Rows for node " + node, e);
172             }
173         }
174     }
175
176     private boolean isInterfaceOfInterest(OvsdbTerminationPointAugmentation terminationPoint, List<String> phyIfName) {
177         logger.trace("SouthboundHandler#isInterfaceOfInterest: Interface : {}", terminationPoint);
178
179         if(terminationPoint.getInterfaceType() == null){
180             // This is OK since eth ports don't have an interface type
181             logger.info("No type found for the interface : {}", terminationPoint);
182             return false;
183         }
184         return (MdsalHelper.createOvsdbInterfaceType(
185                 terminationPoint.getInterfaceType()).equals(NetworkHandler.NETWORK_TYPE_VXLAN)
186                 ||
187                 MdsalHelper.createOvsdbInterfaceType(
188                         terminationPoint.getInterfaceType()).equals(NetworkHandler.NETWORK_TYPE_GRE)
189                 ||
190                 phyIfName.contains(terminationPoint.getName()));
191     }
192
193     /**
194      * Notification about an OpenFlow Node
195      *
196      * @param node the {@link Node Node} of interest in the notification
197      * @param action the {@link Action}
198      * @see NodeCacheListener#notifyNode
199      */
200     @Override
201     public void notifyNode (Node node, Action action) {
202         logger.info("notifyNode: action: {}, Node <{}>", action, node);
203
204         if (action.equals(Action.ADD)) {
205             if (southbound.getBridge(node) != null) {
206                 networkingProviderManager.getProvider(node).initializeOFFlowRules(node);
207             }
208         }
209     }
210
211     /**
212      * Process the event.
213      *
214      * @param abstractEvent the {@link org.opendaylight.ovsdb.openstack.netvirt.AbstractEvent} event to be handled.
215      * @see org.opendaylight.ovsdb.openstack.netvirt.api.EventDispatcher
216      */
217     @Override
218     public void processEvent(AbstractEvent abstractEvent) {
219         if (!(abstractEvent instanceof SouthboundEvent)) {
220             logger.error("processEvent: Unable to process abstract event {}", abstractEvent);
221             return;
222         }
223         SouthboundEvent ev = (SouthboundEvent) abstractEvent;
224         logger.trace("processEvent: {}", ev);
225         switch (ev.getType()) {
226             case NODE:
227                 processOvsdbNodeEvent(ev);
228                 break;
229
230             case BRIDGE:
231                 processBridgeEvent(ev);
232                 break;
233
234             case PORT:
235                 processPortEvent(ev);
236                 break;
237
238             case OPENVSWITCH:
239                 processOpenVSwitchEvent(ev);
240                 break;
241
242             default:
243                 logger.warn("Unable to process type " + ev.getType() +
244                         " action " + ev.getAction() + " for node " + ev.getNode());
245                 break;
246         }
247     }
248
249     private void processOvsdbNodeEvent(SouthboundEvent ev) {
250         switch (ev.getAction()) {
251             case ADD:
252                 processOvsdbNodeCreate(ev.getNode(), (OvsdbNodeAugmentation) ev.getAugmentationData());
253                 break;
254             case UPDATE:
255                 processOvsdbNodeUpdate(ev.getNode(), (OvsdbNodeAugmentation) ev.getAugmentationData());
256                 break;
257             case DELETE:
258                 processOvsdbNodeDelete(ev.getNode(), (OvsdbNodeAugmentation) ev.getAugmentationData());
259                 break;
260         }
261     }
262
263     private void processOvsdbNodeCreate(Node node, OvsdbNodeAugmentation ovsdbNode) {
264         logger.info("processOvsdbNodeCreate <{}> <{}>", node, ovsdbNode);
265         nodeCacheManager.nodeAdded(node);
266         bridgeConfigurationManager.prepareNode(node);
267     }
268
269     private void processOvsdbNodeUpdate(Node node, OvsdbNodeAugmentation ovsdbNode) {
270         logger.info("processOvsdbNodeUpdate <{}> <{}>", node, ovsdbNode);
271         nodeCacheManager.nodeAdded(node);
272     }
273
274     private void processOvsdbNodeDelete(Node node, OvsdbNodeAugmentation ovsdbNode) {
275         logger.info("processOvsdbNodeDelete <{}> <{}>", node, ovsdbNode);
276         nodeCacheManager.nodeRemoved(node);
277         /* TODO SB_MIGRATION
278         * I don't think we want to do this yet
279         InstanceIdentifier<Node> bridgeNodeIid =
280                 MdsalHelper.createInstanceIdentifier(ovsdbNode.getConnectionInfo(),
281                         Constants.INTEGRATION_BRIDGE);
282         southbound.delete(LogicalDatastoreType.CONFIGURATION, bridgeNodeIid);
283         */
284     }
285
286     private void processPortEvent(SouthboundEvent ev) {
287         switch (ev.getAction()) {
288             case ADD:
289             case UPDATE:
290                 processPortUpdate(ev.getNode(), (OvsdbTerminationPointAugmentation) ev.getAugmentationData());
291                 break;
292             case DELETE:
293                 processPortDelete(ev.getNode(), (OvsdbTerminationPointAugmentation) ev.getAugmentationData(), null);
294                 break;
295         }
296     }
297
298     private void processPortUpdate(Node node, OvsdbTerminationPointAugmentation port) {
299         logger.debug("processPortUpdate <{}> <{}>", node, port);
300         NeutronNetwork network = tenantNetworkManager.getTenantNetwork(port);
301         if (network != null && !network.getRouterExternal()) {
302             this.handleInterfaceUpdate(node, port);
303         }
304     }
305
306     private void processOpenVSwitchEvent(SouthboundEvent ev) {
307         switch (ev.getAction()) {
308             case ADD:
309             case UPDATE:
310                 processOpenVSwitchUpdate(ev.getNode());
311                 break;
312             case DELETE:
313                 break;
314         }
315     }
316
317     private void processOpenVSwitchUpdate(Node node) {
318         logger.debug("processOpenVSwitchUpdate {}", node);
319         // TODO this node might be the OvsdbNode and not have termination points
320         // Would need to change listener or grab tp nodes in here.
321         List<TerminationPoint> terminationPoints = southbound.extractTerminationPoints(node);
322         for (TerminationPoint terminationPoint : terminationPoints) {
323             processPortUpdate(node, terminationPoint.getAugmentation(OvsdbTerminationPointAugmentation.class));
324         }
325     }
326
327     private void processBridgeEvent(SouthboundEvent ev) {
328         switch (ev.getAction()) {
329             case ADD:
330                 processBridgeCreate(ev.getNode(), (OvsdbBridgeAugmentation) ev.getAugmentationData());
331                 break;
332             case UPDATE:
333                 processBridgeUpdate(ev.getNode(), (OvsdbBridgeAugmentation) ev.getAugmentationData());
334                 break;
335             case DELETE:
336                 processBridgeDelete(ev.getNode(), (OvsdbBridgeAugmentation) ev.getAugmentationData());
337                 break;
338         }
339     }
340
341     private boolean isMainBridge(Node node, OvsdbBridgeAugmentation bridge) {
342         boolean rv = false;
343         String nodeIdStr = node.getNodeId().getValue();
344         String bridgeName = nodeIdStr.substring(nodeIdStr.lastIndexOf('/') + 1);
345         List<TerminationPoint> terminationPoints = southbound.extractTerminationPoints(node);
346         if (terminationPoints != null && terminationPoints.size() == 1) {
347         }
348         OvsdbTerminationPointAugmentation port = southbound.extractTerminationPointAugmentation(node, bridgeName);
349         if (port != null) {
350             String datapathId = southbound.getDatapathId(bridge);
351             // Having a datapathId means the ovsdb node has connected to ODL
352             if (datapathId != null) {
353                 rv = true;
354             } else {
355                 logger.info("datapathId not found");
356             }
357         }
358         return rv;
359     }
360
361     private void processBridgeCreate(Node node, OvsdbBridgeAugmentation bridge) {
362         logger.debug("processBridgeCreate <{}> <{}>", node, bridge);
363         String datapathId = southbound.getDatapathId(bridge);
364         // Having a datapathId means the ovsdb node has connected to ODL
365         if (datapathId != null) {
366             nodeCacheManager.nodeAdded(node);
367         } else {
368             logger.info("processBridgeCreate datapathId not found");
369         }
370     }
371
372     private void processBridgeUpdate(Node node, OvsdbBridgeAugmentation bridge) {
373         logger.debug("processBridgeUpdate <{}> <{}>", node, bridge);
374         String datapathId = southbound.getDatapathId(bridge);
375         // Having a datapathId means the ovsdb node has connected to ODL
376         if (datapathId != null) {
377             nodeCacheManager.nodeAdded(node);
378         } else {
379             logger.info("processBridgeUpdate datapathId not found");
380         }
381     }
382
383     private void processBridgeDelete(Node node, OvsdbBridgeAugmentation bridge) {
384         logger.debug("processBridgeDelete: Delete bridge from config data store: <{}> <{}>",
385                 node, bridge);
386         nodeCacheManager.nodeRemoved(node);
387         // TODO SB_MIGRATION
388         // Not sure if we want to do this yet
389         southbound.deleteBridge(node);
390     }
391
392     @Override
393     public void setDependencies(BundleContext bundleContext, ServiceReference serviceReference) {
394         configurationService =
395                 (ConfigurationService) ServiceHelper.getGlobalInstance(ConfigurationService.class, this);
396         networkingProviderManager =
397                 (NetworkingProviderManager) ServiceHelper.getGlobalInstance(NetworkingProviderManager.class, this);
398         tenantNetworkManager =
399                 (TenantNetworkManager) ServiceHelper.getGlobalInstance(TenantNetworkManager.class, this);
400         bridgeConfigurationManager =
401                 (BridgeConfigurationManager) ServiceHelper.getGlobalInstance(BridgeConfigurationManager.class, this);
402         nodeCacheManager =
403                 (NodeCacheManager) ServiceHelper.getGlobalInstance(NodeCacheManager.class, this);
404         nodeCacheManager.cacheListenerAdded(
405                 bundleContext.getServiceReference(OvsdbInventoryListener.class.getName()), this);
406         neutronL3Adapter =
407                 (NeutronL3Adapter) ServiceHelper.getGlobalInstance(NeutronL3Adapter.class, this);
408         southbound =
409                 (Southbound) ServiceHelper.getGlobalInstance(Southbound.class, this);
410         eventDispatcher =
411                 (EventDispatcher) ServiceHelper.getGlobalInstance(EventDispatcher.class, this);
412         eventDispatcher.eventHandlerAdded(
413                 bundleContext.getServiceReference(OvsdbInventoryListener.class.getName()), this);
414         ovsdbInventoryService =
415                 (OvsdbInventoryService) ServiceHelper.getGlobalInstance(OvsdbInventoryService.class, this);
416         ovsdbInventoryService.listenerAdded(this);
417     }
418
419     @Override
420     public void setDependencies(Object impl) {}
421 }