Reenable neutron I*Aware services
[ovsdb.git] / openstack / net-virt / src / main / java / org / opendaylight / ovsdb / openstack / netvirt / impl / NeutronL3Adapter.java
1 /*
2  * Copyright (C) 2014 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  * Authors : Dave Tucker, Flavio Fernandes
9  */
10
11 package org.opendaylight.ovsdb.openstack.netvirt.impl;
12
13 import org.opendaylight.neutron.spi.INeutronNetworkCRUD;
14 import org.opendaylight.neutron.spi.INeutronPortCRUD;
15 import org.opendaylight.neutron.spi.INeutronSubnetCRUD;
16 import org.opendaylight.neutron.spi.NeutronFloatingIP;
17 import org.opendaylight.neutron.spi.NeutronNetwork;
18 import org.opendaylight.neutron.spi.NeutronPort;
19 import org.opendaylight.neutron.spi.NeutronRouter;
20 import org.opendaylight.neutron.spi.NeutronRouter_Interface;
21 import org.opendaylight.neutron.spi.NeutronSubnet;
22 import org.opendaylight.neutron.spi.Neutron_IPs;
23 import org.opendaylight.ovsdb.openstack.netvirt.MdsalUtils;
24 import org.opendaylight.ovsdb.openstack.netvirt.api.*;
25 import org.opendaylight.ovsdb.utils.config.ConfigProperties;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
27 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
28
29 import com.google.common.base.Preconditions;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 import java.net.InetAddress;
34 import java.net.UnknownHostException;
35 import java.util.HashMap;
36 import java.util.HashSet;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Set;
40
41 /**
42  * Neutron L3 Adapter implements a hub-like adapter for the various Neutron events. Based on
43  * these events, the abstract router callbacks can be generated to the multi-tenant aware router,
44  * as well as the multi-tenant router forwarding provider.
45  */
46 public class NeutronL3Adapter {
47
48     /**
49      * Logger instance.
50      */
51     static final Logger logger = LoggerFactory.getLogger(NeutronL3Adapter.class);
52
53     // The implementation for each of these services is resolved by the OSGi Service Manager
54     private volatile ConfigurationService configurationService;
55     private volatile TenantNetworkManager tenantNetworkManager;
56     private volatile OvsdbConnectionService connectionService;
57     private volatile INeutronNetworkCRUD neutronNetworkCache;
58     private volatile INeutronSubnetCRUD neutronSubnetCache;
59     private volatile INeutronPortCRUD neutronPortCache;
60     private volatile L3ForwardingProvider l3ForwardingProvider;
61     private volatile InboundNatProvider inboundNatProvider;
62     private volatile OutboundNatProvider outboundNatProvider;
63     private volatile ArpProvider arpProvider;
64     private volatile RoutingProvider routingProvider;
65
66     private Set<String> inboundIpRewriteCache;
67     private Set<String> outboundIpRewriteCache;
68     private Set<String> inboundIpRewriteExclusionCache;
69     private Set<String> outboundIpRewriteExclusionCache;
70     private Set<String> routerInterfacesCache;
71     private Set<String> staticArpEntryCache;
72     private Set<String> l3ForwardingCache;
73     private Set<String> defaultRouteCache;
74     private Map<String, String> networkIdToRouterMacCache;
75     private Map<String, NeutronRouter_Interface> subnetIdToRouterInterfaceCache;
76     private Boolean enabled = false;
77
78     void init() {
79         logger.info(">>>>>> init {}", this.getClass());
80         final String enabledPropertyStr = ConfigProperties.getProperty(this.getClass(), "ovsdb.l3.fwd.enabled");
81         if (enabledPropertyStr != null && enabledPropertyStr.equalsIgnoreCase("yes")) {
82             this.inboundIpRewriteCache = new HashSet<>();
83             this.outboundIpRewriteCache = new HashSet<>();
84             this.inboundIpRewriteExclusionCache = new HashSet<>();
85             this.outboundIpRewriteExclusionCache = new HashSet<>();
86             this.routerInterfacesCache = new HashSet<>();
87             this.staticArpEntryCache = new HashSet<>();
88             this.l3ForwardingCache = new HashSet<>();
89             this.defaultRouteCache = new HashSet<>();
90             this.networkIdToRouterMacCache = new HashMap<>();
91             this.subnetIdToRouterInterfaceCache = new HashMap<>();
92
93             this.enabled = true;
94             logger.info("OVSDB L3 forwarding is enabled");
95         } else {
96             logger.debug("OVSDB L3 forwarding is disabled");
97         }
98     }
99
100     //
101     // Callbacks from OVSDB's northbound handlers
102     //
103
104     public void handleNeutronSubnetEvent(final NeutronSubnet subnet, Action action) {
105         logger.debug("Neutron subnet {} event : {}", action, subnet.toString());
106         if (!this.enabled)
107             return;
108     }
109
110     public void handleNeutronPortEvent(final NeutronPort neutronPort, Action action) {
111         logger.debug("Neutron port {} event : {}", action, neutronPort.toString());
112         if (!this.enabled)
113             return;
114
115         final boolean isDelete = action == Action.DELETE;
116
117         // Treat the port event as a router interface event if the port belongs to router. This is a
118         // helper for handling cases when handleNeutronRouterInterfaceEvent is not available
119         //
120         if (neutronPort.getDeviceOwner().equalsIgnoreCase("network:router_interface")) {
121             for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
122                 NeutronRouter_Interface neutronRouterInterface =
123                         new NeutronRouter_Interface(neutronIP.getSubnetUUID(), neutronPort.getPortUUID());
124                 neutronRouterInterface.setID(neutronIP.getSubnetUUID());  // id of router interface to be same as subnet
125                 neutronRouterInterface.setTenantID(neutronPort.getTenantID());
126
127                 this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
128             }
129         } else {
130             // We made it here, port is not used as a router interface. If this is not a delete action, make sure that
131             // all nodes that are supposed to have a router interface for the port's subnet(s), have it configured. We
132             // need to do this check here because a router interface is not added to a node until tenant becomes needed
133             // there.
134             //
135             if (!isDelete) {
136                 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
137                     NeutronRouter_Interface neutronRouterInterface =
138                             subnetIdToRouterInterfaceCache.get(neutronIP.getSubnetUUID());
139                     if (neutronRouterInterface != null) {
140                         this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
141                     }
142                 }
143             }
144             this.updateL3ForNeutronPort(neutronPort, isDelete);
145         }
146     }
147
148     public void handleNeutronRouterEvent(final NeutronRouter neutronRouter, Action action) {
149         logger.debug("Neutron router {} event : {}", action, neutronRouter.toString());
150         if (!this.enabled)
151             return;
152     }
153
154     public void handleNeutronRouterInterfaceEvent(final NeutronRouter neutronRouter,
155                                                   final NeutronRouter_Interface neutronRouterInterface,
156                                                   Action action) {
157         logger.debug("Router interface {} got event {}. Subnet {}",
158                      neutronRouterInterface.getPortUUID(),
159                      action,
160                      neutronRouterInterface.getSubnetUUID());
161         if (!this.enabled)
162             return;
163
164         final boolean isDelete = action == Action.DELETE;
165
166         this.programFlowsForNeutronRouterInterface(neutronRouterInterface, isDelete);
167
168         // As neutron router interface is added/removed, we need to iterate through all the neutron ports and
169         // see if they are affected by l3
170         //
171         for (NeutronPort neutronPort : neutronPortCache.getAllPorts()) {
172             boolean currPortShouldBeDeleted = false;
173             // Note: delete in this case only applies to 1)router interface delete and 2)ports on the same subnet
174             if (isDelete) {
175                 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
176                     if (neutronRouterInterface.getSubnetUUID().equalsIgnoreCase(neutronIP.getSubnetUUID())) {
177                         currPortShouldBeDeleted = true;
178                         break;
179                     }
180                 }
181             }
182             this.updateL3ForNeutronPort(neutronPort, currPortShouldBeDeleted);
183         }
184     }
185
186     public void handleNeutronFloatingIPEvent(final NeutronFloatingIP neutronFloatingIP,
187                                              Action action) {
188         logger.debug(" Floating IP {} {}<->{}, network uuid {}", action,
189                      neutronFloatingIP.getFixedIPAddress(),
190                      neutronFloatingIP.getFloatingIPAddress(),
191                      neutronFloatingIP.getFloatingNetworkUUID());
192         if (!this.enabled)
193             return;
194
195         this.programFlowsForFloatingIP(neutronFloatingIP, action == Action.DELETE);
196     }
197
198     public void handleNeutronNetworkEvent(final NeutronNetwork neutronNetwork, Action action) {
199         logger.debug("neutronNetwork {}: network: {}", action, neutronNetwork);
200         if (!this.enabled)
201             return;
202     }
203
204     //
205     // Callbacks from OVSDB's southbound handler
206     //
207     public void handleInterfaceEvent(final Node node, final OvsdbTerminationPointAugmentation intf,
208                                      final NeutronNetwork neutronNetwork, Action action) {
209         logger.debug("southbound interface {} node:{} interface:{}, neutronNetwork:{}",
210                      action, node, intf.getName(), neutronNetwork);
211         if (!this.enabled)
212             return;
213
214         NeutronPort neutronPort = tenantNetworkManager.getTenantPort(intf);
215         if (neutronPort != null) {
216             this.handleNeutronPortEvent(neutronPort, action);
217         }
218     }
219
220     //
221     // Internal helpers
222     //
223     private void updateL3ForNeutronPort(final NeutronPort neutronPort, final boolean isDelete) {
224
225         final String networkUUID = neutronPort.getNetworkUUID();
226         final String routerMacAddress = networkIdToRouterMacCache.get(networkUUID);
227
228         // If there is no router interface handling the networkUUID, we are done
229         if (routerMacAddress == null || routerMacAddress.isEmpty()) {
230             return;
231         }
232
233         // If this is the neutron port for the router interface itself, ignore it as well. Ports that represent the
234         // router interface are handled via handleNeutronRouterInterfaceEvent.
235         if (routerMacAddress.equalsIgnoreCase(neutronPort.getMacAddress())) {
236             return;
237         }
238
239         final NeutronNetwork neutronNetwork = neutronNetworkCache.getNetwork(networkUUID);
240         final String providerSegmentationId = neutronNetwork != null ?
241                                               neutronNetwork.getProviderSegmentationID() : null;
242         final String tenantMac = neutronPort.getMacAddress();
243
244         if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
245             tenantMac == null || tenantMac.isEmpty()) {
246             return;  // done: go no further w/out all the info needed...
247         }
248
249         final Action action = isDelete ? Action.DELETE : Action.ADD;
250         List<Node> nodes = connectionService.getBridgeNodes();
251         if (nodes.isEmpty()) {
252             logger.trace("updateL3ForNeutronPort has no nodes to work with");
253         }
254         for (Node node : nodes) {
255             final Long dpid = getDpid(node);
256             final boolean tenantNetworkPresentInNode =
257                     tenantNetworkManager.isTenantNetworkPresentInNode(node, providerSegmentationId);
258             for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
259                 final String tenantIpStr = neutronIP.getIpAddress();
260                 if (tenantIpStr.isEmpty()) {
261                     continue;
262                 }
263
264                 // Configure L3 fwd. We do that regardless of tenant network present, because these rules are
265                 // still needed when routing to subnets non-local to node (bug 2076).
266                 programL3ForwardingStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr, action);
267
268                 // Configure distributed ARP responder. Only needed if tenant network exists in node.
269                 programStaticArpStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr,
270                                        tenantNetworkPresentInNode ? action : Action.DELETE);
271             }
272         }
273     }
274
275     private void programL3ForwardingStage1(Node node, Long dpid, String providerSegmentationId,
276                                            String macAddress, String ipStr,
277                                            Action actionForNode) {
278         // Based on the local cache, figure out whether programming needs to occur. To do this, we
279         // will look at desired action for node.
280         //
281         final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + ipStr;
282         final Boolean isProgrammed = l3ForwardingCache.contains(cacheKey);
283
284         if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
285             logger.trace("programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {} is already done",
286                          node.getNodeId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
287             return;
288         }
289         if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
290             logger.trace("programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {} is already done",
291                     node.getNodeId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
292             return;
293         }
294
295         Status status = this.programL3ForwardingStage2(node, dpid, providerSegmentationId,
296                                                        macAddress, ipStr, actionForNode);
297         if (status.isSuccess()) {
298             // Update cache
299             if (actionForNode == Action.ADD) {
300                 l3ForwardingCache.add(cacheKey);
301             } else {
302                 l3ForwardingCache.remove(cacheKey);
303             }
304         }
305     }
306
307     private Status programL3ForwardingStage2(Node node, Long dpid, String providerSegmentationId,
308                                              String macAddress,
309                                              String address,
310                                              Action actionForNode) {
311         Status status;
312         try {
313             InetAddress inetAddress = InetAddress.getByName(address);
314             status = l3ForwardingProvider == null ?
315                      new Status(StatusCode.SUCCESS) :
316                      l3ForwardingProvider.programForwardingTableEntry(dpid, providerSegmentationId,
317                                                                       inetAddress, macAddress, actionForNode);
318         } catch (UnknownHostException e) {
319             status = new Status(StatusCode.BADREQUEST);
320         }
321
322         if (status.isSuccess()) {
323             logger.debug("ProgramL3Forwarding {} for mac:{} addr:{} node:{} action:{}",
324                          l3ForwardingProvider == null ? "skipped" : "programmed",
325                          macAddress, address, node, actionForNode);
326         } else {
327             logger.error("ProgramL3Forwarding failed for mac:{} addr:{} node:{} action:{} status:{}",
328                          macAddress, address, node, actionForNode, status);
329         }
330         return status;
331     }
332
333     // --
334
335     private void programFlowsForNeutronRouterInterface(final NeutronRouter_Interface destNeutronRouterInterface,
336                                                        Boolean isDelete) {
337         Preconditions.checkNotNull(destNeutronRouterInterface);
338
339         final NeutronPort neutronPort = neutronPortCache.getPort(destNeutronRouterInterface.getPortUUID());
340         final String macAddress = neutronPort != null ? neutronPort.getMacAddress() : null;
341         final List<Neutron_IPs> ipList = neutronPort != null ? neutronPort.getFixedIPs() : null;
342         final NeutronSubnet subnet = neutronSubnetCache.getSubnet(destNeutronRouterInterface.getSubnetUUID());
343         final NeutronNetwork neutronNetwork = subnet != null ?
344                                               neutronNetworkCache.getNetwork(subnet.getNetworkUUID()) : null;
345         final String destinationSegmentationId = neutronNetwork != null ?
346                                                  neutronNetwork.getProviderSegmentationID() : null;
347         final String gatewayIp = subnet != null ? subnet.getGatewayIP() : null;
348         final Boolean isExternal = neutronNetwork != null ? neutronNetwork.getRouterExternal() : Boolean.TRUE;
349         final String cidr = subnet != null ? subnet.getCidr() : null;
350         final int mask = getMaskLenFromCidr(cidr);
351
352         logger.trace("programFlowsForNeutronRouterInterface called for interface {} isDelete {}",
353                      destNeutronRouterInterface, isDelete);
354
355         if (destinationSegmentationId == null || destinationSegmentationId.isEmpty() ||
356             cidr == null || cidr.isEmpty() ||
357             macAddress == null || macAddress.isEmpty() ||
358             ipList == null || ipList.isEmpty()) {
359             logger.debug("programFlowsForNeutronRouterInterface is bailing seg:{} cidr:{} mac:{}  ip:{}",
360                          destinationSegmentationId, cidr, macAddress, ipList);
361             return;  // done: go no further w/out all the info needed...
362         }
363
364         final Action action = isDelete ? Action.DELETE : Action.ADD;
365
366         // Keep cache for finding router's mac from network uuid -- add
367         //
368         if (! isDelete) {
369             networkIdToRouterMacCache.put(neutronNetwork.getNetworkUUID(), macAddress);
370             subnetIdToRouterInterfaceCache.put(subnet.getSubnetUUID(), destNeutronRouterInterface);
371         }
372
373         List<Node> nodes = connectionService.getBridgeNodes();
374         if (nodes.isEmpty()) {
375             logger.trace("programFlowsForNeutronRouterInterface has no nodes to work with");
376         }
377         for (Node node : nodes) {
378             final Long dpid = getDpid(node);
379             final Action actionForNode =
380                     tenantNetworkManager.isTenantNetworkPresentInNode(node, destinationSegmentationId) ?
381                     action : Action.DELETE;
382
383             for (Neutron_IPs neutronIP : ipList) {
384                 final String ipStr = neutronIP.getIpAddress();
385                 if (ipStr.isEmpty()) {
386                     logger.debug("programFlowsForNeutronRouterInterface is skipping node {} ip {}",
387                             node.getNodeId().getValue(), ipStr);
388                     continue;
389                 }
390
391                 // Iterate through all other interfaces and add/remove reflexive flows to this interface
392                 //
393                 for (NeutronRouter_Interface srcNeutronRouterInterface : subnetIdToRouterInterfaceCache.values()) {
394                     programFlowsForNeutronRouterInterfacePair(node, dpid,
395                                                               srcNeutronRouterInterface, destNeutronRouterInterface,
396                                                               neutronNetwork, destinationSegmentationId,
397                                                               macAddress, ipStr, mask, actionForNode,
398                                                               true /*isReflexsive*/);
399                 }
400
401                 programStaticArpStage1(node, dpid, destinationSegmentationId, macAddress, ipStr, actionForNode);
402             }
403
404             // Compute action to be programmed. In the case of rewrite exclusions, we must never program rules
405             // for the external neutron networks.
406             //
407             {
408                 final Action actionForRewriteExclusion = isExternal ? Action.DELETE : actionForNode;
409                 programIpRewriteExclusionStage1(node, dpid, destinationSegmentationId, true /* isInbound */,
410                                                 cidr, actionForRewriteExclusion);
411                 programIpRewriteExclusionStage1(node, dpid, destinationSegmentationId, false /* isInbound */,
412                                                 cidr, actionForRewriteExclusion);
413             }
414
415             // Default route. For non-external subnet, make sure that there is none configured.
416             //
417             if (gatewayIp != null && !gatewayIp.isEmpty()) {
418                 final Action actionForNodeDefaultRoute =
419                         isExternal ? actionForNode : Action.DELETE;
420                 final String defaultGatewayMacAddress = configurationService.getDefaultGatewayMacAddress(node);
421                 programDefaultRouteStage1(node, dpid, destinationSegmentationId, defaultGatewayMacAddress, gatewayIp,
422                                           actionForNodeDefaultRoute);
423             }
424         }
425
426         // Keep cache for finding router's mac from network uuid -- remove
427         //
428         if (isDelete) {
429             networkIdToRouterMacCache.remove(neutronNetwork.getNetworkUUID());
430             subnetIdToRouterInterfaceCache.remove(subnet.getSubnetUUID());
431         }
432     }
433
434     private void programFlowsForNeutronRouterInterfacePair(final Node node,
435                                                            final Long dpid,
436                                                            final NeutronRouter_Interface srcNeutronRouterInterface,
437                                                            final NeutronRouter_Interface dstNeutronRouterInterface,
438                                                            final NeutronNetwork dstNeutronNetwork,
439                                                            final String destinationSegmentationId,
440                                                            final String dstMacAddress,
441                                                            final String destIpStr,
442                                                            final int destMask,
443                                                            final Action actionForNode,
444                                                            Boolean isReflexsive) {
445         Preconditions.checkNotNull(srcNeutronRouterInterface);
446         Preconditions.checkNotNull(dstNeutronRouterInterface);
447
448         final String sourceSubnetId = srcNeutronRouterInterface.getSubnetUUID();
449         if (sourceSubnetId == null) {
450             logger.error("Could not get provider Subnet ID from router interface {}",
451                          srcNeutronRouterInterface.getID());
452             return;
453         }
454
455         final NeutronSubnet sourceSubnet = neutronSubnetCache.getSubnet(sourceSubnetId);
456         final String sourceNetworkId = sourceSubnet == null ? null : sourceSubnet.getNetworkUUID();
457         if (sourceNetworkId == null) {
458             logger.error("Could not get provider Network ID from subnet {}", sourceSubnetId);
459             return;
460         }
461
462         final NeutronNetwork sourceNetwork = neutronNetworkCache.getNetwork(sourceNetworkId);
463         if (sourceNetwork == null) {
464             logger.error("Could not get provider Network for Network ID {}", sourceNetworkId);
465             return;
466         }
467
468         if (! sourceNetwork.getTenantID().equals(dstNeutronNetwork.getTenantID())) {
469             // Isolate subnets from different tenants within the same router
470             return;
471         }
472         final String sourceSegmentationId = sourceNetwork.getProviderSegmentationID();
473         if (sourceSegmentationId == null) {
474             logger.error("Could not get provider Segmentation ID for Subnet {}", sourceSubnetId);
475             return;
476         }
477         if (sourceSegmentationId.equals(destinationSegmentationId)) {
478             // Skip 'self'
479             return;
480         }
481
482         programRouterInterfaceStage1(node, dpid, sourceSegmentationId, destinationSegmentationId,
483                                      dstMacAddress, destIpStr, destMask, actionForNode);
484
485         // Flip roles src->dst; dst->src
486         if (isReflexsive) {
487             final NeutronPort sourceNeutronPort = neutronPortCache.getPort(srcNeutronRouterInterface.getPortUUID());
488             final String macAddress2 = sourceNeutronPort != null ? sourceNeutronPort.getMacAddress() : null;
489             final List<Neutron_IPs> ipList2 = sourceNeutronPort != null ? sourceNeutronPort.getFixedIPs() : null;
490             final String cidr2 = sourceSubnet.getCidr();
491             final int mask2 = getMaskLenFromCidr(cidr2);
492
493             if (cidr2 == null || cidr2.isEmpty() ||
494                 macAddress2 == null || macAddress2.isEmpty() ||
495                 ipList2 == null || ipList2.isEmpty()) {
496                 logger.trace("programFlowsForNeutronRouterInterfacePair reflexive is bailing seg:{} cidr:{} mac:{} ip:{}",
497                              sourceSegmentationId, cidr2, macAddress2, ipList2);
498                 return;  // done: go no further w/out all the info needed...
499             }
500
501             for (Neutron_IPs neutronIP2 : ipList2) {
502                 final String ipStr2 = neutronIP2.getIpAddress();
503                 if (ipStr2.isEmpty()) {
504                     continue;
505                 }
506                 programFlowsForNeutronRouterInterfacePair(node, dpid, dstNeutronRouterInterface,
507                                                           srcNeutronRouterInterface,
508                                                           sourceNetwork, sourceSegmentationId,
509                                                           macAddress2, ipStr2, mask2, actionForNode,
510                                                           false /*isReflexsive*/);
511             }
512         }
513     }
514
515     private void programRouterInterfaceStage1(Node node, Long dpid, String sourceSegmentationId,
516                                               String destinationSegmentationId,
517                                               String macAddress, String ipStr, int mask,
518                                               Action actionForNode) {
519         // Based on the local cache, figure out whether programming needs to occur. To do this, we
520         // will look at desired action for node.
521         //
522         final String cacheKey = node.toString() + ":" + sourceSegmentationId + ":" + destinationSegmentationId + ":" +
523                                 ipStr + "/" + Integer.toString(mask);
524         final Boolean isProgrammed = routerInterfacesCache.contains(cacheKey);
525
526         if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
527             logger.trace("programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
528                          "action {} is already done",
529                          node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
530                          ipStr, mask, actionForNode);
531             return;
532         }
533         if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
534             logger.trace("programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
535                          "action {} is already done",
536                          node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
537                          ipStr, mask, actionForNode);
538             return;
539         }
540
541         Status status = this.programRouterInterfaceStage2(node, dpid, sourceSegmentationId, destinationSegmentationId,
542                                                           macAddress, ipStr, mask, actionForNode);
543         if (status.isSuccess()) {
544             // Update cache
545             if (actionForNode == Action.ADD) {
546                 // TODO: multiTenantAwareRouter.addInterface(UUID.fromString(tenant), ...);
547                 routerInterfacesCache.add(cacheKey);
548             } else {
549                 // TODO: multiTenantAwareRouter.removeInterface(...);
550                 routerInterfacesCache.remove(cacheKey);
551             }
552         }
553     }
554
555     private Status programRouterInterfaceStage2(Node node, Long dpid, String sourceSegmentationId,
556                                                 String destinationSegmentationId,
557                                                 String macAddress,
558                                                 String address, int mask,
559                                                 Action actionForNode) {
560         Status status;
561         try {
562             InetAddress inetAddress = InetAddress.getByName(address);
563             status = routingProvider == null ?
564                      new Status(StatusCode.SUCCESS) :
565                      routingProvider.programRouterInterface(dpid, sourceSegmentationId, destinationSegmentationId,
566                                                             macAddress, inetAddress, mask, actionForNode);
567         } catch (UnknownHostException e) {
568             status = new Status(StatusCode.BADREQUEST);
569         }
570
571         if (status.isSuccess()) {
572             logger.debug("ProgramRouterInterface {} for mac:{} addr:{}/{} node:{} srcTunId:{} destTunId:{} action:{}",
573                          routingProvider == null ? "skipped" : "programmed",
574                          macAddress, address, mask, node, sourceSegmentationId, destinationSegmentationId,
575                          actionForNode);
576         } else {
577             logger.error("ProgramRouterInterface failed for mac:{} addr:{}/{} node:{} srcTunId:{} destTunId:{} action:{} status:{}",
578                          macAddress, address, mask, node, sourceSegmentationId, destinationSegmentationId,
579                          actionForNode, status);
580         }
581         return status;
582     }
583
584     private void programStaticArpStage1(Node node, Long dpid, String providerSegmentationId,
585                                         String macAddress, String ipStr,
586                                         Action actionForNode) {
587         // Based on the local cache, figure out whether programming needs to occur. To do this, we
588         // will look at desired action for node.
589         //
590         final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + ipStr;
591         final Boolean isProgrammed = staticArpEntryCache.contains(cacheKey);
592
593         if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
594             logger.trace("programStaticArpStage1 node {} providerId {} mac {} ip {} action {} is already done",
595                     node.getNodeId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
596             return;
597         }
598         if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
599             logger.trace("programStaticArpStage1 node {} providerId {} mac {} ip {} action {} is already done",
600                     node.getNodeId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
601             return;
602         }
603
604         Status status = this.programStaticArpStage2(node, dpid, providerSegmentationId,
605                                                     macAddress, ipStr, actionForNode);
606         if (status.isSuccess()) {
607             // Update cache
608             if (actionForNode == Action.ADD) {
609                 staticArpEntryCache.add(cacheKey);
610             } else {
611                 staticArpEntryCache.remove(cacheKey);
612             }
613         }
614     }
615
616     private Status programStaticArpStage2(Node node, Long dpid, String providerSegmentationId,
617                                                 String macAddress,
618                                                 String address,
619                                                 Action actionForNode) {
620         Status status;
621         try {
622             InetAddress inetAddress = InetAddress.getByName(address);
623             status = arpProvider == null ?
624                      new Status(StatusCode.SUCCESS) :
625                      arpProvider.programStaticArpEntry(dpid, providerSegmentationId,
626                                                        macAddress, inetAddress, actionForNode);
627         } catch (UnknownHostException e) {
628             status = new Status(StatusCode.BADREQUEST);
629         }
630
631         if (status.isSuccess()) {
632             logger.debug("ProgramStaticArp {} for mac:{} addr:{} node:{} action:{}",
633                          arpProvider == null ? "skipped" : "programmed",
634                          macAddress, address, node, actionForNode);
635         } else {
636             logger.error("ProgramStaticArp failed for mac:{} addr:{} node:{} action:{} status:{}",
637                          macAddress, address, node, actionForNode, status);
638         }
639         return status;
640     }
641
642     private void programIpRewriteExclusionStage1(Node node, Long dpid, String providerSegmentationId,
643                                                  final boolean isInbound, String cidr,
644                                                  Action actionForRewriteExclusion) {
645         // Based on the local cache, figure out whether programming needs to occur. To do this, we
646         // will look at desired action for node.
647         //
648         final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + cidr;
649         final Boolean isProgrammed = isInbound ?
650                                      inboundIpRewriteExclusionCache.contains(cacheKey):
651                                      outboundIpRewriteExclusionCache.contains(cacheKey);
652
653         if (actionForRewriteExclusion == Action.DELETE && isProgrammed == Boolean.FALSE) {
654             logger.trace("programIpRewriteExclusionStage1 node {} providerId {} {} cidr {} action {} is already done",
655                          node.getNodeId().getValue(), providerSegmentationId, isInbound ? "inbound" : "outbound", cidr,
656                          actionForRewriteExclusion);
657             return;
658         }
659         if (actionForRewriteExclusion == Action.ADD && isProgrammed == Boolean.TRUE) {
660             logger.trace("programIpRewriteExclusionStage1 node {} providerId {} {} cidr {} action {} is already done",
661                          node.getNodeId().getValue(), providerSegmentationId, isInbound ? "inbound" : "outbound", cidr,
662                          actionForRewriteExclusion);
663             return;
664         }
665
666         Status status = this.programIpRewriteExclusionStage2(node, dpid, providerSegmentationId, cidr,
667                                                              isInbound, actionForRewriteExclusion);
668         if (status.isSuccess()) {
669             // Update cache
670             if (actionForRewriteExclusion == Action.ADD) {
671                 if (isInbound) {
672                     inboundIpRewriteExclusionCache.add(cacheKey);
673                 } else {
674                     outboundIpRewriteExclusionCache.add(cacheKey);
675                 }
676             } else {
677                 if (isInbound) {
678                     inboundIpRewriteExclusionCache.remove(cacheKey);
679                 } else {
680                     outboundIpRewriteExclusionCache.remove(cacheKey);
681                 }
682             }
683         }
684     }
685
686     private Status programIpRewriteExclusionStage2(Node node, Long dpid, String providerSegmentationId, String cidr,
687                                                    final boolean isInbound, Action actionForNode) {
688         Status status;
689         if (isInbound) {
690             status = inboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
691                      inboundNatProvider.programIpRewriteExclusion(dpid, providerSegmentationId, cidr,
692                                                                   actionForNode);
693         } else {
694             status = outboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
695                      outboundNatProvider.programIpRewriteExclusion(dpid, providerSegmentationId, cidr,
696                                                                    actionForNode);
697         }
698
699         if (status.isSuccess()) {
700             final boolean isSkipped = isInbound ? inboundNatProvider == null : outboundNatProvider == null;
701             logger.debug("IpRewriteExclusion {} {} for cidr:{} node:{} action:{}",
702                          (isInbound ? "inbound" : "outbound"), (isSkipped ? "skipped" : "programmed"),
703                          cidr, node, actionForNode);
704         } else {
705             logger.error("IpRewriteExclusion {} failed for cidr:{} node:{} action:{} status:{}",
706                          (isInbound ? "inbound" : "outbound"), cidr, node, actionForNode, status);
707         }
708         return status;
709     }
710
711     private void programDefaultRouteStage1(Node node, Long dpid, String providerSegmentationId,
712                                            String defaultGatewayMacAddress, String gatewayIp,
713                                            Action actionForNodeDefaultRoute) {
714         // Based on the local cache, figure out whether programming needs to occur. To do this, we
715         // will look at desired action for node.
716         //
717         final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + gatewayIp;
718         final Boolean isProgrammed = defaultRouteCache.contains(cacheKey);
719
720         if (actionForNodeDefaultRoute == Action.DELETE && isProgrammed == Boolean.FALSE) {
721             logger.trace("programDefaultRouteStage1 node {} providerId {} mac {} gw {} action {} is already done",
722                          node.getNodeId().getValue(), providerSegmentationId, defaultGatewayMacAddress, gatewayIp,
723                          actionForNodeDefaultRoute);
724             return;
725         }
726         if (actionForNodeDefaultRoute == Action.ADD && isProgrammed == Boolean.TRUE) {
727             logger.trace("programDefaultRouteStage1 node {} providerId {} mac {} gw {} action {} is already done",
728                          node.getNodeId().getValue(), providerSegmentationId, defaultGatewayMacAddress, gatewayIp,
729                          actionForNodeDefaultRoute);
730             return;
731         }
732
733         Status status = this.programDefaultRouteStage2(node, dpid, providerSegmentationId,
734                                                        defaultGatewayMacAddress, gatewayIp, actionForNodeDefaultRoute);
735         if (status.isSuccess()) {
736             // Update cache
737             if (actionForNodeDefaultRoute == Action.ADD) {
738                 defaultRouteCache.add(cacheKey);
739             } else {
740                 defaultRouteCache.remove(cacheKey);
741             }
742         }
743     }
744
745     private Status programDefaultRouteStage2(Node node, Long dpid, String providerSegmentationId,
746                                           String defaultGatewayMacAddress,
747                                           String gatewayIp,
748                                           Action actionForNodeDefaultRoute) {
749         // TODO: As of Helium, mac address for default gateway is required (bug 1705).
750         if (defaultGatewayMacAddress == null) {
751             logger.error("ProgramDefaultRoute mac not provided. gatewayIp:{} node:{} action:{}",
752                          gatewayIp, node, actionForNodeDefaultRoute);
753             return new Status(StatusCode.NOTIMPLEMENTED);  // Bug 1705
754         }
755
756         Status status;
757         try {
758             InetAddress inetAddress = InetAddress.getByName(gatewayIp);
759             status = routingProvider == null ?
760                      new Status(StatusCode.SUCCESS) :
761                      routingProvider.programDefaultRouteEntry(dpid, providerSegmentationId,
762                                                               defaultGatewayMacAddress, inetAddress,
763                                                               actionForNodeDefaultRoute);
764         } catch (UnknownHostException e) {
765             status = new Status(StatusCode.BADREQUEST);
766         }
767
768         if (status.isSuccess()) {
769             logger.debug("ProgramDefaultRoute {} for mac:{} gatewayIp:{} node:{} action:{}",
770                          routingProvider == null ? "skipped" : "programmed",
771                          defaultGatewayMacAddress, gatewayIp, node, actionForNodeDefaultRoute);
772         } else {
773             logger.error("ProgramDefaultRoute failed for mac:{} gatewayIp:{} node:{} action:{} status:{}",
774                          defaultGatewayMacAddress, gatewayIp, node, actionForNodeDefaultRoute, status);
775         }
776         return status;
777     }
778
779     private void programFlowsForFloatingIP(final NeutronFloatingIP neutronFloatingIP, Boolean isDelete) {
780         Preconditions.checkNotNull(neutronFloatingIP);
781
782         final String networkUUID = neutronFloatingIP.getFloatingNetworkUUID();
783         final String routerMacAddress = networkIdToRouterMacCache.get(networkUUID);
784
785         // If there is no router interface handling the networkUUID, we are done
786         if (routerMacAddress == null || routerMacAddress.isEmpty()) {
787             return;
788         }
789
790         final NeutronNetwork neutronNetwork = neutronNetworkCache.getNetwork(networkUUID);
791         final String providerSegmentationId = neutronNetwork != null ?
792                                               neutronNetwork.getProviderSegmentationID() : null;
793         final String fixedIPAddress = neutronFloatingIP.getFixedIPAddress();
794         final String floatingIpAddress = neutronFloatingIP.getFloatingIPAddress();
795
796         if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
797             // routerMacAddress == null || routerMacAddress.isEmpty() ||
798             fixedIPAddress == null || fixedIPAddress.isEmpty() ||
799             floatingIpAddress == null || floatingIpAddress.isEmpty()) {
800             return;  // done: go no further w/out all the info needed...
801         }
802
803         final Action action = isDelete ? Action.DELETE : Action.ADD;
804         List<Node> nodes = connectionService.getBridgeNodes();
805         if (nodes.isEmpty()) {
806             logger.trace("programFlowsForFloatingIP has no nodes to work with");
807         }
808         for (Node node : nodes) {
809             final Long dpid = getDpid(node);
810             final Action actionForNode =
811                     tenantNetworkManager.isTenantNetworkPresentInNode(node, providerSegmentationId) ?
812                     action : Action.DELETE;
813
814             // Rewrite from float to fixed and vice-versa
815             //
816             programIpRewriteStage1(node, dpid, providerSegmentationId, true /* isInbound */,
817                                    floatingIpAddress, fixedIPAddress, actionForNode);
818             programIpRewriteStage1(node, dpid, providerSegmentationId, false /* isInboubd */,
819                                    fixedIPAddress, floatingIpAddress, actionForNode);
820
821             // Respond to arps for the floating ip address
822             //
823             programStaticArpStage1(node, dpid, providerSegmentationId, routerMacAddress, floatingIpAddress,
824                                    actionForNode);
825         }
826     }
827
828     private void programIpRewriteStage1(Node node, Long dpid, String providerSegmentationId,
829                                         final boolean isInbound,
830                                         String matchAddress, String rewriteAddress,
831                                         Action actionForNode) {
832         // Based on the local cache, figure out whether programming needs to occur. To do this, we
833         // will look at desired action for node.
834         //
835         final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" +
836                                 matchAddress + ":" + rewriteAddress;
837         final Boolean isProgrammed = isInbound ?
838                                      inboundIpRewriteCache.contains(cacheKey) :
839                                      outboundIpRewriteCache.contains(cacheKey);
840
841         if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
842             logger.trace("programIpRewriteStage1 node {} providerId {} {} matchAddr {} rewriteAddr {} action {}" +
843                          " is already done",
844                          node.getNodeId().getValue(), providerSegmentationId, isInbound ? "inbound": "outbound",
845                          matchAddress, rewriteAddress, actionForNode);
846             return;
847         }
848         if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
849             logger.trace("programIpRewriteStage1 node {} providerId {} {} matchAddr {} rewriteAddr {} action {}" +
850                          " is already done",
851                          node.getNodeId().getValue(), providerSegmentationId, isInbound ? "inbound": "outbound",
852                          matchAddress, rewriteAddress, actionForNode);
853             return;
854         }
855
856         Status status = this.programIpRewriteStage2(node, dpid, providerSegmentationId, isInbound,
857                                                     matchAddress, rewriteAddress, actionForNode);
858         if (status.isSuccess()) {
859             // Update cache
860             if (actionForNode == Action.ADD) {
861                 if (isInbound) {
862                     inboundIpRewriteCache.add(cacheKey);
863                 } else {
864                     outboundIpRewriteCache.add(cacheKey);
865                 }
866             } else {
867                 if (isInbound) {
868                     inboundIpRewriteCache.remove(cacheKey);
869                 } else {
870                     outboundIpRewriteCache.remove(cacheKey);
871                 }
872             }
873         }
874     }
875
876     private Status programIpRewriteStage2(Node node, Long dpid, String providerSegmentationId,
877                                           final boolean isInbound,
878                                           String matchAddress, String rewriteAddress,
879                                           Action actionForNode) {
880         Status status;
881         try {
882             InetAddress inetMatchAddress = InetAddress.getByName(matchAddress);
883             InetAddress inetRewriteAddress = InetAddress.getByName(rewriteAddress);
884             if (isInbound) {
885                 status = inboundNatProvider == null ?
886                          new Status(StatusCode.SUCCESS) :
887                          inboundNatProvider.programIpRewriteRule(dpid, providerSegmentationId,
888                                                                  inetMatchAddress, inetRewriteAddress, actionForNode);
889             } else {
890                 status = outboundNatProvider == null ?
891                          new Status(StatusCode.SUCCESS) :
892                          outboundNatProvider.programIpRewriteRule(dpid, providerSegmentationId,
893                                                                   inetMatchAddress, inetRewriteAddress, actionForNode);
894             }
895         } catch (UnknownHostException e) {
896             status = new Status(StatusCode.BADREQUEST);
897         }
898
899         if (status.isSuccess()) {
900             final boolean isSkipped = isInbound ? inboundNatProvider == null : outboundNatProvider == null;
901             logger.debug("ProgramIpRewrite {} {} for match:{} rewrite:{} node:{} action:{}",
902                          (isInbound ? "inbound" : "outbound"), (isSkipped ? "skipped" : "programmed"),
903                          matchAddress, rewriteAddress, node, actionForNode);
904         } else {
905             logger.error("ProgramIpRewrite {} failed for match:{} rewrite:{} node:{} action:{} status:{}",
906                          (isInbound ? "inbound" : "outbound"),
907                          matchAddress, rewriteAddress, node, actionForNode, status);
908         }
909         return status;
910     }
911
912     private int getMaskLenFromCidr(String cidr) {
913         if (cidr == null) return 0;
914         String[] splits = cidr.split("/");
915         if (splits.length != 2) return 0;
916
917         int result;
918         try {
919             result = Integer.parseInt(splits[1].trim());
920         }
921         catch (NumberFormatException nfe)
922         {
923             result = 0;
924         }
925         return result;
926     }
927
928     private Long getDpid(Node node) {
929         /* TODO SB_MIGRATION */
930         // may need to go from OvsdbNode to BridgeNode
931         // get integration bridge on this node and then get dpid
932         return MdsalUtils.getDataPathId(node);
933     }
934 }