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.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;
42 import com.google.common.base.Preconditions;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
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;
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.
60 public class NeutronL3Adapter {
65 static final Logger logger = LoggerFactory.getLogger(NeutronL3Adapter.class);
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;
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;
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<>();
108 // Callbacks from OVSDB's northbound handlers
111 public void handleNeutronSubnetEvent(final NeutronSubnet subnet, Action action) {
112 logger.debug("Neutron subnet {} event : {}", action, subnet.toString());
115 public void handleNeutronPortEvent(final NeutronPort neutronPort, Action action) {
116 logger.debug("Neutron port {} event : {}", action, neutronPort.toString());
118 final boolean isAdd = action == Action.ADD;
119 final boolean isDelete = action == Action.DELETE;
121 if (isAdd || isDelete) {
122 updateL3ForNeutronPort(neutronPort, null /*neutronRouterInterfaceFilter*/, isDelete);
126 public void handleNeutronRouterEvent(final NeutronRouter neutronRouter, Action action) {
127 logger.debug("Neutron router {} event : {}", action, neutronRouter.toString());
130 public void handleNeutronRouterInterfaceEvent(final NeutronRouter neutronRouter,
131 final NeutronRouter_Interface neutronRouterInterface,
133 logger.debug(" Router {} interface {} got event {}. Subnet {}",
134 neutronRouter.getName(),
135 neutronRouterInterface.getPortUUID(),
137 neutronRouterInterface.getSubnetUUID());
139 final boolean isAdd = action == Action.ADD;
140 final boolean isDelete = action == Action.DELETE;
144 routerInterfaceCache.put(neutronRouterInterface.getID(), neutronRouterInterface);
146 this.programFlowsForNeutronRouterInterface(neutronRouterInterface, isDelete, null /*nodeFilter*/);
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
151 if (isAdd || isDelete) {
152 for (NeutronPort neutronPort : neutronPortCache.getAllPorts()) {
153 updateL3ForNeutronPort(neutronPort, neutronRouterInterface, isDelete);
159 routerInterfaceCache.remove(neutronRouterInterface.getID());
163 public void handleNeutronFloatingIPEvent(final NeutronFloatingIP neutronFloatingIP,
165 logger.debug(" Floating IP {} {}<->{}, network uuid {}", action,
166 neutronFloatingIP.getFixedIPAddress(),
167 neutronFloatingIP.getFloatingIPAddress(),
168 neutronFloatingIP.getFloatingNetworkUUID());
170 programFlowsForFloatingIP(neutronFloatingIP, action == Action.DELETE);
173 public void handleNeutronNetworkEvent(final NeutronNetwork neutronNetwork, Action action) {
174 logger.debug("neutronNetwork {}: network: {}", action, neutronNetwork);
178 // Callbacks from OVSDB's southbound handler
180 public void handleInterfaceEvent(final Node node, final Interface intf, final NeutronNetwork neutronNetwork,
182 logger.debug("southbound interface {} node:{} interface:{}, neutronNetwork:{}",
183 action, node, intf.getName(), neutronNetwork);
189 private void updateL3ForNeutronPort(final NeutronPort neutronPort,
190 final NeutronRouter_Interface neutronRouterInterfaceFilter,
191 final boolean isDelete) {
193 final String networkUUID = neutronPort.getNetworkUUID();
194 final String routerMacAddress = networkId2RouterMacCache.get(networkUUID);
196 // If there is no router interface handling the networkUUID, we are done
197 if (routerMacAddress == null || routerMacAddress.isEmpty()) {
201 // If this is the neutron port for the router interface itself, ignore it as well
202 if (routerMacAddress.equalsIgnoreCase(neutronPort.getMacAddress())) {
206 final NeutronNetwork neutronNetwork = neutronNetworkCache.getNetwork(networkUUID);
207 final String providerSegmentationId = neutronNetwork != null ?
208 neutronNetwork.getProviderSegmentationID() : null;
209 final String tenantMac = neutronPort.getMacAddress();
211 if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
212 tenantMac == null || tenantMac.isEmpty()) {
213 return; // done: go no further w/out all the info needed...
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()) {
228 // If router interface was provided, make sure subnetId matches
229 if (neutronRouterInterfaceFilter != null &&
230 !neutronRouterInterfaceFilter.getSubnetUUID().equalsIgnoreCase(neutronIP.getSubnetUUID())) {
235 programL3ForwardingStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr, actionForNode);
237 // Configure distributed ARP responder
238 programStaticArpStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr, actionForNode);
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.
249 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + ipStr;
250 final Boolean isProgrammed = l3ForwardingCache.contains(cacheKey);
252 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE)
254 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE)
257 Status status = this.programL3ForwardingStage2(node, dpid, providerSegmentationId,
258 macAddress, ipStr, actionForNode);
259 if (status.isSuccess()) {
261 if (actionForNode == Action.ADD) {
262 l3ForwardingCache.add(cacheKey);
264 l3ForwardingCache.remove(cacheKey);
269 private Status programL3ForwardingStage2(Node node, Long dpid, String providerSegmentationId,
272 Action actionForNode) {
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);
284 if (status.isSuccess()) {
285 logger.debug("ProgramL3Forwarding {} for mac:{} addr:{} node:{} action:{}",
286 l3ForwardingProvider == null ? "skipped" : "programmed",
287 macAddress, address, node, actionForNode);
289 logger.error("ProgramL3Forwarding failed for mac:{} addr:{} node:{} action:{} status:{}",
290 macAddress, address, node, actionForNode, status);
297 private void programFlowsForNeutronRouterInterface(final NeutronRouter_Interface neutronRouterInterface,
300 Preconditions.checkNotNull(neutronRouterInterface);
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);
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...
322 final Action action = isDelete ? Action.DELETE : Action.ADD;
324 // Keep cache for finding router's mac from network uuid
327 networkId2RouterMacCache.remove(neutronNetwork.getNetworkUUID());
329 networkId2RouterMacCache.put(neutronNetwork.getNetworkUUID(), macAddress);
332 List<Node> nodes = connectionService.getNodes();
333 for (Node node : nodes) {
334 if (nodeFilter != null && !(nodeFilter.getID().equals(node.getID()))) {
338 final Long dpid = getDpid(node);
339 final Action actionForNode =
340 tenantNetworkManager.isTenantNetworkPresentInNode(node, providerSegmentationId) ?
341 action : Action.DELETE;
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);
350 // Compute action to be programmed. In the case of rewrite exclusions, we must never program rules
351 // for the external neutron networks.
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);
361 // Default route. For non-external subnets, make sure that there is none configured.
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);
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.
379 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" +
380 ipStr + "/" + Integer.toString(mask);
381 final Boolean isProgrammed = routerInterfacesCache.contains(cacheKey);
383 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE)
385 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE)
388 Status status = this.programRouterInterfaceStage2(node, dpid, providerSegmentationId,
389 macAddress, ipStr, mask, actionForNode);
390 if (status.isSuccess()) {
392 if (actionForNode == Action.ADD) {
393 // TODO: multiTenantAwareRouter.addInterface(UUID.fromString(tenant), ...);
394 routerInterfacesCache.add(cacheKey);
396 // TODO: multiTenantAwareRouter.removeInterface(...);
397 routerInterfacesCache.remove(cacheKey);
402 private Status programRouterInterfaceStage2(Node node, Long dpid, String providerSegmentationId,
404 String address, int mask,
405 Action actionForNode) {
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);
417 if (status.isSuccess()) {
418 logger.debug("ProgramRouterInterface {} for mac:{} addr:{}/{} node:{} action:{}",
419 routingProvider == null ? "skipped" : "programmed",
420 macAddress, address, mask, node, actionForNode);
422 logger.error("ProgramRouterInterface failed for mac:{} addr:{}/{} node:{} action:{} status:{}",
423 macAddress, address, mask, node, actionForNode, status);
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.
434 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + ipStr;
435 final Boolean isProgrammed = staticArpEntryCache.contains(cacheKey);
437 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE)
439 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE)
442 Status status = this.programStaticArpStage2(node, dpid, providerSegmentationId,
443 macAddress, ipStr, actionForNode);
444 if (status.isSuccess()) {
446 if (actionForNode == Action.ADD) {
447 staticArpEntryCache.add(cacheKey);
449 staticArpEntryCache.remove(cacheKey);
454 private Status programStaticArpStage2(Node node, Long dpid, String providerSegmentationId,
457 Action actionForNode) {
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);
469 if (status.isSuccess()) {
470 logger.debug("ProgramStaticArp {} for mac:{} addr:{} node:{} action:{}",
471 arpProvider == null ? "skipped" : "programmed",
472 macAddress, address, node, actionForNode);
474 logger.error("ProgramStaticArp failed for mac:{} addr:{} node:{} action:{} status:{}",
475 macAddress, address, node, actionForNode, status);
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.
486 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + cidr;
487 final Boolean isProgrammed = isInbound ?
488 inboundIpRewriteExclusionCache.contains(cacheKey):
489 outboundIpRewriteExclusionCache.contains(cacheKey);
491 if (actionForRewriteExclusion == Action.DELETE && isProgrammed == Boolean.FALSE)
493 if (actionForRewriteExclusion == Action.ADD && isProgrammed == Boolean.TRUE)
496 Status status = this.programIpRewriteExclusionStage2(node, dpid, providerSegmentationId, cidr,
497 isInbound, actionForRewriteExclusion);
498 if (status.isSuccess()) {
500 if (actionForRewriteExclusion == Action.ADD) {
502 inboundIpRewriteExclusionCache.add(cacheKey);
504 outboundIpRewriteExclusionCache.add(cacheKey);
508 inboundIpRewriteExclusionCache.remove(cacheKey);
510 outboundIpRewriteExclusionCache.remove(cacheKey);
516 private Status programIpRewriteExclusionStage2(Node node, Long dpid, String providerSegmentationId, String cidr,
517 final boolean isInbound, Action actionForNode) {
520 status = inboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
521 inboundNatProvider.programIpRewriteExclusion(node, dpid, providerSegmentationId, cidr,
524 status = outboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
525 outboundNatProvider.programIpRewriteExclusion(node, dpid, providerSegmentationId, cidr,
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);
535 logger.error("IpRewriteExclusion {} failed for cidr:{} node:{} action:{} status:{}",
536 (isInbound ? "inbound" : "outbound"), cidr, node, actionForNode, status);
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.
547 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + gatewayIp;
548 final Boolean isProgrammed = defaultRouteCache.contains(cacheKey);
550 if (actionForNodeDefaultRoute == Action.DELETE && isProgrammed == Boolean.FALSE)
552 if (actionForNodeDefaultRoute == Action.ADD && isProgrammed == Boolean.TRUE)
555 Status status = this.programDefaultRouteStage2(node, dpid, providerSegmentationId,
556 defaultGatewayMacAddress, gatewayIp, actionForNodeDefaultRoute);
557 if (status.isSuccess()) {
559 if (actionForNodeDefaultRoute == Action.ADD) {
560 defaultRouteCache.add(cacheKey);
562 defaultRouteCache.remove(cacheKey);
567 private Status programDefaultRouteStage2(Node node, Long dpid, String providerSegmentationId,
568 String defaultGatewayMacAddress,
570 Action actionForNodeDefaultRoute) {
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);
583 if (status.isSuccess()) {
584 logger.debug("ProgramDefaultRoute {} for mac:{} gatewayIp:{} node:{} action:{}",
585 routingProvider == null ? "skipped" : "programmed",
586 defaultGatewayMacAddress, gatewayIp, node, actionForNodeDefaultRoute);
588 logger.error("ProgramDefaultRoute failed for mac:{} gatewayIp:{} node:{} action:{} status:{}",
589 defaultGatewayMacAddress, gatewayIp, node, actionForNodeDefaultRoute, status);
594 private void programFlowsForFloatingIP(final NeutronFloatingIP neutronFloatingIP, Boolean isDelete) {
595 Preconditions.checkNotNull(neutronFloatingIP);
597 final String networkUUID = neutronFloatingIP.getFloatingNetworkUUID();
598 final String routerMacAddress = networkId2RouterMacCache.get(networkUUID);
600 // If there is no router interface handling the networkUUID, we are done
601 if (routerMacAddress == null || routerMacAddress.isEmpty()) {
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();
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...
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;
626 // Rewrite from float to fixed and vice-versa
628 programIpRewriteStage1(node, dpid, providerSegmentationId, true /* isInbound */,
629 floatingIpAddress, fixedIPAddress, actionForNode);
630 programIpRewriteStage1(node, dpid, providerSegmentationId, false /* isInboubd */,
631 fixedIPAddress, floatingIpAddress, actionForNode);
633 // Respond to arps for the floating ip address
635 programStaticArpStage1(node, dpid, providerSegmentationId, routerMacAddress, floatingIpAddress,
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.
647 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" +
648 matchAddress + ":" + rewriteAddress;
649 final Boolean isProgrammed = isInbound ?
650 inboundIpRewriteCache.contains(cacheKey) :
651 outboundIpRewriteCache.contains(cacheKey);
653 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE)
655 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE)
658 Status status = this.programIpRewriteStage2(node, dpid, providerSegmentationId, isInbound,
659 matchAddress, rewriteAddress, actionForNode);
660 if (status.isSuccess()) {
662 if (actionForNode == Action.ADD) {
664 inboundIpRewriteCache.add(cacheKey);
666 outboundIpRewriteCache.add(cacheKey);
670 inboundIpRewriteCache.remove(cacheKey);
672 outboundIpRewriteCache.remove(cacheKey);
678 private Status programIpRewriteStage2(Node node, Long dpid, String providerSegmentationId,
679 final boolean isInbound,
680 String matchAddress, String rewriteAddress,
681 Action actionForNode) {
684 InetAddress inetMatchAddress = InetAddress.getByName(matchAddress);
685 InetAddress inetRewriteAddress = InetAddress.getByName(rewriteAddress);
687 status = inboundNatProvider == null ?
688 new Status(StatusCode.SUCCESS) :
689 inboundNatProvider.programIpRewriteRule(node, dpid, providerSegmentationId,
690 inetMatchAddress, inetRewriteAddress, actionForNode);
692 status = outboundNatProvider == null ?
693 new Status(StatusCode.SUCCESS) :
694 outboundNatProvider.programIpRewriteRule(node, dpid, providerSegmentationId,
695 inetMatchAddress, inetRewriteAddress, actionForNode);
697 } catch (UnknownHostException e) {
698 status = new Status(StatusCode.BADREQUEST);
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);
707 logger.error("ProgramIpRewrite {} failed for match:{} rewrite:{} node:{} action:{} status:{}",
708 (isInbound ? "inbound" : "outbound"),
709 matchAddress, rewriteAddress, node, actionForNode, status);
718 private int getMaskLenFromCidr(String cidr) {
719 if (cidr == null) return 0;
720 String[] splits = cidr.split("/");
721 if (splits.length != 2) return 0;
725 result = Integer.parseInt(splits[1].trim());
727 catch (NumberFormatException nfe)
734 private Long getDpid (Node node) {
735 Preconditions.checkNotNull(ovsdbConfigurationService);
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);
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);
757 private String getInternalBridgeUUID (Node node, String bridgeName) {
758 Preconditions.checkNotNull(ovsdbConfigurationService);
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;
768 } catch (Exception e) {
769 logger.error("Error getting Bridge Identifier for {} / {}", node, bridgeName, e);
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;