2 * Copyright (C) 2014 Red Hat, Inc.
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
8 * Authors : Dave Tucker, Flavio Fernandes
11 package org.opendaylight.ovsdb.openstack.netvirt.impl;
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;
43 import com.google.common.base.Preconditions;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
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;
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.
61 public class NeutronL3Adapter {
66 static final Logger logger = LoggerFactory.getLogger(NeutronL3Adapter.class);
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;
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;
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<>();
109 // Callbacks from OVSDB's northbound handlers
112 public void handleNeutronSubnetEvent(final NeutronSubnet subnet, Action action) {
113 logger.debug("Neutron subnet {} event : {}", action, subnet.toString());
116 public void handleNeutronPortEvent(final NeutronPort neutronPort, Action action) {
117 logger.debug("Neutron port {} event : {}", action, neutronPort.toString());
119 final boolean isAdd = action == Action.ADD;
120 final boolean isDelete = action == Action.DELETE;
124 updateL3ForNeutronPort(neutronPort, null /*neutronRouterInterfaceFilter*/, false /*isDelete*/);
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
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());
137 this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
143 updateL3ForNeutronPort(neutronPort, null /*neutronRouterInterfaceFilter*/, true /*isDelete*/);
147 public void handleNeutronRouterEvent(final NeutronRouter neutronRouter, Action action) {
148 logger.debug("Neutron router {} event : {}", action, neutronRouter.toString());
151 public void handleNeutronRouterInterfaceEvent(final NeutronRouter neutronRouter,
152 final NeutronRouter_Interface neutronRouterInterface,
154 logger.debug(" Router {} interface {} got event {}. Subnet {}",
155 neutronRouter != null ? neutronRouter.getName() : "-",
156 neutronRouterInterface.getPortUUID(),
158 neutronRouterInterface.getSubnetUUID());
160 final boolean isAdd = action == Action.ADD;
161 final boolean isDelete = action == Action.DELETE;
165 routerInterfaceCache.put(neutronRouterInterface.getID(), neutronRouterInterface);
167 this.programFlowsForNeutronRouterInterface(neutronRouterInterface, isDelete, null /*nodeFilter*/);
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
172 if (isAdd || isDelete) {
173 for (NeutronPort neutronPort : neutronPortCache.getAllPorts()) {
174 updateL3ForNeutronPort(neutronPort, neutronRouterInterface, isDelete);
180 routerInterfaceCache.remove(neutronRouterInterface.getID());
184 public void handleNeutronFloatingIPEvent(final NeutronFloatingIP neutronFloatingIP,
186 logger.debug(" Floating IP {} {}<->{}, network uuid {}", action,
187 neutronFloatingIP.getFixedIPAddress(),
188 neutronFloatingIP.getFloatingIPAddress(),
189 neutronFloatingIP.getFloatingNetworkUUID());
191 programFlowsForFloatingIP(neutronFloatingIP, action == Action.DELETE);
194 public void handleNeutronNetworkEvent(final NeutronNetwork neutronNetwork, Action action) {
195 logger.debug("neutronNetwork {}: network: {}", action, neutronNetwork);
199 // Callbacks from OVSDB's southbound handler
201 public void handleInterfaceEvent(final Node node, final Interface intf, final NeutronNetwork neutronNetwork,
203 logger.debug("southbound interface {} node:{} interface:{}, neutronNetwork:{}",
204 action, node, intf.getName(), neutronNetwork);
206 // Deletes are handled in this.handleNeutronPortEvent()
207 if (action == Action.DELETE) {
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) {
215 String neutronPortId = externalIds.get(Constants.EXTERNAL_ID_INTERFACE_ID);
216 if (neutronPortId == null) {
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);
225 updateL3ForNeutronPort(neutronPort, null /*neutronRouterInterfaceFilter*/, false /*isDelete*/);
231 private void updateL3ForNeutronPort(final NeutronPort neutronPort,
232 final NeutronRouter_Interface neutronRouterInterfaceFilter,
233 final boolean isDelete) {
235 final String networkUUID = neutronPort.getNetworkUUID();
236 final String routerMacAddress = networkId2RouterMacCache.get(networkUUID);
238 // If there is no router interface handling the networkUUID, we are done
239 if (routerMacAddress == null || routerMacAddress.isEmpty()) {
243 // If this is the neutron port for the router interface itself, ignore it as well
244 if (routerMacAddress.equalsIgnoreCase(neutronPort.getMacAddress())) {
248 final NeutronNetwork neutronNetwork = neutronNetworkCache.getNetwork(networkUUID);
249 final String providerSegmentationId = neutronNetwork != null ?
250 neutronNetwork.getProviderSegmentationID() : null;
251 final String tenantMac = neutronPort.getMacAddress();
253 if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
254 tenantMac == null || tenantMac.isEmpty()) {
255 return; // done: go no further w/out all the info needed...
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()) {
270 // If router interface was provided, make sure subnetId matches
271 if (neutronRouterInterfaceFilter != null &&
272 !neutronRouterInterfaceFilter.getSubnetUUID().equalsIgnoreCase(neutronIP.getSubnetUUID())) {
277 programL3ForwardingStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr, actionForNode);
279 // Configure distributed ARP responder
280 programStaticArpStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr, actionForNode);
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.
291 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + ipStr;
292 final Boolean isProgrammed = l3ForwardingCache.contains(cacheKey);
294 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE)
296 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE)
299 Status status = this.programL3ForwardingStage2(node, dpid, providerSegmentationId,
300 macAddress, ipStr, actionForNode);
301 if (status.isSuccess()) {
303 if (actionForNode == Action.ADD) {
304 l3ForwardingCache.add(cacheKey);
306 l3ForwardingCache.remove(cacheKey);
311 private Status programL3ForwardingStage2(Node node, Long dpid, String providerSegmentationId,
314 Action actionForNode) {
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);
326 if (status.isSuccess()) {
327 logger.debug("ProgramL3Forwarding {} for mac:{} addr:{} node:{} action:{}",
328 l3ForwardingProvider == null ? "skipped" : "programmed",
329 macAddress, address, node, actionForNode);
331 logger.error("ProgramL3Forwarding failed for mac:{} addr:{} node:{} action:{} status:{}",
332 macAddress, address, node, actionForNode, status);
339 private void programFlowsForNeutronRouterInterface(final NeutronRouter_Interface neutronRouterInterface,
342 Preconditions.checkNotNull(neutronRouterInterface);
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);
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...
364 final Action action = isDelete ? Action.DELETE : Action.ADD;
366 // Keep cache for finding router's mac from network uuid
369 networkId2RouterMacCache.remove(neutronNetwork.getNetworkUUID());
371 networkId2RouterMacCache.put(neutronNetwork.getNetworkUUID(), macAddress);
374 List<Node> nodes = connectionService.getNodes();
375 for (Node node : nodes) {
376 if (nodeFilter != null && !(nodeFilter.getID().equals(node.getID()))) {
380 final Long dpid = getDpid(node);
381 final Action actionForNode =
382 tenantNetworkManager.isTenantNetworkPresentInNode(node, providerSegmentationId) ?
383 action : Action.DELETE;
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);
392 // Compute action to be programmed. In the case of rewrite exclusions, we must never program rules
393 // for the external neutron networks.
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);
403 // Default route. For non-external subnets, make sure that there is none configured.
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);
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.
421 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" +
422 ipStr + "/" + Integer.toString(mask);
423 final Boolean isProgrammed = routerInterfacesCache.contains(cacheKey);
425 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE)
427 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE)
430 Status status = this.programRouterInterfaceStage2(node, dpid, providerSegmentationId,
431 macAddress, ipStr, mask, actionForNode);
432 if (status.isSuccess()) {
434 if (actionForNode == Action.ADD) {
435 // TODO: multiTenantAwareRouter.addInterface(UUID.fromString(tenant), ...);
436 routerInterfacesCache.add(cacheKey);
438 // TODO: multiTenantAwareRouter.removeInterface(...);
439 routerInterfacesCache.remove(cacheKey);
444 private Status programRouterInterfaceStage2(Node node, Long dpid, String providerSegmentationId,
446 String address, int mask,
447 Action actionForNode) {
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);
459 if (status.isSuccess()) {
460 logger.debug("ProgramRouterInterface {} for mac:{} addr:{}/{} node:{} action:{}",
461 routingProvider == null ? "skipped" : "programmed",
462 macAddress, address, mask, node, actionForNode);
464 logger.error("ProgramRouterInterface failed for mac:{} addr:{}/{} node:{} action:{} status:{}",
465 macAddress, address, mask, node, actionForNode, status);
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.
476 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + ipStr;
477 final Boolean isProgrammed = staticArpEntryCache.contains(cacheKey);
479 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE)
481 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE)
484 Status status = this.programStaticArpStage2(node, dpid, providerSegmentationId,
485 macAddress, ipStr, actionForNode);
486 if (status.isSuccess()) {
488 if (actionForNode == Action.ADD) {
489 staticArpEntryCache.add(cacheKey);
491 staticArpEntryCache.remove(cacheKey);
496 private Status programStaticArpStage2(Node node, Long dpid, String providerSegmentationId,
499 Action actionForNode) {
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);
511 if (status.isSuccess()) {
512 logger.debug("ProgramStaticArp {} for mac:{} addr:{} node:{} action:{}",
513 arpProvider == null ? "skipped" : "programmed",
514 macAddress, address, node, actionForNode);
516 logger.error("ProgramStaticArp failed for mac:{} addr:{} node:{} action:{} status:{}",
517 macAddress, address, node, actionForNode, status);
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.
528 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + cidr;
529 final Boolean isProgrammed = isInbound ?
530 inboundIpRewriteExclusionCache.contains(cacheKey):
531 outboundIpRewriteExclusionCache.contains(cacheKey);
533 if (actionForRewriteExclusion == Action.DELETE && isProgrammed == Boolean.FALSE)
535 if (actionForRewriteExclusion == Action.ADD && isProgrammed == Boolean.TRUE)
538 Status status = this.programIpRewriteExclusionStage2(node, dpid, providerSegmentationId, cidr,
539 isInbound, actionForRewriteExclusion);
540 if (status.isSuccess()) {
542 if (actionForRewriteExclusion == Action.ADD) {
544 inboundIpRewriteExclusionCache.add(cacheKey);
546 outboundIpRewriteExclusionCache.add(cacheKey);
550 inboundIpRewriteExclusionCache.remove(cacheKey);
552 outboundIpRewriteExclusionCache.remove(cacheKey);
558 private Status programIpRewriteExclusionStage2(Node node, Long dpid, String providerSegmentationId, String cidr,
559 final boolean isInbound, Action actionForNode) {
562 status = inboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
563 inboundNatProvider.programIpRewriteExclusion(node, dpid, providerSegmentationId, cidr,
566 status = outboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
567 outboundNatProvider.programIpRewriteExclusion(node, dpid, providerSegmentationId, cidr,
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);
577 logger.error("IpRewriteExclusion {} failed for cidr:{} node:{} action:{} status:{}",
578 (isInbound ? "inbound" : "outbound"), cidr, node, actionForNode, status);
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.
589 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + gatewayIp;
590 final Boolean isProgrammed = defaultRouteCache.contains(cacheKey);
592 if (actionForNodeDefaultRoute == Action.DELETE && isProgrammed == Boolean.FALSE)
594 if (actionForNodeDefaultRoute == Action.ADD && isProgrammed == Boolean.TRUE)
597 Status status = this.programDefaultRouteStage2(node, dpid, providerSegmentationId,
598 defaultGatewayMacAddress, gatewayIp, actionForNodeDefaultRoute);
599 if (status.isSuccess()) {
601 if (actionForNodeDefaultRoute == Action.ADD) {
602 defaultRouteCache.add(cacheKey);
604 defaultRouteCache.remove(cacheKey);
609 private Status programDefaultRouteStage2(Node node, Long dpid, String providerSegmentationId,
610 String defaultGatewayMacAddress,
612 Action actionForNodeDefaultRoute) {
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);
625 if (status.isSuccess()) {
626 logger.debug("ProgramDefaultRoute {} for mac:{} gatewayIp:{} node:{} action:{}",
627 routingProvider == null ? "skipped" : "programmed",
628 defaultGatewayMacAddress, gatewayIp, node, actionForNodeDefaultRoute);
630 logger.error("ProgramDefaultRoute failed for mac:{} gatewayIp:{} node:{} action:{} status:{}",
631 defaultGatewayMacAddress, gatewayIp, node, actionForNodeDefaultRoute, status);
636 private void programFlowsForFloatingIP(final NeutronFloatingIP neutronFloatingIP, Boolean isDelete) {
637 Preconditions.checkNotNull(neutronFloatingIP);
639 final String networkUUID = neutronFloatingIP.getFloatingNetworkUUID();
640 final String routerMacAddress = networkId2RouterMacCache.get(networkUUID);
642 // If there is no router interface handling the networkUUID, we are done
643 if (routerMacAddress == null || routerMacAddress.isEmpty()) {
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();
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...
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;
668 // Rewrite from float to fixed and vice-versa
670 programIpRewriteStage1(node, dpid, providerSegmentationId, true /* isInbound */,
671 floatingIpAddress, fixedIPAddress, actionForNode);
672 programIpRewriteStage1(node, dpid, providerSegmentationId, false /* isInboubd */,
673 fixedIPAddress, floatingIpAddress, actionForNode);
675 // Respond to arps for the floating ip address
677 programStaticArpStage1(node, dpid, providerSegmentationId, routerMacAddress, floatingIpAddress,
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.
689 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" +
690 matchAddress + ":" + rewriteAddress;
691 final Boolean isProgrammed = isInbound ?
692 inboundIpRewriteCache.contains(cacheKey) :
693 outboundIpRewriteCache.contains(cacheKey);
695 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE)
697 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE)
700 Status status = this.programIpRewriteStage2(node, dpid, providerSegmentationId, isInbound,
701 matchAddress, rewriteAddress, actionForNode);
702 if (status.isSuccess()) {
704 if (actionForNode == Action.ADD) {
706 inboundIpRewriteCache.add(cacheKey);
708 outboundIpRewriteCache.add(cacheKey);
712 inboundIpRewriteCache.remove(cacheKey);
714 outboundIpRewriteCache.remove(cacheKey);
720 private Status programIpRewriteStage2(Node node, Long dpid, String providerSegmentationId,
721 final boolean isInbound,
722 String matchAddress, String rewriteAddress,
723 Action actionForNode) {
726 InetAddress inetMatchAddress = InetAddress.getByName(matchAddress);
727 InetAddress inetRewriteAddress = InetAddress.getByName(rewriteAddress);
729 status = inboundNatProvider == null ?
730 new Status(StatusCode.SUCCESS) :
731 inboundNatProvider.programIpRewriteRule(node, dpid, providerSegmentationId,
732 inetMatchAddress, inetRewriteAddress, actionForNode);
734 status = outboundNatProvider == null ?
735 new Status(StatusCode.SUCCESS) :
736 outboundNatProvider.programIpRewriteRule(node, dpid, providerSegmentationId,
737 inetMatchAddress, inetRewriteAddress, actionForNode);
739 } catch (UnknownHostException e) {
740 status = new Status(StatusCode.BADREQUEST);
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);
749 logger.error("ProgramIpRewrite {} failed for match:{} rewrite:{} node:{} action:{} status:{}",
750 (isInbound ? "inbound" : "outbound"),
751 matchAddress, rewriteAddress, node, actionForNode, status);
760 private int getMaskLenFromCidr(String cidr) {
761 if (cidr == null) return 0;
762 String[] splits = cidr.split("/");
763 if (splits.length != 2) return 0;
767 result = Integer.parseInt(splits[1].trim());
769 catch (NumberFormatException nfe)
776 private Long getDpid (Node node) {
777 Preconditions.checkNotNull(ovsdbConfigurationService);
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);
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);
799 private String getInternalBridgeUUID (Node node, String bridgeName) {
800 Preconditions.checkNotNull(ovsdbConfigurationService);
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;
810 } catch (Exception e) {
811 logger.error("Error getting Bridge Identifier for {} / {}", node, bridgeName, e);
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;