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