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.neutron.spi.INeutronNetworkCRUD;
14 import org.opendaylight.neutron.spi.INeutronPortCRUD;
15 import org.opendaylight.neutron.spi.INeutronSubnetCRUD;
16 import org.opendaylight.neutron.spi.NeutronFloatingIP;
17 import org.opendaylight.neutron.spi.NeutronNetwork;
18 import org.opendaylight.neutron.spi.NeutronPort;
19 import org.opendaylight.neutron.spi.NeutronRouter;
20 import org.opendaylight.neutron.spi.NeutronRouter_Interface;
21 import org.opendaylight.neutron.spi.NeutronSubnet;
22 import org.opendaylight.neutron.spi.Neutron_IPs;
23 import org.opendaylight.ovsdb.lib.notation.Row;
24 import org.opendaylight.ovsdb.openstack.netvirt.api.ConfigurationService;
25 import org.opendaylight.ovsdb.openstack.netvirt.api.Constants;
26 import org.opendaylight.ovsdb.openstack.netvirt.api.TenantNetworkManager;
27 import org.opendaylight.ovsdb.plugin.api.OvsdbConfigurationService;
28 import org.opendaylight.ovsdb.plugin.api.OvsdbConnectionService;
29 import org.opendaylight.ovsdb.plugin.api.Status;
30 import org.opendaylight.ovsdb.plugin.api.StatusCode;
31 import org.opendaylight.ovsdb.schema.openvswitch.Bridge;
32 import org.opendaylight.ovsdb.openstack.netvirt.api.Action;
33 import org.opendaylight.ovsdb.openstack.netvirt.api.ArpProvider;
34 import org.opendaylight.ovsdb.openstack.netvirt.api.InboundNatProvider;
35 import org.opendaylight.ovsdb.openstack.netvirt.api.L3ForwardingProvider;
36 import org.opendaylight.ovsdb.openstack.netvirt.api.OutboundNatProvider;
37 import org.opendaylight.ovsdb.openstack.netvirt.api.RoutingProvider;
38 import org.opendaylight.ovsdb.schema.openvswitch.Interface;
39 import org.opendaylight.ovsdb.utils.config.ConfigProperties;
40 import org.opendaylight.ovsdb.utils.mdsal.node.StringConvertor;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
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.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 ConfigurationService configurationService;
69 private volatile TenantNetworkManager tenantNetworkManager;
70 private volatile OvsdbConfigurationService ovsdbConfigurationService;
71 private volatile OvsdbConnectionService connectionService;
72 private volatile INeutronNetworkCRUD neutronNetworkCache;
73 private volatile INeutronSubnetCRUD neutronSubnetCache;
74 private volatile INeutronPortCRUD neutronPortCache;
75 private volatile L3ForwardingProvider l3ForwardingProvider;
76 private volatile InboundNatProvider inboundNatProvider;
77 private volatile OutboundNatProvider outboundNatProvider;
78 private volatile ArpProvider arpProvider;
79 private volatile RoutingProvider routingProvider;
81 private Set<String> inboundIpRewriteCache;
82 private Set<String> outboundIpRewriteCache;
83 private Set<String> inboundIpRewriteExclusionCache;
84 private Set<String> outboundIpRewriteExclusionCache;
85 private Set<String> routerInterfacesCache;
86 private Set<String> staticArpEntryCache;
87 private Set<String> l3ForwardingCache;
88 private Set<String> defaultRouteCache;
89 private Map<String, String> networkIdToRouterMacCache;
90 private Map<String, NeutronRouter_Interface> subnetIdToRouterInterfaceCache;
91 private Boolean enabled = false;
94 final String enabledPropertyStr = ConfigProperties.getProperty(this.getClass(), "ovsdb.l3.fwd.enabled");
95 if (enabledPropertyStr != null && enabledPropertyStr.equalsIgnoreCase("yes")) {
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.networkIdToRouterMacCache = new HashMap<>();
105 this.subnetIdToRouterInterfaceCache = new HashMap<>();
108 logger.info("OVSDB L3 forwarding is enabled");
110 logger.debug("OVSDB L3 forwarding is disabled");
115 // Callbacks from OVSDB's northbound handlers
118 public void handleNeutronSubnetEvent(final NeutronSubnet subnet, Action action) {
119 logger.debug("Neutron subnet {} event : {}", action, subnet.toString());
124 public void handleNeutronPortEvent(final NeutronPort neutronPort, Action action) {
125 logger.debug("Neutron port {} event : {}", action, neutronPort.toString());
129 final boolean isDelete = action == Action.DELETE;
131 // Treat the port event as a router interface event if the port belongs to router. This is a
132 // helper for handling cases when handleNeutronRouterInterfaceEvent is not available
134 if (neutronPort.getDeviceOwner().equalsIgnoreCase("network:router_interface")) {
135 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
136 NeutronRouter_Interface neutronRouterInterface =
137 new NeutronRouter_Interface(neutronIP.getSubnetUUID(), neutronPort.getPortUUID());
138 neutronRouterInterface.setID(neutronIP.getSubnetUUID()); // id of router interface to be same as subnet
139 neutronRouterInterface.setTenantID(neutronPort.getTenantID());
141 this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
144 // We made it here, port is not used as a router interface. If this is not a delete action, make sure that
145 // all nodes that are supposed to have a router interface for the port's subnet(s), have it configured. We
146 // need to do this check here because a router interface is not added to a node until tenant becomes needed
150 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
151 NeutronRouter_Interface neutronRouterInterface =
152 subnetIdToRouterInterfaceCache.get(neutronIP.getSubnetUUID());
153 if (neutronRouterInterface != null) {
154 this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
158 this.updateL3ForNeutronPort(neutronPort, isDelete);
162 public void handleNeutronRouterEvent(final NeutronRouter neutronRouter, Action action) {
163 logger.debug("Neutron router {} event : {}", action, neutronRouter.toString());
168 public void handleNeutronRouterInterfaceEvent(final NeutronRouter neutronRouter,
169 final NeutronRouter_Interface neutronRouterInterface,
171 logger.debug("Router interface {} got event {}. Subnet {}",
172 neutronRouterInterface.getPortUUID(),
174 neutronRouterInterface.getSubnetUUID());
178 final boolean isDelete = action == Action.DELETE;
180 this.programFlowsForNeutronRouterInterface(neutronRouterInterface, isDelete);
182 // As neutron router interface is added/removed, we need to iterate through all the neutron ports and
183 // see if they are affected by l3
185 for (NeutronPort neutronPort : neutronPortCache.getAllPorts()) {
186 boolean currPortShouldBeDeleted = false;
187 // Note: delete in this case only applies to 1)router interface delete and 2)ports on the same subnet
189 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
190 if (neutronRouterInterface.getSubnetUUID().equalsIgnoreCase(neutronIP.getSubnetUUID())) {
191 currPortShouldBeDeleted = true;
196 this.updateL3ForNeutronPort(neutronPort, currPortShouldBeDeleted);
200 public void handleNeutronFloatingIPEvent(final NeutronFloatingIP neutronFloatingIP,
202 logger.debug(" Floating IP {} {}<->{}, network uuid {}", action,
203 neutronFloatingIP.getFixedIPAddress(),
204 neutronFloatingIP.getFloatingIPAddress(),
205 neutronFloatingIP.getFloatingNetworkUUID());
209 this.programFlowsForFloatingIP(neutronFloatingIP, action == Action.DELETE);
212 public void handleNeutronNetworkEvent(final NeutronNetwork neutronNetwork, Action action) {
213 logger.debug("neutronNetwork {}: network: {}", action, neutronNetwork);
219 // Callbacks from OVSDB's southbound handler
221 public void handleInterfaceEvent(final Node node, final Interface intf, final NeutronNetwork neutronNetwork,
223 logger.debug("southbound interface {} node:{} interface:{}, neutronNetwork:{}",
224 action, node, intf.getName(), neutronNetwork);
228 // See if there is an external uuid, so we can find the respective neutronPort
229 Map<String, String> externalIds = intf.getExternalIdsColumn().getData();
230 if (externalIds == null) {
233 String neutronPortId = externalIds.get(Constants.EXTERNAL_ID_INTERFACE_ID);
234 if (neutronPortId == null) {
237 final NeutronPort neutronPort = neutronPortCache.getPort(neutronPortId);
238 if (neutronPort == null) {
239 logger.warn("southbound interface {} node:{} interface:{}, neutronNetwork:{} did not find port:{}",
240 action, node, intf.getName(), neutronNetwork, neutronPortId);
243 this.handleNeutronPortEvent(neutronPort, action);
249 private void updateL3ForNeutronPort(final NeutronPort neutronPort, final boolean isDelete) {
251 final String networkUUID = neutronPort.getNetworkUUID();
252 final String routerMacAddress = networkIdToRouterMacCache.get(networkUUID);
254 // If there is no router interface handling the networkUUID, we are done
255 if (routerMacAddress == null || routerMacAddress.isEmpty()) {
259 // If this is the neutron port for the router interface itself, ignore it as well. Ports that represent the
260 // router interface are handled via handleNeutronRouterInterfaceEvent.
261 if (routerMacAddress.equalsIgnoreCase(neutronPort.getMacAddress())) {
265 final NeutronNetwork neutronNetwork = neutronNetworkCache.getNetwork(networkUUID);
266 final String providerSegmentationId = neutronNetwork != null ?
267 neutronNetwork.getProviderSegmentationID() : null;
268 final String tenantMac = neutronPort.getMacAddress();
270 if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
271 tenantMac == null || tenantMac.isEmpty()) {
272 return; // done: go no further w/out all the info needed...
275 final Action action = isDelete ? Action.DELETE : Action.ADD;
276 List<Node> nodes = connectionService.getNodes();
277 if (nodes.isEmpty()) {
278 logger.trace("updateL3ForNeutronPort has no nodes to work with");
280 for (Node node : nodes) {
281 final Long dpid = getDpid(node);
282 final boolean tenantNetworkPresentInNode =
283 tenantNetworkManager.isTenantNetworkPresentInNode(node, providerSegmentationId);
284 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
285 final String tenantIpStr = neutronIP.getIpAddress();
286 if (tenantIpStr.isEmpty()) {
290 // Configure L3 fwd. We do that regardless of tenant network present, because these rules are
291 // still needed when routing to subnets non-local to node (bug 2076).
292 programL3ForwardingStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr, action);
294 // Configure distributed ARP responder. Only needed if tenant network exists in node.
295 programStaticArpStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr,
296 tenantNetworkPresentInNode ? action : Action.DELETE);
301 private void programL3ForwardingStage1(Node node, Long dpid, String providerSegmentationId,
302 String macAddress, String ipStr,
303 Action actionForNode) {
304 // Based on the local cache, figure out whether programming needs to occur. To do this, we
305 // will look at desired action for node.
307 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + ipStr;
308 final Boolean isProgrammed = l3ForwardingCache.contains(cacheKey);
310 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
311 logger.trace("programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {} is already done",
312 node.getId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
315 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
316 logger.trace("programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {} is already done",
317 node.getId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
321 Status status = this.programL3ForwardingStage2(node, dpid, providerSegmentationId,
322 macAddress, ipStr, actionForNode);
323 if (status.isSuccess()) {
325 if (actionForNode == Action.ADD) {
326 l3ForwardingCache.add(cacheKey);
328 l3ForwardingCache.remove(cacheKey);
333 private Status programL3ForwardingStage2(Node node, Long dpid, String providerSegmentationId,
336 Action actionForNode) {
339 InetAddress inetAddress = InetAddress.getByName(address);
340 status = l3ForwardingProvider == null ?
341 new Status(StatusCode.SUCCESS) :
342 l3ForwardingProvider.programForwardingTableEntry(node, dpid, providerSegmentationId,
343 inetAddress, macAddress, actionForNode);
344 } catch (UnknownHostException e) {
345 status = new Status(StatusCode.BADREQUEST);
348 if (status.isSuccess()) {
349 logger.debug("ProgramL3Forwarding {} for mac:{} addr:{} node:{} action:{}",
350 l3ForwardingProvider == null ? "skipped" : "programmed",
351 macAddress, address, node, actionForNode);
353 logger.error("ProgramL3Forwarding failed for mac:{} addr:{} node:{} action:{} status:{}",
354 macAddress, address, node, actionForNode, status);
361 private void programFlowsForNeutronRouterInterface(final NeutronRouter_Interface destNeutronRouterInterface,
363 Preconditions.checkNotNull(destNeutronRouterInterface);
365 final NeutronPort neutronPort = neutronPortCache.getPort(destNeutronRouterInterface.getPortUUID());
366 final String macAddress = neutronPort != null ? neutronPort.getMacAddress() : null;
367 final List<Neutron_IPs> ipList = neutronPort != null ? neutronPort.getFixedIPs() : null;
368 final NeutronSubnet subnet = neutronSubnetCache.getSubnet(destNeutronRouterInterface.getSubnetUUID());
369 final NeutronNetwork neutronNetwork = subnet != null ?
370 neutronNetworkCache.getNetwork(subnet.getNetworkUUID()) : null;
371 final String destinationSegmentationId = neutronNetwork != null ?
372 neutronNetwork.getProviderSegmentationID() : null;
373 final String gatewayIp = subnet != null ? subnet.getGatewayIP() : null;
374 final Boolean isExternal = neutronNetwork != null ? neutronNetwork.getRouterExternal() : Boolean.TRUE;
375 final String cidr = subnet != null ? subnet.getCidr() : null;
376 final int mask = getMaskLenFromCidr(cidr);
378 logger.trace("programFlowsForNeutronRouterInterface called for interface {} isDelete {}",
379 destNeutronRouterInterface, isDelete);
381 if (destinationSegmentationId == null || destinationSegmentationId.isEmpty() ||
382 cidr == null || cidr.isEmpty() ||
383 macAddress == null || macAddress.isEmpty() ||
384 ipList == null || ipList.isEmpty()) {
385 logger.debug("programFlowsForNeutronRouterInterface is bailing seg:{} cidr:{} mac:{} ip:{}",
386 destinationSegmentationId, cidr, macAddress, ipList);
387 return; // done: go no further w/out all the info needed...
390 final Action action = isDelete ? Action.DELETE : Action.ADD;
392 // Keep cache for finding router's mac from network uuid -- add
395 networkIdToRouterMacCache.put(neutronNetwork.getNetworkUUID(), macAddress);
396 subnetIdToRouterInterfaceCache.put(subnet.getSubnetUUID(), destNeutronRouterInterface);
399 List<Node> nodes = connectionService.getNodes();
400 if (nodes.isEmpty()) {
401 logger.trace("programFlowsForNeutronRouterInterface has no nodes to work with");
403 for (Node node : nodes) {
404 final Long dpid = getDpid(node);
405 final Action actionForNode =
406 tenantNetworkManager.isTenantNetworkPresentInNode(node, destinationSegmentationId) ?
407 action : Action.DELETE;
409 for (Neutron_IPs neutronIP : ipList) {
410 final String ipStr = neutronIP.getIpAddress();
411 if (ipStr.isEmpty()) {
412 logger.debug("programFlowsForNeutronRouterInterface is skipping node {} ip {}",
413 node.getId().getValue(), ipStr);
417 // Iterate through all other interfaces and add/remove reflexive flows to this interface
419 for (NeutronRouter_Interface srcNeutronRouterInterface : subnetIdToRouterInterfaceCache.values()) {
420 programFlowsForNeutronRouterInterfacePair(node, dpid,
421 srcNeutronRouterInterface, destNeutronRouterInterface,
422 neutronNetwork, destinationSegmentationId,
423 macAddress, ipStr, mask, actionForNode,
424 true /*isReflexsive*/);
427 programStaticArpStage1(node, dpid, destinationSegmentationId, macAddress, ipStr, actionForNode);
430 // Compute action to be programmed. In the case of rewrite exclusions, we must never program rules
431 // for the external neutron networks.
434 final Action actionForRewriteExclusion = isExternal ? Action.DELETE : actionForNode;
435 programIpRewriteExclusionStage1(node, dpid, destinationSegmentationId, true /* isInbound */,
436 cidr, actionForRewriteExclusion);
437 programIpRewriteExclusionStage1(node, dpid, destinationSegmentationId, false /* isInbound */,
438 cidr, actionForRewriteExclusion);
441 // Default route. For non-external subnet, make sure that there is none configured.
443 if (gatewayIp != null && !gatewayIp.isEmpty()) {
444 final Action actionForNodeDefaultRoute =
445 isExternal ? actionForNode : Action.DELETE;
446 final String defaultGatewayMacAddress = configurationService.getDefaultGatewayMacAddress(node);
447 programDefaultRouteStage1(node, dpid, destinationSegmentationId, defaultGatewayMacAddress, gatewayIp,
448 actionForNodeDefaultRoute);
452 // Keep cache for finding router's mac from network uuid -- remove
455 networkIdToRouterMacCache.remove(neutronNetwork.getNetworkUUID());
456 subnetIdToRouterInterfaceCache.remove(subnet.getSubnetUUID());
460 private void programFlowsForNeutronRouterInterfacePair(final Node node,
462 final NeutronRouter_Interface srcNeutronRouterInterface,
463 final NeutronRouter_Interface dstNeutronRouterInterface,
464 final NeutronNetwork dstNeutronNetwork,
465 final String destinationSegmentationId,
466 final String dstMacAddress,
467 final String destIpStr,
469 final Action actionForNode,
470 Boolean isReflexsive) {
471 Preconditions.checkNotNull(srcNeutronRouterInterface);
472 Preconditions.checkNotNull(dstNeutronRouterInterface);
474 final String sourceSubnetId = srcNeutronRouterInterface.getSubnetUUID();
475 if (sourceSubnetId == null) {
476 logger.error("Could not get provider Subnet ID from router interface {}",
477 srcNeutronRouterInterface.getID());
481 final NeutronSubnet sourceSubnet = neutronSubnetCache.getSubnet(sourceSubnetId);
482 final String sourceNetworkId = sourceSubnet == null ? null : sourceSubnet.getNetworkUUID();
483 if (sourceNetworkId == null) {
484 logger.error("Could not get provider Network ID from subnet {}", sourceSubnetId);
488 final NeutronNetwork sourceNetwork = neutronNetworkCache.getNetwork(sourceNetworkId);
489 if (sourceNetwork == null) {
490 logger.error("Could not get provider Network for Network ID {}", sourceNetworkId);
494 if (! sourceNetwork.getTenantID().equals(dstNeutronNetwork.getTenantID())) {
495 // Isolate subnets from different tenants within the same router
498 final String sourceSegmentationId = sourceNetwork.getProviderSegmentationID();
499 if (sourceSegmentationId == null) {
500 logger.error("Could not get provider Segmentation ID for Subnet {}", sourceSubnetId);
503 if (sourceSegmentationId.equals(destinationSegmentationId)) {
508 programRouterInterfaceStage1(node, dpid, sourceSegmentationId, destinationSegmentationId,
509 dstMacAddress, destIpStr, destMask, actionForNode);
511 // Flip roles src->dst; dst->src
513 final NeutronPort sourceNeutronPort = neutronPortCache.getPort(srcNeutronRouterInterface.getPortUUID());
514 final String macAddress2 = sourceNeutronPort != null ? sourceNeutronPort.getMacAddress() : null;
515 final List<Neutron_IPs> ipList2 = sourceNeutronPort != null ? sourceNeutronPort.getFixedIPs() : null;
516 final String cidr2 = sourceSubnet.getCidr();
517 final int mask2 = getMaskLenFromCidr(cidr2);
519 if (cidr2 == null || cidr2.isEmpty() ||
520 macAddress2 == null || macAddress2.isEmpty() ||
521 ipList2 == null || ipList2.isEmpty()) {
522 logger.trace("programFlowsForNeutronRouterInterfacePair reflexive is bailing seg:{} cidr:{} mac:{} ip:{}",
523 sourceSegmentationId, cidr2, macAddress2, ipList2);
524 return; // done: go no further w/out all the info needed...
527 for (Neutron_IPs neutronIP2 : ipList2) {
528 final String ipStr2 = neutronIP2.getIpAddress();
529 if (ipStr2.isEmpty()) {
532 programFlowsForNeutronRouterInterfacePair(node, dpid, dstNeutronRouterInterface,
533 srcNeutronRouterInterface,
534 sourceNetwork, sourceSegmentationId,
535 macAddress2, ipStr2, mask2, actionForNode,
536 false /*isReflexsive*/);
541 private void programRouterInterfaceStage1(Node node, Long dpid, String sourceSegmentationId,
542 String destinationSegmentationId,
543 String macAddress, String ipStr, int mask,
544 Action actionForNode) {
545 // Based on the local cache, figure out whether programming needs to occur. To do this, we
546 // will look at desired action for node.
548 final String cacheKey = node.toString() + ":" + sourceSegmentationId + ":" + destinationSegmentationId + ":" +
549 ipStr + "/" + Integer.toString(mask);
550 final Boolean isProgrammed = routerInterfacesCache.contains(cacheKey);
552 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
553 logger.trace("programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
554 "action {} is already done",
555 node.getId().getValue(), sourceSegmentationId, destinationSegmentationId,
556 ipStr, mask, actionForNode);
559 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
560 logger.trace("programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
561 "action {} is already done",
562 node.getId().getValue(), sourceSegmentationId, destinationSegmentationId,
563 ipStr, mask, actionForNode);
567 Status status = this.programRouterInterfaceStage2(node, dpid, sourceSegmentationId, destinationSegmentationId,
568 macAddress, ipStr, mask, actionForNode);
569 if (status.isSuccess()) {
571 if (actionForNode == Action.ADD) {
572 // TODO: multiTenantAwareRouter.addInterface(UUID.fromString(tenant), ...);
573 routerInterfacesCache.add(cacheKey);
575 // TODO: multiTenantAwareRouter.removeInterface(...);
576 routerInterfacesCache.remove(cacheKey);
581 private Status programRouterInterfaceStage2(Node node, Long dpid, String sourceSegmentationId,
582 String destinationSegmentationId,
584 String address, int mask,
585 Action actionForNode) {
588 InetAddress inetAddress = InetAddress.getByName(address);
589 status = routingProvider == null ?
590 new Status(StatusCode.SUCCESS) :
591 routingProvider.programRouterInterface(node, dpid, sourceSegmentationId, destinationSegmentationId,
592 macAddress, inetAddress, mask, actionForNode);
593 } catch (UnknownHostException e) {
594 status = new Status(StatusCode.BADREQUEST);
597 if (status.isSuccess()) {
598 logger.debug("ProgramRouterInterface {} for mac:{} addr:{}/{} node:{} action:{}",
599 routingProvider == null ? "skipped" : "programmed",
600 macAddress, address, mask, node, actionForNode);
602 logger.error("ProgramRouterInterface failed for mac:{} addr:{}/{} node:{} action:{} status:{}",
603 macAddress, address, mask, node, actionForNode, status);
608 private void programStaticArpStage1(Node node, Long dpid, String providerSegmentationId,
609 String macAddress, String ipStr,
610 Action actionForNode) {
611 // Based on the local cache, figure out whether programming needs to occur. To do this, we
612 // will look at desired action for node.
614 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + ipStr;
615 final Boolean isProgrammed = staticArpEntryCache.contains(cacheKey);
617 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
618 logger.trace("programStaticArpStage1 node {} providerId {} mac {} ip {} action {} is already done",
619 node.getId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
622 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
623 logger.trace("programStaticArpStage1 node {} providerId {} mac {} ip {} action {} is already done",
624 node.getId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
628 Status status = this.programStaticArpStage2(node, dpid, providerSegmentationId,
629 macAddress, ipStr, actionForNode);
630 if (status.isSuccess()) {
632 if (actionForNode == Action.ADD) {
633 staticArpEntryCache.add(cacheKey);
635 staticArpEntryCache.remove(cacheKey);
640 private Status programStaticArpStage2(Node node, Long dpid, String providerSegmentationId,
643 Action actionForNode) {
646 InetAddress inetAddress = InetAddress.getByName(address);
647 status = arpProvider == null ?
648 new Status(StatusCode.SUCCESS) :
649 arpProvider.programStaticArpEntry(node, dpid, providerSegmentationId,
650 macAddress, inetAddress, actionForNode);
651 } catch (UnknownHostException e) {
652 status = new Status(StatusCode.BADREQUEST);
655 if (status.isSuccess()) {
656 logger.debug("ProgramStaticArp {} for mac:{} addr:{} node:{} action:{}",
657 arpProvider == null ? "skipped" : "programmed",
658 macAddress, address, node, actionForNode);
660 logger.error("ProgramStaticArp failed for mac:{} addr:{} node:{} action:{} status:{}",
661 macAddress, address, node, actionForNode, status);
666 private void programIpRewriteExclusionStage1(Node node, Long dpid, String providerSegmentationId,
667 final boolean isInbound, String cidr,
668 Action actionForRewriteExclusion) {
669 // Based on the local cache, figure out whether programming needs to occur. To do this, we
670 // will look at desired action for node.
672 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + cidr;
673 final Boolean isProgrammed = isInbound ?
674 inboundIpRewriteExclusionCache.contains(cacheKey):
675 outboundIpRewriteExclusionCache.contains(cacheKey);
677 if (actionForRewriteExclusion == Action.DELETE && isProgrammed == Boolean.FALSE) {
678 logger.trace("programIpRewriteExclusionStage1 node {} providerId {} {} cidr {} action {} is already done",
679 node.getId().getValue(), providerSegmentationId, isInbound ? "inbound" : "outbound", cidr,
680 actionForRewriteExclusion);
683 if (actionForRewriteExclusion == Action.ADD && isProgrammed == Boolean.TRUE) {
684 logger.trace("programIpRewriteExclusionStage1 node {} providerId {} {} cidr {} action {} is already done",
685 node.getId().getValue(), providerSegmentationId, isInbound ? "inbound" : "outbound", cidr,
686 actionForRewriteExclusion);
690 Status status = this.programIpRewriteExclusionStage2(node, dpid, providerSegmentationId, cidr,
691 isInbound, actionForRewriteExclusion);
692 if (status.isSuccess()) {
694 if (actionForRewriteExclusion == Action.ADD) {
696 inboundIpRewriteExclusionCache.add(cacheKey);
698 outboundIpRewriteExclusionCache.add(cacheKey);
702 inboundIpRewriteExclusionCache.remove(cacheKey);
704 outboundIpRewriteExclusionCache.remove(cacheKey);
710 private Status programIpRewriteExclusionStage2(Node node, Long dpid, String providerSegmentationId, String cidr,
711 final boolean isInbound, Action actionForNode) {
714 status = inboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
715 inboundNatProvider.programIpRewriteExclusion(node, dpid, providerSegmentationId, cidr,
718 status = outboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
719 outboundNatProvider.programIpRewriteExclusion(node, dpid, providerSegmentationId, cidr,
723 if (status.isSuccess()) {
724 final boolean isSkipped = isInbound ? inboundNatProvider == null : outboundNatProvider == null;
725 logger.debug("IpRewriteExclusion {} {} for cidr:{} node:{} action:{}",
726 (isInbound ? "inbound" : "outbound"), (isSkipped ? "skipped" : "programmed"),
727 cidr, node, actionForNode);
729 logger.error("IpRewriteExclusion {} failed for cidr:{} node:{} action:{} status:{}",
730 (isInbound ? "inbound" : "outbound"), cidr, node, actionForNode, status);
735 private void programDefaultRouteStage1(Node node, Long dpid, String providerSegmentationId,
736 String defaultGatewayMacAddress, String gatewayIp,
737 Action actionForNodeDefaultRoute) {
738 // Based on the local cache, figure out whether programming needs to occur. To do this, we
739 // will look at desired action for node.
741 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + gatewayIp;
742 final Boolean isProgrammed = defaultRouteCache.contains(cacheKey);
744 if (actionForNodeDefaultRoute == Action.DELETE && isProgrammed == Boolean.FALSE) {
745 logger.trace("programDefaultRouteStage1 node {} providerId {} mac {} gw {} action {} is already done",
746 node.getId().getValue(), providerSegmentationId, defaultGatewayMacAddress, gatewayIp,
747 actionForNodeDefaultRoute);
750 if (actionForNodeDefaultRoute == Action.ADD && isProgrammed == Boolean.TRUE) {
751 logger.trace("programDefaultRouteStage1 node {} providerId {} mac {} gw {} action {} is already done",
752 node.getId().getValue(), providerSegmentationId, defaultGatewayMacAddress, gatewayIp,
753 actionForNodeDefaultRoute);
757 Status status = this.programDefaultRouteStage2(node, dpid, providerSegmentationId,
758 defaultGatewayMacAddress, gatewayIp, actionForNodeDefaultRoute);
759 if (status.isSuccess()) {
761 if (actionForNodeDefaultRoute == Action.ADD) {
762 defaultRouteCache.add(cacheKey);
764 defaultRouteCache.remove(cacheKey);
769 private Status programDefaultRouteStage2(Node node, Long dpid, String providerSegmentationId,
770 String defaultGatewayMacAddress,
772 Action actionForNodeDefaultRoute) {
773 // TODO: As of Helium, mac address for default gateway is required (bug 1705).
774 if (defaultGatewayMacAddress == null) {
775 logger.error("ProgramDefaultRoute mac not provided. gatewayIp:{} node:{} action:{}",
776 gatewayIp, node, actionForNodeDefaultRoute);
777 return new Status(StatusCode.NOTIMPLEMENTED); // Bug 1705
782 InetAddress inetAddress = InetAddress.getByName(gatewayIp);
783 status = routingProvider == null ?
784 new Status(StatusCode.SUCCESS) :
785 routingProvider.programDefaultRouteEntry(node, dpid, providerSegmentationId,
786 defaultGatewayMacAddress, inetAddress,
787 actionForNodeDefaultRoute);
788 } catch (UnknownHostException e) {
789 status = new Status(StatusCode.BADREQUEST);
792 if (status.isSuccess()) {
793 logger.debug("ProgramDefaultRoute {} for mac:{} gatewayIp:{} node:{} action:{}",
794 routingProvider == null ? "skipped" : "programmed",
795 defaultGatewayMacAddress, gatewayIp, node, actionForNodeDefaultRoute);
797 logger.error("ProgramDefaultRoute failed for mac:{} gatewayIp:{} node:{} action:{} status:{}",
798 defaultGatewayMacAddress, gatewayIp, node, actionForNodeDefaultRoute, status);
803 private void programFlowsForFloatingIP(final NeutronFloatingIP neutronFloatingIP, Boolean isDelete) {
804 Preconditions.checkNotNull(neutronFloatingIP);
806 final String networkUUID = neutronFloatingIP.getFloatingNetworkUUID();
807 final String routerMacAddress = networkIdToRouterMacCache.get(networkUUID);
809 // If there is no router interface handling the networkUUID, we are done
810 if (routerMacAddress == null || routerMacAddress.isEmpty()) {
814 final NeutronNetwork neutronNetwork = neutronNetworkCache.getNetwork(networkUUID);
815 final String providerSegmentationId = neutronNetwork != null ?
816 neutronNetwork.getProviderSegmentationID() : null;
817 final String fixedIPAddress = neutronFloatingIP.getFixedIPAddress();
818 final String floatingIpAddress = neutronFloatingIP.getFloatingIPAddress();
820 if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
821 // routerMacAddress == null || routerMacAddress.isEmpty() ||
822 fixedIPAddress == null || fixedIPAddress.isEmpty() ||
823 floatingIpAddress == null || floatingIpAddress.isEmpty()) {
824 return; // done: go no further w/out all the info needed...
827 final Action action = isDelete ? Action.DELETE : Action.ADD;
828 List<Node> nodes = connectionService.getNodes();
829 if (nodes.isEmpty()) {
830 logger.trace("programFlowsForFloatingIP has no nodes to work with");
832 for (Node node : nodes) {
833 final Long dpid = getDpid(node);
834 final Action actionForNode =
835 tenantNetworkManager.isTenantNetworkPresentInNode(node, providerSegmentationId) ?
836 action : Action.DELETE;
838 // Rewrite from float to fixed and vice-versa
840 programIpRewriteStage1(node, dpid, providerSegmentationId, true /* isInbound */,
841 floatingIpAddress, fixedIPAddress, actionForNode);
842 programIpRewriteStage1(node, dpid, providerSegmentationId, false /* isInboubd */,
843 fixedIPAddress, floatingIpAddress, actionForNode);
845 // Respond to arps for the floating ip address
847 programStaticArpStage1(node, dpid, providerSegmentationId, routerMacAddress, floatingIpAddress,
852 private void programIpRewriteStage1(Node node, Long dpid, String providerSegmentationId,
853 final boolean isInbound,
854 String matchAddress, String rewriteAddress,
855 Action actionForNode) {
856 // Based on the local cache, figure out whether programming needs to occur. To do this, we
857 // will look at desired action for node.
859 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" +
860 matchAddress + ":" + rewriteAddress;
861 final Boolean isProgrammed = isInbound ?
862 inboundIpRewriteCache.contains(cacheKey) :
863 outboundIpRewriteCache.contains(cacheKey);
865 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
866 logger.trace("programIpRewriteStage1 node {} providerId {} {} matchAddr {} rewriteAddr {} action {}" +
868 node.getId().getValue(), providerSegmentationId, isInbound ? "inbound": "outbound",
869 matchAddress, rewriteAddress, actionForNode);
872 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
873 logger.trace("programIpRewriteStage1 node {} providerId {} {} matchAddr {} rewriteAddr {} action {}" +
875 node.getId().getValue(), providerSegmentationId, isInbound ? "inbound": "outbound",
876 matchAddress, rewriteAddress, actionForNode);
880 Status status = this.programIpRewriteStage2(node, dpid, providerSegmentationId, isInbound,
881 matchAddress, rewriteAddress, actionForNode);
882 if (status.isSuccess()) {
884 if (actionForNode == Action.ADD) {
886 inboundIpRewriteCache.add(cacheKey);
888 outboundIpRewriteCache.add(cacheKey);
892 inboundIpRewriteCache.remove(cacheKey);
894 outboundIpRewriteCache.remove(cacheKey);
900 private Status programIpRewriteStage2(Node node, Long dpid, String providerSegmentationId,
901 final boolean isInbound,
902 String matchAddress, String rewriteAddress,
903 Action actionForNode) {
906 InetAddress inetMatchAddress = InetAddress.getByName(matchAddress);
907 InetAddress inetRewriteAddress = InetAddress.getByName(rewriteAddress);
909 status = inboundNatProvider == null ?
910 new Status(StatusCode.SUCCESS) :
911 inboundNatProvider.programIpRewriteRule(node, dpid, providerSegmentationId,
912 inetMatchAddress, inetRewriteAddress, actionForNode);
914 status = outboundNatProvider == null ?
915 new Status(StatusCode.SUCCESS) :
916 outboundNatProvider.programIpRewriteRule(node, dpid, providerSegmentationId,
917 inetMatchAddress, inetRewriteAddress, actionForNode);
919 } catch (UnknownHostException e) {
920 status = new Status(StatusCode.BADREQUEST);
923 if (status.isSuccess()) {
924 final boolean isSkipped = isInbound ? inboundNatProvider == null : outboundNatProvider == null;
925 logger.debug("ProgramIpRewrite {} {} for match:{} rewrite:{} node:{} action:{}",
926 (isInbound ? "inbound" : "outbound"), (isSkipped ? "skipped" : "programmed"),
927 matchAddress, rewriteAddress, node, actionForNode);
929 logger.error("ProgramIpRewrite {} failed for match:{} rewrite:{} node:{} action:{} status:{}",
930 (isInbound ? "inbound" : "outbound"),
931 matchAddress, rewriteAddress, node, actionForNode, status);
940 private int getMaskLenFromCidr(String cidr) {
941 if (cidr == null) return 0;
942 String[] splits = cidr.split("/");
943 if (splits.length != 2) return 0;
947 result = Integer.parseInt(splits[1].trim());
949 catch (NumberFormatException nfe)
956 private Long getDpid (Node node) {
957 Preconditions.checkNotNull(ovsdbConfigurationService);
959 String bridgeName = configurationService.getIntegrationBridgeName();
960 String bridgeUuid = this.getInternalBridgeUUID(node, bridgeName);
961 if (bridgeUuid == null) {
962 logger.error("Unable to spot Bridge Identifier for {} in {}", bridgeName, node);
967 Row bridgeRow = ovsdbConfigurationService
968 .getRow(node, ovsdbConfigurationService.getTableName(node, Bridge.class), bridgeUuid);
969 Bridge bridge = ovsdbConfigurationService.getTypedRow(node, Bridge.class, bridgeRow);
970 Set<String> dpids = bridge.getDatapathIdColumn().getData();
971 if (dpids == null || dpids.size() == 0) return 0L;
972 return StringConvertor.dpidStringToLong((String) dpids.toArray()[0]);
973 } catch (Exception e) {
974 logger.error("Error finding Bridge's OF DPID", e);
979 private String getInternalBridgeUUID (Node node, String bridgeName) {
980 Preconditions.checkNotNull(ovsdbConfigurationService);
982 Map<String, Row> bridgeTable =
983 ovsdbConfigurationService.getRows(node,
984 ovsdbConfigurationService.getTableName(node, Bridge.class));
985 if (bridgeTable == null) return null;
986 for (String key : bridgeTable.keySet()) {
987 Bridge bridge = ovsdbConfigurationService.getTypedRow(node, Bridge.class, bridgeTable.get(key));
988 if (bridge.getName().equals(bridgeName)) return key;
990 } catch (Exception e) {
991 logger.error("Error getting Bridge Identifier for {} / {}", node, bridgeName, e);