2 * Copyright (c) 2013, 2016 Red Hat, Inc. and others. All rights reserved.
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
9 package org.opendaylight.netvirt.openstack.netvirt;
11 import java.util.List;
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;
41 * @author Madhu Venugopal
42 * @author Brent Salisbury
44 * @author Sam Hague (shague@redhat.com)
46 public class SouthboundHandler extends AbstractHandler
47 implements ConfigInterface, NodeCacheListener, OvsdbInventoryListener {
48 private static final Logger LOG = LoggerFactory.getLogger(SouthboundHandler.class);
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 private volatile VLANProvider vlanProvider;
62 private SouthboundEvent.Type ovsdbTypeToSouthboundEventType(OvsdbType ovsdbType) {
63 SouthboundEvent.Type type = SouthboundEvent.Type.NODE;
67 type = SouthboundEvent.Type.NODE;
70 type = SouthboundEvent.Type.BRIDGE;
73 type = SouthboundEvent.Type.PORT;
76 type = SouthboundEvent.Type.CONTROLLER;
79 type = SouthboundEvent.Type.OPENVSWITCH;
82 LOG.warn("Invalid OvsdbType: {}", ovsdbType);
89 public void ovsdbUpdate(Node node, DataObject resourceAugmentationData, OvsdbType ovsdbType, Action action) {
90 LOG.info("Received ovsdbUpdate for : {} with action : {} for the OVS node : {}"
91 +"Resource Data : {}", ovsdbType, action, node, resourceAugmentationData);
92 enqueueEvent(new SouthboundEvent(node, resourceAugmentationData,
93 ovsdbTypeToSouthboundEventType(ovsdbType), action));
96 private void handleInterfaceUpdate (Node node, OvsdbTerminationPointAugmentation tp) {
97 NeutronNetwork network = tenantNetworkManager.getTenantNetwork(tp);
98 if (network != null && !network.getRouterExternal()) {
99 LOG.trace("InterfaceUpdate for OVS Node :{} OvsdbTerminationPointAugmentation :" + " {} for the network : {}", node, tp, network.getNetworkUUID());
100 if (bridgeConfigurationManager.createLocalNetwork(node, network)) {
101 networkingProviderManager.getProvider(node).handleInterfaceUpdate(network, node, tp);
104 LOG.debug("No tenant network found on node : {} for interface: {}", node, tp);
106 distributedArpService.processInterfaceEvent(node, tp, network, Action.UPDATE);
107 neutronL3Adapter.handleInterfaceEvent(node, tp, network, Action.UPDATE);
110 private void handleInterfaceDelete (Node node, OvsdbTerminationPointAugmentation intf,
111 boolean isLastInstanceOnNode, NeutronNetwork network) {
112 LOG.debug("handleInterfaceDelete: node: <{}>, isLastInstanceOnNode: {}, interface: <{}>",
113 node, isLastInstanceOnNode, intf);
115 distributedArpService.processInterfaceEvent(node, intf, network, Action.DELETE);
116 neutronL3Adapter.handleInterfaceEvent(node, intf, network, Action.DELETE);
117 final NeutronPort neutronPort = tenantNetworkManager.getTenantPort(intf);
118 programVLANNetworkFlowProvider(node, intf, network, neutronPort, false);
119 List<String> phyIfName = bridgeConfigurationManager.getAllPhysicalInterfaceNames(node);
120 if (isInterfaceOfInterest(intf, phyIfName)) {
121 // delete tunnel or physical interfaces
122 networkingProviderManager.getProvider(node).handleInterfaceDelete(network.getProviderNetworkType(),
123 network, node, intf, isLastInstanceOnNode);
124 } else if (network != null) {
125 // vlan doesn't need a tunnel endpoint
126 if (!network.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN) &&
127 configurationService.getTunnelEndPoint(node) == null) {
128 LOG.error("Tunnel end-point configuration missing. Please configure it in OpenVSwitch Table");
131 networkingProviderManager.getProvider(node).handleInterfaceDelete(network.getProviderNetworkType(),
132 network, node, intf, isLastInstanceOnNode);
137 public void triggerUpdates() {
138 List<Node> ovsdbNodes = southbound.readOvsdbTopologyNodes();
139 for (Node node : ovsdbNodes) {
140 ovsdbUpdate(node, node.getAugmentation(OvsdbNodeAugmentation.class),
141 OvsdbInventoryListener.OvsdbType.NODE, Action.ADD);
145 private void processPortDelete(Node node, OvsdbTerminationPointAugmentation ovsdbTerminationPointAugmentation,
147 LOG.debug("processPortDelete for Node : {} ovsdbTerminationPointAugmentation : {}",
148 node, ovsdbTerminationPointAugmentation);
149 NeutronNetwork network;
150 if (context == null) {
151 network = tenantNetworkManager.getTenantNetwork(ovsdbTerminationPointAugmentation);
153 network = (NeutronNetwork)context;
155 List<String> phyIfName = bridgeConfigurationManager.getAllPhysicalInterfaceNames(node);
156 if (isInterfaceOfInterest(ovsdbTerminationPointAugmentation, phyIfName)) {
157 if (network != null) {
158 this.handleInterfaceDelete(node, ovsdbTerminationPointAugmentation, false, network);
160 LOG.warn("processPortDelete: network was null, ignoring update");
162 } else if (network != null && !network.getRouterExternal()) {
163 LOG.debug("Network {}: Delete interface {} attached to bridge {}", network.getNetworkUUID(),
164 ovsdbTerminationPointAugmentation.getInterfaceUuid(), node.getNodeId());
166 OvsdbBridgeAugmentation ovsdbBridgeAugmentation = southbound.getBridge(node);
167 if (ovsdbBridgeAugmentation != null) {
168 List<TerminationPoint> terminationPoints = node.getTerminationPoint();
169 if (!terminationPoints.isEmpty()){
170 boolean isLastInstanceOnNode = true;
171 for (TerminationPoint terminationPoint : terminationPoints) {
172 OvsdbTerminationPointAugmentation tpAugmentation =
173 terminationPoint.getAugmentation( OvsdbTerminationPointAugmentation.class);
174 if (tpAugmentation.getInterfaceUuid().equals(
175 ovsdbTerminationPointAugmentation.getInterfaceUuid())) {
178 NeutronNetwork neutronNetwork = tenantNetworkManager.getTenantNetwork(tpAugmentation);
179 if (neutronNetwork != null) {
180 String neutronNetworkSegId = neutronNetwork.getProviderSegmentationID();
181 String networkSegId = network.getProviderSegmentationID();
182 // vxlan ports should not be removed in table 110 flow entry
183 // unless last VM instance removed from the openstack node(Bug# 5813)
184 if (neutronNetworkSegId.equals(networkSegId)) {
185 isLastInstanceOnNode = false;
190 this.handleInterfaceDelete(node, ovsdbTerminationPointAugmentation,
191 isLastInstanceOnNode, network);
194 } catch (Exception e) {
195 LOG.error("Error fetching Interface Rows for node {}", node, e);
197 } else if (network != null && network.getRouterExternal()) {
198 this.handleInterfaceDelete(node, ovsdbTerminationPointAugmentation, false, network);
200 //remove neutronPort from the CleanupCache, if it has the entry.
201 NeutronPort neutronPort = null;
202 String neutronPortId = southbound.getInterfaceExternalIdsValue(ovsdbTerminationPointAugmentation,
203 Constants.EXTERNAL_ID_INTERFACE_ID);
204 if (neutronPortId != null) {
205 LOG.trace("Clean up the NeutronPortCache for {} ", neutronPortId);
206 neutronPort = neutronL3Adapter.getPortFromCleanupCache(neutronPortId);
208 if (neutronPort != null) {
209 LOG.debug("Clean up the NeutronPortCache ");
210 neutronL3Adapter.removePortFromCleanupCache(neutronPort);
211 neutronL3Adapter.removeNetworkFromCleanupCache(neutronPort.getNetworkUUID());
213 LOG.trace("Nothing to Clean up in the NeutronPortCache ");
218 private boolean isInterfaceOfInterest(OvsdbTerminationPointAugmentation terminationPoint, List<String> phyIfName) {
219 LOG.trace("SouthboundHandler#isInterfaceOfInterest: Interface : {}", terminationPoint);
221 if(terminationPoint.getInterfaceType() == null){
222 // This is OK since eth ports don't have an interface type
225 return MdsalHelper.createOvsdbInterfaceType(
226 terminationPoint.getInterfaceType()).equals(NetworkHandler.NETWORK_TYPE_VXLAN)
228 MdsalHelper.createOvsdbInterfaceType(
229 terminationPoint.getInterfaceType()).equals(NetworkHandler.NETWORK_TYPE_GRE)
231 phyIfName.contains(terminationPoint.getName());
235 * Notification about an OpenFlow Node
237 * @param node the {@link Node Node} of interest in the notification
238 * @param action the {@link Action}
239 * @see NodeCacheListener#notifyNode
242 public void notifyNode (Node node, Action action) {
243 LOG.info("notifyNode : action: {}, Node : {} ", action, node);
245 if ((action.equals(Action.ADD)) && (southbound.getBridge(node) != null)) {
246 networkingProviderManager.getProvider(node).initializeOFFlowRules(node);
253 * @param abstractEvent the {@link AbstractEvent} event to be handled.
254 * @see EventDispatcher
257 public void processEvent(AbstractEvent abstractEvent) {
258 if (!(abstractEvent instanceof SouthboundEvent)) {
259 LOG.error("processEvent Unable to process abstract event : {}", abstractEvent);
262 SouthboundEvent ev = (SouthboundEvent) abstractEvent;
263 LOG.trace("processEvent : {} for TransactionId : {}", ev, ev.getTransactionId());
264 switch (ev.getType()) {
266 processOvsdbNodeEvent(ev);
270 processBridgeEvent(ev);
274 processPortEvent(ev);
278 processOpenVSwitchEvent(ev);
282 LOG.warn("Unable to process type : {} action : {} for node : {}", ev.getType(), ev.getAction(), ev.getNode());
285 LOG.trace("processEvent exit : {} for TransactionId : {}", ev, ev.getTransactionId());
288 private void processOvsdbNodeEvent(SouthboundEvent ev) {
289 switch (ev.getAction()) {
291 processOvsdbNodeCreate(ev.getNode(), (OvsdbNodeAugmentation) ev.getAugmentationData());
294 processOvsdbNodeUpdate(ev.getNode(), (OvsdbNodeAugmentation) ev.getAugmentationData());
297 processOvsdbNodeDelete(ev.getNode(), (OvsdbNodeAugmentation) ev.getAugmentationData());
302 private void processOvsdbNodeCreate(Node node, OvsdbNodeAugmentation ovsdbNode) {
303 LOG.info("processOvsdb Node Create : {} ", ovsdbNode);
304 nodeCacheManager.nodeAdded(node);
305 bridgeConfigurationManager.prepareNode(node);
308 private void processOvsdbNodeUpdate(Node node, OvsdbNodeAugmentation ovsdbNode) {
309 LOG.info("processOvsdb Node Update : {} ", ovsdbNode);
310 nodeCacheManager.nodeAdded(node);
313 private void processOvsdbNodeDelete(Node node, OvsdbNodeAugmentation ovsdbNode) {
314 LOG.info("processOvsdb Node Delete : {} ", ovsdbNode);
315 nodeCacheManager.nodeRemoved(node);
317 * I don't think we want to do this yet
318 InstanceIdentifier<Node> bridgeNodeIid =
319 MdsalHelper.createInstanceIdentifier(ovsdbNode.getConnectionInfo(),
320 Constants.INTEGRATION_BRIDGE);
321 southbound.delete(LogicalDatastoreType.CONFIGURATION, bridgeNodeIid);
325 private void processPortEvent(SouthboundEvent ev) {
326 switch (ev.getAction()) {
329 processPortUpdate(ev.getNode(), (OvsdbTerminationPointAugmentation) ev.getAugmentationData(), ev.getAction());
332 processPortDelete(ev.getNode(), (OvsdbTerminationPointAugmentation) ev.getAugmentationData(), null);
337 private void processPortUpdate(Node node, OvsdbTerminationPointAugmentation port, Action action) {
338 LOG.debug("processPortUpdate : {} for the Node: {}", port, node);
339 NeutronNetwork network = tenantNetworkManager.getTenantNetwork(port);
340 if (network != null) {
341 final NeutronPort neutronPort = tenantNetworkManager.getTenantPort(port);
342 if (!network.getRouterExternal()) {
343 handleInterfaceUpdate(node, port);
344 } else if (action != null && action.equals(Action.UPDATE)) {
345 programVLANNetworkFlowProvider(node, port, network, neutronPort, true);
350 private void programVLANNetworkFlowProvider(final Node node, final OvsdbTerminationPointAugmentation port,
351 final NeutronNetwork network, final NeutronPort neutronPort, final Boolean isWrite) {
352 if (neutronPort != null && neutronPort.getDeviceOwner().equalsIgnoreCase(Constants.OWNER_ROUTER_GATEWAY) &&
353 network.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN) &&
354 configurationService.isL3MultipleExternalNetworkEnabled()) {
355 vlanProvider.programProviderNetworkFlow(node, port, network, neutronPort, isWrite);
359 private void processOpenVSwitchEvent(SouthboundEvent ev) {
360 switch (ev.getAction()) {
363 processOpenVSwitchUpdate(ev.getNode());
370 private void processOpenVSwitchUpdate(Node node) {
371 LOG.debug("processOpenVSwitchUpdate : {}", node);
372 // TODO this node might be the OvsdbNode and not have termination points
373 // Would need to change listener or grab tp nodes in here.
374 List<TerminationPoint> terminationPoints = southbound.extractTerminationPoints(node);
375 for (TerminationPoint terminationPoint : terminationPoints) {
376 processPortUpdate(node, terminationPoint.getAugmentation(OvsdbTerminationPointAugmentation.class), null);
380 private void processBridgeEvent(SouthboundEvent ev) {
381 switch (ev.getAction()) {
383 processBridgeCreate(ev.getNode(), (OvsdbBridgeAugmentation) ev.getAugmentationData());
386 processBridgeUpdate(ev.getNode(), (OvsdbBridgeAugmentation) ev.getAugmentationData());
389 processBridgeDelete(ev.getNode(), (OvsdbBridgeAugmentation) ev.getAugmentationData());
394 private void processBridgeCreate(Node node, OvsdbBridgeAugmentation bridge) {
395 LOG.debug("BridgeCreate : {} for the Node : {}", bridge, node);
396 String datapathId = southbound.getDatapathId(bridge);
397 // Having a datapathId means the ovsdb node has connected to ODL
398 if (datapathId != null) {
399 nodeCacheManager.nodeAdded(node);
401 LOG.debug("processBridgeCreate datapathId not found");
405 private void processBridgeUpdate(Node node, OvsdbBridgeAugmentation bridge) {
406 LOG.debug("BridgeUpdate : {} for the node : {}", bridge, node);
407 String datapathId = southbound.getDatapathId(bridge);
408 // Having a datapathId means the ovsdb node has connected to ODL
409 if (datapathId != null) {
410 nodeCacheManager.nodeAdded(node);
412 LOG.debug("processBridgeUpdate datapathId not found");
416 private void processBridgeDelete(Node node, OvsdbBridgeAugmentation bridge) {
417 LOG.debug("BridgeDelete: Delete bridge from config data store : {}"
418 +"Node : {}", bridge, node);
419 nodeCacheManager.nodeRemoved(node);
421 // Not sure if we want to do this yet
422 southbound.deleteBridge(node);
426 public void setDependencies(ServiceReference serviceReference) {
427 configurationService =
428 (ConfigurationService) ServiceHelper.getGlobalInstance(ConfigurationService.class, this);
429 networkingProviderManager =
430 (NetworkingProviderManager) ServiceHelper.getGlobalInstance(NetworkingProviderManager.class, this);
431 tenantNetworkManager =
432 (TenantNetworkManager) ServiceHelper.getGlobalInstance(TenantNetworkManager.class, this);
433 bridgeConfigurationManager =
434 (BridgeConfigurationManager) ServiceHelper.getGlobalInstance(BridgeConfigurationManager.class, this);
436 (NodeCacheManager) ServiceHelper.getGlobalInstance(NodeCacheManager.class, this);
437 nodeCacheManager.cacheListenerAdded(serviceReference, this);
439 (NeutronL3Adapter) ServiceHelper.getGlobalInstance(NeutronL3Adapter.class, this);
440 distributedArpService =
441 (DistributedArpService) ServiceHelper.getGlobalInstance(DistributedArpService.class, this);
443 (Southbound) ServiceHelper.getGlobalInstance(Southbound.class, this);
445 (EventDispatcher) ServiceHelper.getGlobalInstance(EventDispatcher.class, this);
446 eventDispatcher.eventHandlerAdded(serviceReference, this);
447 ovsdbInventoryService =
448 (OvsdbInventoryService) ServiceHelper.getGlobalInstance(OvsdbInventoryService.class, this);
449 ovsdbInventoryService.listenerAdded(this);
451 (VLANProvider) ServiceHelper.getGlobalInstance(VLANProvider.class, this);
455 public void setDependencies(Object impl) {