0e3832a65af431441b85baef7d749657aea5f778
[netvirt.git] / openstack / net-virt / src / main / java / org / opendaylight / ovsdb / openstack / netvirt / SouthboundHandler.java
1 /*
2  * Copyright (c) 2013, 2015 Red Hat, Inc. and others. All rights reserved.
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
9 package org.opendaylight.ovsdb.openstack.netvirt;
10
11 import java.util.List;
12
13 import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronNetwork;
14 import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronPort;
15 import org.opendaylight.ovsdb.openstack.netvirt.api.*;
16 import org.opendaylight.ovsdb.openstack.netvirt.impl.NeutronL3Adapter;
17 import org.opendaylight.ovsdb.utils.servicehelper.ServiceHelper;
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
21 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
22 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
23 import org.opendaylight.yangtools.yang.binding.DataObject;
24 import org.osgi.framework.ServiceReference;
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 ConfigInterface, NodeCacheListener, OvsdbInventoryListener {
36     private static final Logger LOG = 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 NeutronL3Adapter neutronL3Adapter;
44     private volatile NodeCacheManager nodeCacheManager;
45     private volatile OvsdbInventoryService ovsdbInventoryService;
46     private volatile Southbound southbound;
47
48     private SouthboundEvent.Type ovsdbTypeToSouthboundEventType(OvsdbType ovsdbType) {
49         SouthboundEvent.Type type = SouthboundEvent.Type.NODE;
50
51         switch (ovsdbType) {
52             case NODE:
53                 type = SouthboundEvent.Type.NODE;
54                 break;
55             case BRIDGE:
56                 type = SouthboundEvent.Type.BRIDGE;
57                 break;
58             case PORT:
59                 type = SouthboundEvent.Type.PORT;
60                 break;
61             case CONTROLLER:
62                 type = SouthboundEvent.Type.CONTROLLER;
63                 break;
64             case OPENVSWITCH:
65                 type = SouthboundEvent.Type.OPENVSWITCH;
66                 break;
67             default:
68                 LOG.warn("Invalid OvsdbType: {}", ovsdbType);
69                 break;
70         }
71         return type;
72     }
73
74     @Override
75     public void ovsdbUpdate(Node node, DataObject resourceAugmentationData, OvsdbType ovsdbType, Action action) {
76         LOG.info("ovsdbUpdate: {} - {} - <<{}>> <<{}>>", ovsdbType, action, node, resourceAugmentationData);
77         enqueueEvent(new SouthboundEvent(node, resourceAugmentationData,
78                 ovsdbTypeToSouthboundEventType(ovsdbType), action));
79     }
80
81     private void handleInterfaceUpdate (Node node, OvsdbTerminationPointAugmentation tp) {
82         LOG.debug("handleInterfaceUpdate <{}> <{}>", node, tp);
83         NeutronNetwork network = tenantNetworkManager.getTenantNetwork(tp);
84         if (network != null && !network.getRouterExternal()) {
85             LOG.trace("handleInterfaceUpdate <{}> <{}> network: {}", node, tp, network.getNetworkUUID());
86             if (bridgeConfigurationManager.createLocalNetwork(node, network)) {
87                 networkingProviderManager.getProvider(node).handleInterfaceUpdate(network, node, tp);
88             }
89         } else {
90             LOG.debug("No tenant network found on node: <{}> for interface: <{}>", node, tp);
91         }
92         neutronL3Adapter.handleInterfaceEvent(node, tp, network, Action.UPDATE);
93     }
94
95     private void handleInterfaceDelete (Node node, OvsdbTerminationPointAugmentation intf,
96                                         boolean isLastInstanceOnNode, NeutronNetwork network) {
97         LOG.debug("handleInterfaceDelete: node: <{}>, isLastInstanceOnNode: {}, interface: <{}>",
98                 node, isLastInstanceOnNode, intf);
99
100         neutronL3Adapter.handleInterfaceEvent(node, intf, network, Action.DELETE);
101         List<String> phyIfName = bridgeConfigurationManager.getAllPhysicalInterfaceNames(node);
102         if (isInterfaceOfInterest(intf, phyIfName)) {
103             // delete tunnel or physical interfaces
104             networkingProviderManager.getProvider(node).handleInterfaceDelete(network.getProviderNetworkType(),
105                     network, node, intf, isLastInstanceOnNode);
106         } else if (network != null) {
107             // vlan doesn't need a tunnel endpoint
108             if (!network.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN) &&
109                     configurationService.getTunnelEndPoint(node) == null) {
110                 LOG.error("Tunnel end-point configuration missing. Please configure it in OpenVSwitch Table");
111                 return;
112             }
113             networkingProviderManager.getProvider(node).handleInterfaceDelete(network.getProviderNetworkType(),
114                     network, node, intf, isLastInstanceOnNode);
115         }
116     }
117
118     @Override
119     public void triggerUpdates() {
120         LOG.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         LOG.debug("processPortDelete <{}> <{}>", node, ovsdbTerminationPointAugmentation);
131         NeutronNetwork network;
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                 LOG.warn("processPortDelete: network was null, ignoring update");
143             }
144         } else if (network != null && !network.getRouterExternal()) {
145             LOG.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                 LOG.error("Error fetching Interface Rows for node {}", node, e);
172             }
173         }
174         //remove neutronPort from the CleanupCache, if it has the entry.
175         NeutronPort neutronPort = null;
176         String neutronPortId = southbound.getInterfaceExternalIdsValue(ovsdbTerminationPointAugmentation,
177                 Constants.EXTERNAL_ID_INTERFACE_ID);
178         if (neutronPortId != null) {
179             LOG.trace("Clean up the NeutronPortCache for {} ", neutronPortId);
180             neutronPort = neutronL3Adapter.getPortFromCleanupCache(neutronPortId);
181         }
182         if (neutronPort != null) {
183             LOG.debug("Clean up the NeutronPortCache ");
184             neutronL3Adapter.removePortFromCleanupCache(neutronPort);
185         } else {
186             LOG.trace("Nothing to Clean up in the NeutronPortCache ");
187         }
188
189     }
190
191     private boolean isInterfaceOfInterest(OvsdbTerminationPointAugmentation terminationPoint, List<String> phyIfName) {
192         LOG.trace("SouthboundHandler#isInterfaceOfInterest: Interface : {}", terminationPoint);
193
194         if(terminationPoint.getInterfaceType() == null){
195             // This is OK since eth ports don't have an interface type
196             LOG.info("No type found for the interface : {}", terminationPoint);
197             return false;
198         }
199         return MdsalHelper.createOvsdbInterfaceType(
200                 terminationPoint.getInterfaceType()).equals(NetworkHandler.NETWORK_TYPE_VXLAN)
201                ||
202                MdsalHelper.createOvsdbInterfaceType(
203                        terminationPoint.getInterfaceType()).equals(NetworkHandler.NETWORK_TYPE_GRE)
204                ||
205                phyIfName.contains(terminationPoint.getName());
206     }
207
208     /**
209      * Notification about an OpenFlow Node
210      *
211      * @param node the {@link Node Node} of interest in the notification
212      * @param action the {@link Action}
213      * @see NodeCacheListener#notifyNode
214      */
215     @Override
216     public void notifyNode (Node node, Action action) {
217         LOG.info("notifyNode: action: {}, Node <{}>", action, node);
218
219         if ((action.equals(Action.ADD)) && (southbound.getBridge(node) != null)) {
220             networkingProviderManager.getProvider(node).initializeOFFlowRules(node);
221         }
222     }
223
224     /**
225      * Process the event.
226      *
227      * @param abstractEvent the {@link org.opendaylight.ovsdb.openstack.netvirt.AbstractEvent} event to be handled.
228      * @see org.opendaylight.ovsdb.openstack.netvirt.api.EventDispatcher
229      */
230     @Override
231     public void processEvent(AbstractEvent abstractEvent) {
232         if (!(abstractEvent instanceof SouthboundEvent)) {
233             LOG.error("processEvent: Unable to process abstract event {}", abstractEvent);
234             return;
235         }
236         SouthboundEvent ev = (SouthboundEvent) abstractEvent;
237         LOG.trace("processEvent ({}): {}", ev, ev.getTransactionId());
238         switch (ev.getType()) {
239             case NODE:
240                 processOvsdbNodeEvent(ev);
241                 break;
242
243             case BRIDGE:
244                 processBridgeEvent(ev);
245                 break;
246
247             case PORT:
248                 processPortEvent(ev);
249                 break;
250
251             case OPENVSWITCH:
252                 processOpenVSwitchEvent(ev);
253                 break;
254
255             default:
256                 LOG.warn("Unable to process type {} action {} for node {}", ev.getType(), ev.getAction(), ev.getNode());
257                 break;
258         }
259         LOG.trace("processEvent exit ({}): {}", ev, ev.getTransactionId());
260     }
261
262     private void processOvsdbNodeEvent(SouthboundEvent ev) {
263         switch (ev.getAction()) {
264             case ADD:
265                 processOvsdbNodeCreate(ev.getNode(), (OvsdbNodeAugmentation) ev.getAugmentationData());
266                 break;
267             case UPDATE:
268                 processOvsdbNodeUpdate(ev.getNode(), (OvsdbNodeAugmentation) ev.getAugmentationData());
269                 break;
270             case DELETE:
271                 processOvsdbNodeDelete(ev.getNode(), (OvsdbNodeAugmentation) ev.getAugmentationData());
272                 break;
273         }
274     }
275
276     private void processOvsdbNodeCreate(Node node, OvsdbNodeAugmentation ovsdbNode) {
277         LOG.info("processOvsdbNodeCreate <{}> <{}>", node, ovsdbNode);
278         nodeCacheManager.nodeAdded(node);
279         bridgeConfigurationManager.prepareNode(node);
280     }
281
282     private void processOvsdbNodeUpdate(Node node, OvsdbNodeAugmentation ovsdbNode) {
283         LOG.info("processOvsdbNodeUpdate <{}> <{}>", node, ovsdbNode);
284         nodeCacheManager.nodeAdded(node);
285     }
286
287     private void processOvsdbNodeDelete(Node node, OvsdbNodeAugmentation ovsdbNode) {
288         LOG.info("processOvsdbNodeDelete <{}> <{}>", node, ovsdbNode);
289         nodeCacheManager.nodeRemoved(node);
290         /* TODO SB_MIGRATION
291         * I don't think we want to do this yet
292         InstanceIdentifier<Node> bridgeNodeIid =
293                 MdsalHelper.createInstanceIdentifier(ovsdbNode.getConnectionInfo(),
294                         Constants.INTEGRATION_BRIDGE);
295         southbound.delete(LogicalDatastoreType.CONFIGURATION, bridgeNodeIid);
296         */
297     }
298
299     private void processPortEvent(SouthboundEvent ev) {
300         switch (ev.getAction()) {
301             case ADD:
302             case UPDATE:
303                 processPortUpdate(ev.getNode(), (OvsdbTerminationPointAugmentation) ev.getAugmentationData());
304                 break;
305             case DELETE:
306                 processPortDelete(ev.getNode(), (OvsdbTerminationPointAugmentation) ev.getAugmentationData(), null);
307                 break;
308         }
309     }
310
311     private void processPortUpdate(Node node, OvsdbTerminationPointAugmentation port) {
312         LOG.debug("processPortUpdate <{}> <{}>", node, port);
313         NeutronNetwork network = tenantNetworkManager.getTenantNetwork(port);
314         if (network != null && !network.getRouterExternal()) {
315             this.handleInterfaceUpdate(node, port);
316         }
317     }
318
319     private void processOpenVSwitchEvent(SouthboundEvent ev) {
320         switch (ev.getAction()) {
321             case ADD:
322             case UPDATE:
323                 processOpenVSwitchUpdate(ev.getNode());
324                 break;
325             case DELETE:
326                 break;
327         }
328     }
329
330     private void processOpenVSwitchUpdate(Node node) {
331         LOG.debug("processOpenVSwitchUpdate {}", node);
332         // TODO this node might be the OvsdbNode and not have termination points
333         // Would need to change listener or grab tp nodes in here.
334         List<TerminationPoint> terminationPoints = southbound.extractTerminationPoints(node);
335         for (TerminationPoint terminationPoint : terminationPoints) {
336             processPortUpdate(node, terminationPoint.getAugmentation(OvsdbTerminationPointAugmentation.class));
337         }
338     }
339
340     private void processBridgeEvent(SouthboundEvent ev) {
341         switch (ev.getAction()) {
342             case ADD:
343                 processBridgeCreate(ev.getNode(), (OvsdbBridgeAugmentation) ev.getAugmentationData());
344                 break;
345             case UPDATE:
346                 processBridgeUpdate(ev.getNode(), (OvsdbBridgeAugmentation) ev.getAugmentationData());
347                 break;
348             case DELETE:
349                 processBridgeDelete(ev.getNode(), (OvsdbBridgeAugmentation) ev.getAugmentationData());
350                 break;
351         }
352     }
353
354     private void processBridgeCreate(Node node, OvsdbBridgeAugmentation bridge) {
355         LOG.debug("processBridgeCreate <{}> <{}>", node, bridge);
356         String datapathId = southbound.getDatapathId(bridge);
357         // Having a datapathId means the ovsdb node has connected to ODL
358         if (datapathId != null) {
359             nodeCacheManager.nodeAdded(node);
360         } else {
361             LOG.info("processBridgeCreate datapathId not found");
362         }
363     }
364
365     private void processBridgeUpdate(Node node, OvsdbBridgeAugmentation bridge) {
366         LOG.debug("processBridgeUpdate <{}> <{}>", node, bridge);
367         String datapathId = southbound.getDatapathId(bridge);
368         // Having a datapathId means the ovsdb node has connected to ODL
369         if (datapathId != null) {
370             nodeCacheManager.nodeAdded(node);
371         } else {
372             LOG.info("processBridgeUpdate datapathId not found");
373         }
374     }
375
376     private void processBridgeDelete(Node node, OvsdbBridgeAugmentation bridge) {
377         LOG.debug("processBridgeDelete: Delete bridge from config data store: <{}> <{}>",
378                 node, bridge);
379         nodeCacheManager.nodeRemoved(node);
380         // TODO SB_MIGRATION
381         // Not sure if we want to do this yet
382         southbound.deleteBridge(node);
383     }
384
385     @Override
386     public void setDependencies(ServiceReference serviceReference) {
387         configurationService =
388                 (ConfigurationService) ServiceHelper.getGlobalInstance(ConfigurationService.class, this);
389         networkingProviderManager =
390                 (NetworkingProviderManager) ServiceHelper.getGlobalInstance(NetworkingProviderManager.class, this);
391         tenantNetworkManager =
392                 (TenantNetworkManager) ServiceHelper.getGlobalInstance(TenantNetworkManager.class, this);
393         bridgeConfigurationManager =
394                 (BridgeConfigurationManager) ServiceHelper.getGlobalInstance(BridgeConfigurationManager.class, this);
395         nodeCacheManager =
396                 (NodeCacheManager) ServiceHelper.getGlobalInstance(NodeCacheManager.class, this);
397         nodeCacheManager.cacheListenerAdded(serviceReference, this);
398         neutronL3Adapter =
399                 (NeutronL3Adapter) ServiceHelper.getGlobalInstance(NeutronL3Adapter.class, this);
400         southbound =
401                 (Southbound) ServiceHelper.getGlobalInstance(Southbound.class, this);
402         eventDispatcher =
403                 (EventDispatcher) ServiceHelper.getGlobalInstance(EventDispatcher.class, this);
404         eventDispatcher.eventHandlerAdded(serviceReference, this);
405         ovsdbInventoryService =
406                 (OvsdbInventoryService) ServiceHelper.getGlobalInstance(OvsdbInventoryService.class, this);
407         ovsdbInventoryService.listenerAdded(this);
408     }
409
410     @Override
411     public void setDependencies(Object impl) {
412     }
413 }