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