2884bab77ce288c8a063d3edaa392f4f340aa0a8
[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.controller.networkconfig.neutron.INeutronNetworkCRUD;
14 import org.opendaylight.controller.networkconfig.neutron.INeutronPortCRUD;
15 import org.opendaylight.controller.networkconfig.neutron.INeutronSubnetCRUD;
16 import org.opendaylight.controller.networkconfig.neutron.NeutronFloatingIP;
17 import org.opendaylight.controller.networkconfig.neutron.NeutronNetwork;
18 import org.opendaylight.controller.networkconfig.neutron.NeutronPort;
19 import org.opendaylight.controller.networkconfig.neutron.NeutronRouter;
20 import org.opendaylight.controller.networkconfig.neutron.NeutronRouter_Interface;
21 import org.opendaylight.controller.networkconfig.neutron.NeutronSubnet;
22 import org.opendaylight.controller.networkconfig.neutron.Neutron_IPs;
23 import org.opendaylight.controller.sal.core.Node;
24 import org.opendaylight.controller.sal.utils.HexEncode;
25 import org.opendaylight.controller.sal.utils.Status;
26 import org.opendaylight.controller.sal.utils.StatusCode;
27 import org.opendaylight.ovsdb.lib.notation.Row;
28 import org.opendaylight.ovsdb.openstack.netvirt.api.Constants;
29 import org.opendaylight.ovsdb.openstack.netvirt.api.MultiTenantAwareRouter;
30 import org.opendaylight.ovsdb.openstack.netvirt.api.NetworkingProviderManager;
31 import org.opendaylight.ovsdb.openstack.netvirt.api.TenantNetworkManager;
32 import org.opendaylight.ovsdb.plugin.api.OvsdbConfigurationService;
33 import org.opendaylight.ovsdb.plugin.api.OvsdbConnectionService;
34 import org.opendaylight.ovsdb.schema.openvswitch.Bridge;
35 import org.opendaylight.ovsdb.openstack.netvirt.api.Action;
36 import org.opendaylight.ovsdb.openstack.netvirt.api.ArpProvider;
37 import org.opendaylight.ovsdb.openstack.netvirt.api.InboundNatProvider;
38 import org.opendaylight.ovsdb.openstack.netvirt.api.L3ForwardingProvider;
39 import org.opendaylight.ovsdb.openstack.netvirt.api.OutboundNatProvider;
40 import org.opendaylight.ovsdb.openstack.netvirt.api.RoutingProvider;
41 import org.opendaylight.ovsdb.schema.openvswitch.Interface;
42
43 import com.google.common.base.Preconditions;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 import java.net.InetAddress;
48 import java.net.UnknownHostException;
49 import java.util.HashMap;
50 import java.util.HashSet;
51 import java.util.Iterator;
52 import java.util.List;
53 import java.util.Map;
54 import java.util.Set;
55
56 /**
57  * Neutron L3 Adapter implements a hub-like adapter for the various Neutron events. Based on
58  * these events, the abstract router callbacks can be generated to the multi-tenant aware router,
59  * as well as the multi-tenant router forwarding provider.
60  */
61 public class NeutronL3Adapter {
62
63     /**
64      * Logger instance.
65      */
66     static final Logger logger = LoggerFactory.getLogger(NeutronL3Adapter.class);
67
68     // The implementation for each of these services is resolved by the OSGi Service Manager
69     private volatile org.opendaylight.ovsdb.openstack.netvirt.api.ConfigurationService configurationService;
70     private volatile TenantNetworkManager tenantNetworkManager;
71     private volatile NetworkingProviderManager networkingProviderManager;
72     private volatile OvsdbConfigurationService ovsdbConfigurationService;
73     private volatile OvsdbConnectionService connectionService;
74     private volatile INeutronNetworkCRUD neutronNetworkCache;
75     private volatile INeutronSubnetCRUD neutronSubnetCache;
76     private volatile INeutronPortCRUD neutronPortCache;
77     private volatile MultiTenantAwareRouter multiTenantAwareRouter;
78     private volatile L3ForwardingProvider l3ForwardingProvider;
79     private volatile InboundNatProvider inboundNatProvider;
80     private volatile OutboundNatProvider outboundNatProvider;
81     private volatile ArpProvider arpProvider;
82     private volatile RoutingProvider routingProvider;
83
84     private Set<String> inboundIpRewriteCache;
85     private Set<String> outboundIpRewriteCache;
86     private Set<String> inboundIpRewriteExclusionCache;
87     private Set<String> outboundIpRewriteExclusionCache;
88     private Set<String> routerInterfacesCache;
89     private Set<String> staticArpEntryCache;
90     private Set<String> l3ForwardingCache;
91     private Set<String> defaultRouteCache;
92     private Map<String, String> networkId2RouterMacCache;
93     private Map<String, NeutronRouter_Interface> routerInterfaceCache;
94
95     void init() {
96         this.inboundIpRewriteCache = new HashSet<>();
97         this.outboundIpRewriteCache = new HashSet<>();
98         this.inboundIpRewriteExclusionCache = new HashSet<>();
99         this.outboundIpRewriteExclusionCache = new HashSet<>();
100         this.routerInterfacesCache = new HashSet<>();
101         this.staticArpEntryCache = new HashSet<>();
102         this.l3ForwardingCache = new HashSet<>();
103         this.defaultRouteCache = new HashSet<>();
104         this.networkId2RouterMacCache = new HashMap<>();
105         this.routerInterfaceCache = new HashMap<>();
106     }
107
108     //
109     // Callbacks from OVSDB's northbound handlers
110     //
111
112     public void handleNeutronSubnetEvent(final NeutronSubnet subnet, Action action) {
113         logger.debug("Neutron subnet {} event : {}", action, subnet.toString());
114     }
115
116     public void handleNeutronPortEvent(final NeutronPort neutronPort, Action action) {
117         logger.debug("Neutron port {} event : {}", action, neutronPort.toString());
118
119         final boolean isAdd = action == Action.ADD;
120         final boolean isDelete = action == Action.DELETE;
121
122         // add 'before'
123         if (isAdd) {
124             updateL3ForNeutronPort(neutronPort, null /*neutronRouterInterfaceFilter*/, false /*isDelete*/);
125         }
126
127         // Also treat the port event as a router interface event iff the port belongs to router. This is a
128         // helper for handling cases when handleNeutronRouterInterfaceEvent is not available
129         //
130         if (neutronPort.getDeviceOwner().equalsIgnoreCase("network:router_interface")) {
131             for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
132                 NeutronRouter_Interface neutronRouterInterface =
133                         new NeutronRouter_Interface(neutronIP.getSubnetUUID(), neutronPort.getPortUUID());
134                 neutronRouterInterface.setID(neutronIP.getSubnetUUID());  // id of router interface to be same as subnet
135                 neutronRouterInterface.setTenantID(neutronPort.getTenantID());
136
137                 this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
138             }
139         }
140
141         // delete 'after'
142         if (isDelete) {
143                 updateL3ForNeutronPort(neutronPort, null /*neutronRouterInterfaceFilter*/, true /*isDelete*/);
144         }
145     }
146
147     public void handleNeutronRouterEvent(final NeutronRouter neutronRouter, Action action) {
148         logger.debug("Neutron router {} event : {}", action, neutronRouter.toString());
149     }
150
151     public void handleNeutronRouterInterfaceEvent(final NeutronRouter neutronRouter,
152                                                   final NeutronRouter_Interface neutronRouterInterface,
153                                                   Action action) {
154         logger.debug(" Router {} interface {} got event {}. Subnet {}",
155                      neutronRouter != null ? neutronRouter.getName() : "-",
156                      neutronRouterInterface.getPortUUID(),
157                      action,
158                      neutronRouterInterface.getSubnetUUID());
159
160         final boolean isAdd = action == Action.ADD;
161         final boolean isDelete = action == Action.DELETE;
162
163         // add 'before'
164         if (isAdd) {
165             routerInterfaceCache.put(neutronRouterInterface.getID(), neutronRouterInterface);
166         }
167         this.programFlowsForNeutronRouterInterface(neutronRouterInterface, isDelete, null /*nodeFilter*/);
168
169         // As neutron router interface is added/removed, we need to iterate through all the neutron ports and
170         // see if they are affected by l3
171         //
172         if (isAdd || isDelete) {
173             for (NeutronPort neutronPort : neutronPortCache.getAllPorts()) {
174                 updateL3ForNeutronPort(neutronPort, neutronRouterInterface, isDelete);
175             }
176         }
177
178         // delete 'after'
179         if (isDelete) {
180             routerInterfaceCache.remove(neutronRouterInterface.getID());
181         }
182     }
183
184     public void handleNeutronFloatingIPEvent(final NeutronFloatingIP neutronFloatingIP,
185                                              Action action) {
186         logger.debug(" Floating IP {} {}<->{}, network uuid {}", action,
187                      neutronFloatingIP.getFixedIPAddress(),
188                      neutronFloatingIP.getFloatingIPAddress(),
189                      neutronFloatingIP.getFloatingNetworkUUID());
190
191         programFlowsForFloatingIP(neutronFloatingIP, action == Action.DELETE);
192     }
193
194     public void handleNeutronNetworkEvent(final NeutronNetwork neutronNetwork, Action action) {
195         logger.debug("neutronNetwork {}: network: {}", action, neutronNetwork);
196     }
197
198     //
199     // Callbacks from OVSDB's southbound handler
200     //
201     public void handleInterfaceEvent(final Node node, final Interface intf, final NeutronNetwork neutronNetwork,
202                                      Action action) {
203         logger.debug("southbound interface {} node:{} interface:{}, neutronNetwork:{}",
204                      action, node, intf.getName(), neutronNetwork);
205
206         // Deletes are handled in this.handleNeutronPortEvent()
207         if (action == Action.DELETE) {
208             return;
209         }
210         // See if there is an external uuid, so we can find the respective neutronPort
211         Map<String, String> externalIds = intf.getExternalIdsColumn().getData();
212         if (externalIds == null) {
213             return;
214         }
215         String neutronPortId = externalIds.get(Constants.EXTERNAL_ID_INTERFACE_ID);
216         if (neutronPortId == null) {
217             return;
218         }
219         final NeutronPort neutronPort = neutronPortCache.getPort(neutronPortId);
220         if (neutronPort == null) {
221             logger.warn("southbound interface {} node:{} interface:{}, neutronNetwork:{} did not find port:{}",
222                          action, node, intf.getName(), neutronNetwork, neutronPortId);
223             return;
224         }
225         updateL3ForNeutronPort(neutronPort, null /*neutronRouterInterfaceFilter*/, false /*isDelete*/);
226     }
227
228     //
229     // Internal helpers
230     //
231     private void updateL3ForNeutronPort(final NeutronPort neutronPort,
232                                         final NeutronRouter_Interface neutronRouterInterfaceFilter,
233                                         final boolean isDelete) {
234
235         final String networkUUID = neutronPort.getNetworkUUID();
236         final String routerMacAddress = networkId2RouterMacCache.get(networkUUID);
237
238         // If there is no router interface handling the networkUUID, we are done
239         if (routerMacAddress == null || routerMacAddress.isEmpty()) {
240             return;
241         }
242
243         // If this is the neutron port for the router interface itself, ignore it as well
244         if (routerMacAddress.equalsIgnoreCase(neutronPort.getMacAddress())) {
245             return;
246         }
247
248         final NeutronNetwork neutronNetwork = neutronNetworkCache.getNetwork(networkUUID);
249         final String providerSegmentationId = neutronNetwork != null ?
250                                               neutronNetwork.getProviderSegmentationID() : null;
251         final String tenantMac = neutronPort.getMacAddress();
252
253         if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
254             tenantMac == null || tenantMac.isEmpty()) {
255             return;  // done: go no further w/out all the info needed...
256         }
257
258         final Action action = isDelete ? Action.DELETE : Action.ADD;
259         List<Node> nodes = connectionService.getNodes();
260         for (Node node : nodes) {
261             final Long dpid = getDpid(node);
262             final Action actionForNode =
263                     tenantNetworkManager.isTenantNetworkPresentInNode(node, providerSegmentationId) ?
264                     action : Action.DELETE;
265             for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
266                 final String tenantIpStr = neutronIP.getIpAddress();
267                 if (tenantIpStr.isEmpty()) {
268                     continue;
269                 }
270                 // If router interface was provided, make sure subnetId matches
271                 if (neutronRouterInterfaceFilter != null &&
272                     !neutronRouterInterfaceFilter.getSubnetUUID().equalsIgnoreCase(neutronIP.getSubnetUUID())) {
273                     continue;
274                 }
275
276                 // Configure L3 fwd
277                 programL3ForwardingStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr, actionForNode);
278
279                 // Configure distributed ARP responder
280                 programStaticArpStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr, actionForNode);
281             }
282         }
283     }
284
285     private void programL3ForwardingStage1(Node node, Long dpid, String providerSegmentationId,
286                                            String macAddress, String ipStr,
287                                            Action actionForNode) {
288         // Based on the local cache, figure out whether programming needs to occur. To do this, we
289         // will look at desired action for node.
290         //
291         final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + ipStr;
292         final Boolean isProgrammed = l3ForwardingCache.contains(cacheKey);
293
294         if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE)
295             return;
296         if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE)
297             return;
298
299         Status status = this.programL3ForwardingStage2(node, dpid, providerSegmentationId,
300                                                        macAddress, ipStr, actionForNode);
301         if (status.isSuccess()) {
302             // Update cache
303             if (actionForNode == Action.ADD) {
304                 l3ForwardingCache.add(cacheKey);
305             } else {
306                 l3ForwardingCache.remove(cacheKey);
307             }
308         }
309     }
310
311     private Status programL3ForwardingStage2(Node node, Long dpid, String providerSegmentationId,
312                                              String macAddress,
313                                              String address,
314                                              Action actionForNode) {
315         Status status;
316         try {
317             InetAddress inetAddress = InetAddress.getByName(address);
318             status = l3ForwardingProvider == null ?
319                      new Status(StatusCode.SUCCESS) :
320                      l3ForwardingProvider.programForwardingTableEntry(node, dpid, providerSegmentationId,
321                                                                       inetAddress, macAddress, actionForNode);
322         } catch (UnknownHostException e) {
323             status = new Status(StatusCode.BADREQUEST);
324         }
325
326         if (status.isSuccess()) {
327             logger.debug("ProgramL3Forwarding {} for mac:{} addr:{} node:{} action:{}",
328                          l3ForwardingProvider == null ? "skipped" : "programmed",
329                          macAddress, address, node, actionForNode);
330         } else {
331             logger.error("ProgramL3Forwarding failed for mac:{} addr:{} node:{} action:{} status:{}",
332                          macAddress, address, node, actionForNode, status);
333         }
334         return status;
335     }
336
337     // --
338
339     private void programFlowsForNeutronRouterInterface(final NeutronRouter_Interface neutronRouterInterface,
340                                                        Boolean isDelete,
341                                                        Node nodeFilter) {
342         Preconditions.checkNotNull(neutronRouterInterface);
343
344         final NeutronPort neutronPort = neutronPortCache.getPort(neutronRouterInterface.getPortUUID());
345         final String macAddress = neutronPort != null ? neutronPort.getMacAddress() : null;
346         final List<Neutron_IPs> ipList = neutronPort != null ? neutronPort.getFixedIPs() : null;
347         final NeutronSubnet subnet = neutronSubnetCache.getSubnet(neutronRouterInterface.getSubnetUUID());
348         final NeutronNetwork neutronNetwork = subnet != null ?
349                                               neutronNetworkCache.getNetwork(subnet.getNetworkUUID()) : null;
350         final String providerSegmentationId = neutronNetwork != null ?
351                                               neutronNetwork.getProviderSegmentationID() : null;
352         final String gatewayIp = subnet != null ? subnet.getGatewayIP() : null;
353         final Boolean isExternal = neutronNetwork != null ? neutronNetwork.getRouterExternal() : Boolean.TRUE;
354         final String cidr = subnet != null ? subnet.getCidr() : null;
355         final int mask = getMaskLenFromCidr(cidr);
356
357         if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
358             cidr == null || cidr.isEmpty() ||
359             macAddress == null || macAddress.isEmpty() ||
360             ipList == null || ipList.isEmpty()) {
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
367         //
368         if (isDelete) {
369             networkId2RouterMacCache.remove(neutronNetwork.getNetworkUUID());
370         } else {
371             networkId2RouterMacCache.put(neutronNetwork.getNetworkUUID(), macAddress);
372         }
373
374         List<Node> nodes = connectionService.getNodes();
375         for (Node node : nodes) {
376             if (nodeFilter != null && !(nodeFilter.getID().equals(node.getID()))) {
377                 continue;
378             }
379
380             final Long dpid = getDpid(node);
381             final Action actionForNode =
382                     tenantNetworkManager.isTenantNetworkPresentInNode(node, providerSegmentationId) ?
383                     action : Action.DELETE;
384
385             for (Neutron_IPs neutronIP : ipList) {
386                 final String ipStr = neutronIP.getIpAddress();
387                 if (ipStr.isEmpty()) continue;
388                 programRouterInterfaceStage1(node, dpid, providerSegmentationId, macAddress, ipStr, mask, actionForNode);
389                 programStaticArpStage1(node, dpid, providerSegmentationId, macAddress, ipStr, actionForNode);
390             }
391
392             // Compute action to be programmed. In the case of rewrite exclusions, we must never program rules
393             // for the external neutron networks.
394             //
395             {
396                 final Action actionForRewriteExclusion = isExternal ? Action.DELETE : actionForNode;
397                 programIpRewriteExclusionStage1(node, dpid, providerSegmentationId, true /* isInbound */,
398                                                 cidr, actionForRewriteExclusion);
399                 programIpRewriteExclusionStage1(node, dpid, providerSegmentationId, false /* isInbound */,
400                                                 cidr, actionForRewriteExclusion);
401             }
402
403             // Default route. For non-external subnets, make sure that there is none configured.
404             //
405             if (gatewayIp != null && !gatewayIp.isEmpty()) {
406                 final Action actionForNodeDefaultRoute =
407                         isExternal ? actionForNode : Action.DELETE;
408                 final String defaultGatewayMacAddress = configurationService.getDefaultGatewayMacAddress(node);
409                 programDefaultRouteStage1(node, dpid, providerSegmentationId, defaultGatewayMacAddress, gatewayIp,
410                                           actionForNodeDefaultRoute);
411             }
412         }
413     }
414
415     private void programRouterInterfaceStage1(Node node, Long dpid, String providerSegmentationId,
416                                               String macAddress, String ipStr, int mask,
417                                               Action actionForNode) {
418         // Based on the local cache, figure out whether programming needs to occur. To do this, we
419         // will look at desired action for node.
420         //
421         final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" +
422                                 ipStr + "/" + Integer.toString(mask);
423         final Boolean isProgrammed = routerInterfacesCache.contains(cacheKey);
424
425         if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE)
426             return;
427         if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE)
428             return;
429
430         Status status = this.programRouterInterfaceStage2(node, dpid, providerSegmentationId,
431                                                           macAddress, ipStr, mask, actionForNode);
432         if (status.isSuccess()) {
433             // Update cache
434             if (actionForNode == Action.ADD) {
435                 // TODO: multiTenantAwareRouter.addInterface(UUID.fromString(tenant), ...);
436                 routerInterfacesCache.add(cacheKey);
437             } else {
438                 // TODO: multiTenantAwareRouter.removeInterface(...);
439                 routerInterfacesCache.remove(cacheKey);
440             }
441         }
442     }
443
444     private Status programRouterInterfaceStage2(Node node, Long dpid, String providerSegmentationId,
445                                                 String macAddress,
446                                                 String address, int mask,
447                                                 Action actionForNode) {
448         Status status;
449         try {
450             InetAddress inetAddress = InetAddress.getByName(address);
451             status = routingProvider == null ?
452                      new Status(StatusCode.SUCCESS) :
453                      routingProvider.programRouterInterface(node, dpid, providerSegmentationId,
454                                                             macAddress, inetAddress, mask, actionForNode);
455         } catch (UnknownHostException e) {
456             status = new Status(StatusCode.BADREQUEST);
457         }
458
459         if (status.isSuccess()) {
460             logger.debug("ProgramRouterInterface {} for mac:{} addr:{}/{} node:{} action:{}",
461                          routingProvider == null ? "skipped" : "programmed",
462                          macAddress, address, mask, node, actionForNode);
463         } else {
464             logger.error("ProgramRouterInterface failed for mac:{} addr:{}/{} node:{} action:{} status:{}",
465                          macAddress, address, mask, node, actionForNode, status);
466         }
467         return status;
468     }
469
470     private void programStaticArpStage1(Node node, Long dpid, String providerSegmentationId,
471                                         String macAddress, String ipStr,
472                                         Action actionForNode) {
473         // Based on the local cache, figure out whether programming needs to occur. To do this, we
474         // will look at desired action for node.
475         //
476         final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + ipStr;
477         final Boolean isProgrammed = staticArpEntryCache.contains(cacheKey);
478
479         if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE)
480             return;
481         if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE)
482             return;
483
484         Status status = this.programStaticArpStage2(node, dpid, providerSegmentationId,
485                                                     macAddress, ipStr, actionForNode);
486         if (status.isSuccess()) {
487             // Update cache
488             if (actionForNode == Action.ADD) {
489                 staticArpEntryCache.add(cacheKey);
490             } else {
491                 staticArpEntryCache.remove(cacheKey);
492             }
493         }
494     }
495
496     private Status programStaticArpStage2(Node node, Long dpid, String providerSegmentationId,
497                                                 String macAddress,
498                                                 String address,
499                                                 Action actionForNode) {
500         Status status;
501         try {
502             InetAddress inetAddress = InetAddress.getByName(address);
503             status = arpProvider == null ?
504                      new Status(StatusCode.SUCCESS) :
505                      arpProvider.programStaticArpEntry(node, dpid, providerSegmentationId,
506                                                        macAddress, inetAddress, actionForNode);
507         } catch (UnknownHostException e) {
508             status = new Status(StatusCode.BADREQUEST);
509         }
510
511         if (status.isSuccess()) {
512             logger.debug("ProgramStaticArp {} for mac:{} addr:{} node:{} action:{}",
513                          arpProvider == null ? "skipped" : "programmed",
514                          macAddress, address, node, actionForNode);
515         } else {
516             logger.error("ProgramStaticArp failed for mac:{} addr:{} node:{} action:{} status:{}",
517                          macAddress, address, node, actionForNode, status);
518         }
519         return status;
520     }
521
522     private void programIpRewriteExclusionStage1(Node node, Long dpid, String providerSegmentationId,
523                                                  final boolean isInbound, String cidr,
524                                                  Action actionForRewriteExclusion) {
525         // Based on the local cache, figure out whether programming needs to occur. To do this, we
526         // will look at desired action for node.
527         //
528         final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + cidr;
529         final Boolean isProgrammed = isInbound ?
530                                      inboundIpRewriteExclusionCache.contains(cacheKey):
531                                      outboundIpRewriteExclusionCache.contains(cacheKey);
532
533         if (actionForRewriteExclusion == Action.DELETE && isProgrammed == Boolean.FALSE)
534             return;
535         if (actionForRewriteExclusion == Action.ADD && isProgrammed == Boolean.TRUE)
536             return;
537
538         Status status = this.programIpRewriteExclusionStage2(node, dpid, providerSegmentationId, cidr,
539                                                              isInbound, actionForRewriteExclusion);
540         if (status.isSuccess()) {
541             // Update cache
542             if (actionForRewriteExclusion == Action.ADD) {
543                 if (isInbound) {
544                     inboundIpRewriteExclusionCache.add(cacheKey);
545                 } else {
546                     outboundIpRewriteExclusionCache.add(cacheKey);
547                 }
548             } else {
549                 if (isInbound) {
550                     inboundIpRewriteExclusionCache.remove(cacheKey);
551                 } else {
552                     outboundIpRewriteExclusionCache.remove(cacheKey);
553                 }
554             }
555         }
556     }
557
558     private Status programIpRewriteExclusionStage2(Node node, Long dpid, String providerSegmentationId, String cidr,
559                                                    final boolean isInbound, Action actionForNode) {
560         Status status;
561         if (isInbound) {
562             status = inboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
563                      inboundNatProvider.programIpRewriteExclusion(node, dpid, providerSegmentationId, cidr,
564                                                                   actionForNode);
565         } else {
566             status = outboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
567                      outboundNatProvider.programIpRewriteExclusion(node, dpid, providerSegmentationId, cidr,
568                                                                    actionForNode);
569         }
570
571         if (status.isSuccess()) {
572             final boolean isSkipped = isInbound ? inboundNatProvider == null : outboundNatProvider == null;
573             logger.debug("IpRewriteExclusion {} {} for cidr:{} node:{} action:{}",
574                          (isInbound ? "inbound" : "outbound"), (isSkipped ? "skipped" : "programmed"),
575                          cidr, node, actionForNode);
576         } else {
577             logger.error("IpRewriteExclusion {} failed for cidr:{} node:{} action:{} status:{}",
578                          (isInbound ? "inbound" : "outbound"), cidr, node, actionForNode, status);
579         }
580         return status;
581     }
582
583     private void programDefaultRouteStage1(Node node, Long dpid, String providerSegmentationId,
584                                            String defaultGatewayMacAddress, String gatewayIp,
585                                            Action actionForNodeDefaultRoute) {
586         // Based on the local cache, figure out whether programming needs to occur. To do this, we
587         // will look at desired action for node.
588         //
589         final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + gatewayIp;
590         final Boolean isProgrammed = defaultRouteCache.contains(cacheKey);
591
592         if (actionForNodeDefaultRoute == Action.DELETE && isProgrammed == Boolean.FALSE)
593             return;
594         if (actionForNodeDefaultRoute == Action.ADD && isProgrammed == Boolean.TRUE)
595             return;
596
597         Status status = this.programDefaultRouteStage2(node, dpid, providerSegmentationId,
598                                                        defaultGatewayMacAddress, gatewayIp, actionForNodeDefaultRoute);
599         if (status.isSuccess()) {
600             // Update cache
601             if (actionForNodeDefaultRoute == Action.ADD) {
602                 defaultRouteCache.add(cacheKey);
603             } else {
604                 defaultRouteCache.remove(cacheKey);
605             }
606         }
607     }
608
609     private Status programDefaultRouteStage2(Node node, Long dpid, String providerSegmentationId,
610                                           String defaultGatewayMacAddress,
611                                           String gatewayIp,
612                                           Action actionForNodeDefaultRoute) {
613         Status status;
614         try {
615             InetAddress inetAddress = InetAddress.getByName(gatewayIp);
616             status = routingProvider == null ?
617                      new Status(StatusCode.SUCCESS) :
618                      routingProvider.programDefaultRouteEntry(node, dpid, providerSegmentationId,
619                                                               defaultGatewayMacAddress, inetAddress,
620                                                               actionForNodeDefaultRoute);
621         } catch (UnknownHostException e) {
622             status = new Status(StatusCode.BADREQUEST);
623         }
624
625         if (status.isSuccess()) {
626             logger.debug("ProgramDefaultRoute {} for mac:{} gatewayIp:{} node:{} action:{}",
627                          routingProvider == null ? "skipped" : "programmed",
628                          defaultGatewayMacAddress, gatewayIp, node, actionForNodeDefaultRoute);
629         } else {
630             logger.error("ProgramDefaultRoute failed for mac:{} gatewayIp:{} node:{} action:{} status:{}",
631                          defaultGatewayMacAddress, gatewayIp, node, actionForNodeDefaultRoute, status);
632         }
633         return status;
634     }
635
636     private void programFlowsForFloatingIP(final NeutronFloatingIP neutronFloatingIP, Boolean isDelete) {
637         Preconditions.checkNotNull(neutronFloatingIP);
638
639         final String networkUUID = neutronFloatingIP.getFloatingNetworkUUID();
640         final String routerMacAddress = networkId2RouterMacCache.get(networkUUID);
641
642         // If there is no router interface handling the networkUUID, we are done
643         if (routerMacAddress == null || routerMacAddress.isEmpty()) {
644             return;
645         }
646
647         final NeutronNetwork neutronNetwork = neutronNetworkCache.getNetwork(networkUUID);
648         final String providerSegmentationId = neutronNetwork != null ?
649                                               neutronNetwork.getProviderSegmentationID() : null;
650         final String fixedIPAddress = neutronFloatingIP.getFixedIPAddress();
651         final String floatingIpAddress = neutronFloatingIP.getFloatingIPAddress();
652
653         if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
654             // routerMacAddress == null || routerMacAddress.isEmpty() ||
655             fixedIPAddress == null || fixedIPAddress.isEmpty() ||
656             floatingIpAddress == null || floatingIpAddress.isEmpty()) {
657             return;  // done: go no further w/out all the info needed...
658         }
659
660         final Action action = isDelete ? Action.DELETE : Action.ADD;
661         List<Node> nodes = connectionService.getNodes();
662         for (Node node : nodes) {
663             final Long dpid = getDpid(node);
664             final Action actionForNode =
665                     tenantNetworkManager.isTenantNetworkPresentInNode(node, providerSegmentationId) ?
666                     action : Action.DELETE;
667
668             // Rewrite from float to fixed and vice-versa
669             //
670             programIpRewriteStage1(node, dpid, providerSegmentationId, true /* isInbound */,
671                                    floatingIpAddress, fixedIPAddress, actionForNode);
672             programIpRewriteStage1(node, dpid, providerSegmentationId, false /* isInboubd */,
673                                    fixedIPAddress, floatingIpAddress, actionForNode);
674
675             // Respond to arps for the floating ip address
676             //
677             programStaticArpStage1(node, dpid, providerSegmentationId, routerMacAddress, floatingIpAddress,
678                                    actionForNode);
679         }
680     }
681
682     private void programIpRewriteStage1(Node node, Long dpid, String providerSegmentationId,
683                                         final boolean isInbound,
684                                         String matchAddress, String rewriteAddress,
685                                         Action actionForNode) {
686         // Based on the local cache, figure out whether programming needs to occur. To do this, we
687         // will look at desired action for node.
688         //
689         final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" +
690                                 matchAddress + ":" + rewriteAddress;
691         final Boolean isProgrammed = isInbound ?
692                                      inboundIpRewriteCache.contains(cacheKey) :
693                                      outboundIpRewriteCache.contains(cacheKey);
694
695         if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE)
696             return;
697         if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE)
698             return;
699
700         Status status = this.programIpRewriteStage2(node, dpid, providerSegmentationId, isInbound,
701                                                     matchAddress, rewriteAddress, actionForNode);
702         if (status.isSuccess()) {
703             // Update cache
704             if (actionForNode == Action.ADD) {
705                 if (isInbound) {
706                     inboundIpRewriteCache.add(cacheKey);
707                 } else {
708                     outboundIpRewriteCache.add(cacheKey);
709                 }
710             } else {
711                 if (isInbound) {
712                     inboundIpRewriteCache.remove(cacheKey);
713                 } else {
714                     outboundIpRewriteCache.remove(cacheKey);
715                 }
716             }
717         }
718     }
719
720     private Status programIpRewriteStage2(Node node, Long dpid, String providerSegmentationId,
721                                           final boolean isInbound,
722                                           String matchAddress, String rewriteAddress,
723                                           Action actionForNode) {
724         Status status;
725         try {
726             InetAddress inetMatchAddress = InetAddress.getByName(matchAddress);
727             InetAddress inetRewriteAddress = InetAddress.getByName(rewriteAddress);
728             if (isInbound) {
729                 status = inboundNatProvider == null ?
730                          new Status(StatusCode.SUCCESS) :
731                          inboundNatProvider.programIpRewriteRule(node, dpid, providerSegmentationId,
732                                                                  inetMatchAddress, inetRewriteAddress, actionForNode);
733             } else {
734                 status = outboundNatProvider == null ?
735                          new Status(StatusCode.SUCCESS) :
736                          outboundNatProvider.programIpRewriteRule(node, dpid, providerSegmentationId,
737                                                                   inetMatchAddress, inetRewriteAddress, actionForNode);
738             }
739         } catch (UnknownHostException e) {
740             status = new Status(StatusCode.BADREQUEST);
741         }
742
743         if (status.isSuccess()) {
744             final boolean isSkipped = isInbound ? inboundNatProvider == null : outboundNatProvider == null;
745             logger.debug("ProgramIpRewrite {} {} for match:{} rewrite:{} node:{} action:{}",
746                          (isInbound ? "inbound" : "outbound"), (isSkipped ? "skipped" : "programmed"),
747                          matchAddress, rewriteAddress, node, actionForNode);
748         } else {
749             logger.error("ProgramIpRewrite {} failed for match:{} rewrite:{} node:{} action:{} status:{}",
750                          (isInbound ? "inbound" : "outbound"),
751                          matchAddress, rewriteAddress, node, actionForNode, status);
752         }
753         return status;
754     }
755
756     //
757     // More Internals
758     //
759
760     private int getMaskLenFromCidr(String cidr) {
761         if (cidr == null) return 0;
762         String[] splits = cidr.split("/");
763         if (splits.length != 2) return 0;
764
765         int result;
766         try {
767             result = Integer.parseInt(splits[1].trim());
768         }
769         catch (NumberFormatException nfe)
770         {
771             result = 0;
772         }
773         return result;
774     }
775
776     private Long getDpid (Node node) {
777         Preconditions.checkNotNull(ovsdbConfigurationService);
778
779         String bridgeName = configurationService.getIntegrationBridgeName();
780         String bridgeUuid = this.getInternalBridgeUUID(node, bridgeName);
781         if (bridgeUuid == null) {
782             logger.error("Unable to spot Bridge Identifier for {} in {}", bridgeName, node);
783             return 0L;
784         }
785
786         try {
787             Row bridgeRow =  ovsdbConfigurationService
788                     .getRow(node, ovsdbConfigurationService.getTableName(node, Bridge.class), bridgeUuid);
789             Bridge bridge = ovsdbConfigurationService.getTypedRow(node, Bridge.class, bridgeRow);
790             Set<String> dpids = bridge.getDatapathIdColumn().getData();
791             if (dpids == null || dpids.size() == 0) return 0L;
792             return HexEncode.stringToLong((String) dpids.toArray()[0]);
793         } catch (Exception e) {
794             logger.error("Error finding Bridge's OF DPID", e);
795             return 0L;
796         }
797     }
798
799     private String getInternalBridgeUUID (Node node, String bridgeName) {
800         Preconditions.checkNotNull(ovsdbConfigurationService);
801         try {
802             Map<String, Row> bridgeTable =
803                     ovsdbConfigurationService.getRows(node,
804                                                       ovsdbConfigurationService.getTableName(node, Bridge.class));
805             if (bridgeTable == null) return null;
806             for (String key : bridgeTable.keySet()) {
807                 Bridge bridge = ovsdbConfigurationService.getTypedRow(node, Bridge.class, bridgeTable.get(key));
808                 if (bridge.getName().equals(bridgeName)) return key;
809             }
810         } catch (Exception e) {
811             logger.error("Error getting Bridge Identifier for {} / {}", node, bridgeName, e);
812         }
813         return null;
814     }
815
816     private NeutronRouter_Interface getNeutronRouterInterface(String subnetUUID) {
817         Iterator it = routerInterfaceCache.entrySet().iterator();
818         while (it.hasNext()) {
819             Map.Entry pairs = (Map.Entry)it.next();
820             NeutronRouter_Interface routerInterface = (NeutronRouter_Interface) pairs.getValue();
821             if (routerInterface.getSubnetUUID().equalsIgnoreCase(subnetUUID)) {
822                 return routerInterface;
823             }
824         }
825         return null;
826     }
827
828 }
829
830