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 java.util.ArrayList;
14 import org.opendaylight.neutron.spi.INeutronNetworkCRUD;
15 import org.opendaylight.neutron.spi.INeutronPortCRUD;
16 import org.opendaylight.neutron.spi.INeutronSubnetCRUD;
17 import org.opendaylight.neutron.spi.NeutronFloatingIP;
18 import org.opendaylight.neutron.spi.NeutronNetwork;
19 import org.opendaylight.neutron.spi.NeutronPort;
20 import org.opendaylight.neutron.spi.NeutronRouter;
21 import org.opendaylight.neutron.spi.NeutronRouter_Interface;
22 import org.opendaylight.neutron.spi.NeutronSubnet;
23 import org.opendaylight.neutron.spi.Neutron_IPs;
24 import org.opendaylight.ovsdb.lib.notation.Row;
25 import org.opendaylight.ovsdb.openstack.netvirt.api.*;
26 import org.opendaylight.ovsdb.schema.openvswitch.Bridge;
27 import org.opendaylight.ovsdb.schema.openvswitch.Interface;
28 import org.opendaylight.ovsdb.utils.config.ConfigProperties;
29 import org.opendaylight.ovsdb.utils.mdsal.node.StringConvertor;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
31 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
33 import com.google.common.base.Preconditions;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
37 import java.net.InetAddress;
38 import java.net.UnknownHostException;
39 import java.util.HashMap;
40 import java.util.HashSet;
41 import java.util.List;
46 * Neutron L3 Adapter implements a hub-like adapter for the various Neutron events. Based on
47 * these events, the abstract router callbacks can be generated to the multi-tenant aware router,
48 * as well as the multi-tenant router forwarding provider.
50 public class NeutronL3Adapter {
55 static final Logger logger = LoggerFactory.getLogger(NeutronL3Adapter.class);
57 // The implementation for each of these services is resolved by the OSGi Service Manager
58 private volatile ConfigurationService configurationService;
59 private volatile TenantNetworkManager tenantNetworkManager;
60 /* TODO SB_MIGRATION */
61 private volatile OvsdbConfigurationService ovsdbConfigurationService;
62 private volatile OvsdbConnectionService connectionService;
63 private volatile MdsalConsumer mdsalConsumer;
64 private volatile INeutronNetworkCRUD neutronNetworkCache;
65 private volatile INeutronSubnetCRUD neutronSubnetCache;
66 private volatile INeutronPortCRUD neutronPortCache;
67 private volatile L3ForwardingProvider l3ForwardingProvider;
68 private volatile InboundNatProvider inboundNatProvider;
69 private volatile OutboundNatProvider outboundNatProvider;
70 private volatile ArpProvider arpProvider;
71 private volatile RoutingProvider routingProvider;
73 private Set<String> inboundIpRewriteCache;
74 private Set<String> outboundIpRewriteCache;
75 private Set<String> inboundIpRewriteExclusionCache;
76 private Set<String> outboundIpRewriteExclusionCache;
77 private Set<String> routerInterfacesCache;
78 private Set<String> staticArpEntryCache;
79 private Set<String> l3ForwardingCache;
80 private Set<String> defaultRouteCache;
81 private Map<String, String> networkIdToRouterMacCache;
82 private Map<String, NeutronRouter_Interface> subnetIdToRouterInterfaceCache;
83 private Boolean enabled = false;
86 final String enabledPropertyStr = ConfigProperties.getProperty(this.getClass(), "ovsdb.l3.fwd.enabled");
87 if (enabledPropertyStr != null && enabledPropertyStr.equalsIgnoreCase("yes")) {
88 this.inboundIpRewriteCache = new HashSet<>();
89 this.outboundIpRewriteCache = new HashSet<>();
90 this.inboundIpRewriteExclusionCache = new HashSet<>();
91 this.outboundIpRewriteExclusionCache = new HashSet<>();
92 this.routerInterfacesCache = new HashSet<>();
93 this.staticArpEntryCache = new HashSet<>();
94 this.l3ForwardingCache = new HashSet<>();
95 this.defaultRouteCache = new HashSet<>();
96 this.networkIdToRouterMacCache = new HashMap<>();
97 this.subnetIdToRouterInterfaceCache = new HashMap<>();
100 logger.info("OVSDB L3 forwarding is enabled");
102 logger.debug("OVSDB L3 forwarding is disabled");
107 // Callbacks from OVSDB's northbound handlers
110 public void handleNeutronSubnetEvent(final NeutronSubnet subnet, Action action) {
111 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());
121 final boolean isDelete = action == Action.DELETE;
123 // Treat the port event as a router interface event if the port belongs to router. This is a
124 // helper for handling cases when handleNeutronRouterInterfaceEvent is not available
126 if (neutronPort.getDeviceOwner().equalsIgnoreCase("network:router_interface")) {
127 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
128 NeutronRouter_Interface neutronRouterInterface =
129 new NeutronRouter_Interface(neutronIP.getSubnetUUID(), neutronPort.getPortUUID());
130 neutronRouterInterface.setID(neutronIP.getSubnetUUID()); // id of router interface to be same as subnet
131 neutronRouterInterface.setTenantID(neutronPort.getTenantID());
133 this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
136 // We made it here, port is not used as a router interface. If this is not a delete action, make sure that
137 // all nodes that are supposed to have a router interface for the port's subnet(s), have it configured. We
138 // need to do this check here because a router interface is not added to a node until tenant becomes needed
142 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
143 NeutronRouter_Interface neutronRouterInterface =
144 subnetIdToRouterInterfaceCache.get(neutronIP.getSubnetUUID());
145 if (neutronRouterInterface != null) {
146 this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
150 this.updateL3ForNeutronPort(neutronPort, isDelete);
154 public void handleNeutronRouterEvent(final NeutronRouter neutronRouter, Action action) {
155 logger.debug("Neutron router {} event : {}", action, neutronRouter.toString());
160 public void handleNeutronRouterInterfaceEvent(final NeutronRouter neutronRouter,
161 final NeutronRouter_Interface neutronRouterInterface,
163 logger.debug("Router interface {} got event {}. Subnet {}",
164 neutronRouterInterface.getPortUUID(),
166 neutronRouterInterface.getSubnetUUID());
170 final boolean isDelete = action == Action.DELETE;
172 this.programFlowsForNeutronRouterInterface(neutronRouterInterface, isDelete);
174 // As neutron router interface is added/removed, we need to iterate through all the neutron ports and
175 // see if they are affected by l3
177 for (NeutronPort neutronPort : neutronPortCache.getAllPorts()) {
178 boolean currPortShouldBeDeleted = false;
179 // Note: delete in this case only applies to 1)router interface delete and 2)ports on the same subnet
181 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
182 if (neutronRouterInterface.getSubnetUUID().equalsIgnoreCase(neutronIP.getSubnetUUID())) {
183 currPortShouldBeDeleted = true;
188 this.updateL3ForNeutronPort(neutronPort, currPortShouldBeDeleted);
192 public void handleNeutronFloatingIPEvent(final NeutronFloatingIP neutronFloatingIP,
194 logger.debug(" Floating IP {} {}<->{}, network uuid {}", action,
195 neutronFloatingIP.getFixedIPAddress(),
196 neutronFloatingIP.getFloatingIPAddress(),
197 neutronFloatingIP.getFloatingNetworkUUID());
201 this.programFlowsForFloatingIP(neutronFloatingIP, action == Action.DELETE);
204 public void handleNeutronNetworkEvent(final NeutronNetwork neutronNetwork, Action action) {
205 logger.debug("neutronNetwork {}: network: {}", action, neutronNetwork);
211 // Callbacks from OVSDB's southbound handler
213 public void handleInterfaceEvent(final Node node, final OvsdbTerminationPointAugmentation intf,
214 final NeutronNetwork neutronNetwork, Action action) {
215 logger.debug("southbound interface {} node:{} interface:{}, neutronNetwork:{}",
216 action, node, intf.getName(), neutronNetwork);
220 NeutronPort neutronPort = tenantNetworkManager.getTenantPort(intf);
221 if (neutronPort != null) {
222 this.handleNeutronPortEvent(neutronPort, action);
229 private void updateL3ForNeutronPort(final NeutronPort neutronPort, final boolean isDelete) {
231 final String networkUUID = neutronPort.getNetworkUUID();
232 final String routerMacAddress = networkIdToRouterMacCache.get(networkUUID);
234 // If there is no router interface handling the networkUUID, we are done
235 if (routerMacAddress == null || routerMacAddress.isEmpty()) {
239 // If this is the neutron port for the router interface itself, ignore it as well. Ports that represent the
240 // router interface are handled via handleNeutronRouterInterfaceEvent.
241 if (routerMacAddress.equalsIgnoreCase(neutronPort.getMacAddress())) {
245 final NeutronNetwork neutronNetwork = neutronNetworkCache.getNetwork(networkUUID);
246 final String providerSegmentationId = neutronNetwork != null ?
247 neutronNetwork.getProviderSegmentationID() : null;
248 final String tenantMac = neutronPort.getMacAddress();
250 if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
251 tenantMac == null || tenantMac.isEmpty()) {
252 return; // done: go no further w/out all the info needed...
255 final Action action = isDelete ? Action.DELETE : Action.ADD;
257 List<Node> nodes = connectionService.getNodes();
259 List<Node> nodes = new ArrayList<>(); // TODO SB_MIGRATION
260 if (nodes.isEmpty()) {
261 logger.trace("updateL3ForNeutronPort has no nodes to work with");
263 for (Node node : nodes) {
264 final Long dpid = getDpid(node);
265 final boolean tenantNetworkPresentInNode =
266 tenantNetworkManager.isTenantNetworkPresentInNode(node, providerSegmentationId);
267 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
268 final String tenantIpStr = neutronIP.getIpAddress();
269 if (tenantIpStr.isEmpty()) {
273 // Configure L3 fwd. We do that regardless of tenant network present, because these rules are
274 // still needed when routing to subnets non-local to node (bug 2076).
275 programL3ForwardingStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr, action);
277 // Configure distributed ARP responder. Only needed if tenant network exists in node.
278 programStaticArpStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr,
279 tenantNetworkPresentInNode ? action : Action.DELETE);
284 private void programL3ForwardingStage1(Node node, Long dpid, String providerSegmentationId,
285 String macAddress, String ipStr,
286 Action actionForNode) {
287 // Based on the local cache, figure out whether programming needs to occur. To do this, we
288 // will look at desired action for node.
290 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + ipStr;
291 final Boolean isProgrammed = l3ForwardingCache.contains(cacheKey);
293 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
294 logger.trace("programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {} is already done",
295 node.getNodeId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
298 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
299 logger.trace("programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {} is already done",
300 node.getNodeId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
304 Status status = this.programL3ForwardingStage2(node, dpid, providerSegmentationId,
305 macAddress, ipStr, actionForNode);
306 if (status.isSuccess()) {
308 if (actionForNode == Action.ADD) {
309 l3ForwardingCache.add(cacheKey);
311 l3ForwardingCache.remove(cacheKey);
316 private Status programL3ForwardingStage2(Node node, Long dpid, String providerSegmentationId,
319 Action actionForNode) {
322 InetAddress inetAddress = InetAddress.getByName(address);
323 status = l3ForwardingProvider == null ?
324 new Status(StatusCode.SUCCESS) :
325 l3ForwardingProvider.programForwardingTableEntry(dpid, providerSegmentationId,
326 inetAddress, macAddress, actionForNode);
327 } catch (UnknownHostException e) {
328 status = new Status(StatusCode.BADREQUEST);
331 if (status.isSuccess()) {
332 logger.debug("ProgramL3Forwarding {} for mac:{} addr:{} node:{} action:{}",
333 l3ForwardingProvider == null ? "skipped" : "programmed",
334 macAddress, address, node, actionForNode);
336 logger.error("ProgramL3Forwarding failed for mac:{} addr:{} node:{} action:{} status:{}",
337 macAddress, address, node, actionForNode, status);
344 private void programFlowsForNeutronRouterInterface(final NeutronRouter_Interface destNeutronRouterInterface,
346 Preconditions.checkNotNull(destNeutronRouterInterface);
348 final NeutronPort neutronPort = neutronPortCache.getPort(destNeutronRouterInterface.getPortUUID());
349 final String macAddress = neutronPort != null ? neutronPort.getMacAddress() : null;
350 final List<Neutron_IPs> ipList = neutronPort != null ? neutronPort.getFixedIPs() : null;
351 final NeutronSubnet subnet = neutronSubnetCache.getSubnet(destNeutronRouterInterface.getSubnetUUID());
352 final NeutronNetwork neutronNetwork = subnet != null ?
353 neutronNetworkCache.getNetwork(subnet.getNetworkUUID()) : null;
354 final String destinationSegmentationId = neutronNetwork != null ?
355 neutronNetwork.getProviderSegmentationID() : null;
356 final String gatewayIp = subnet != null ? subnet.getGatewayIP() : null;
357 final Boolean isExternal = neutronNetwork != null ? neutronNetwork.getRouterExternal() : Boolean.TRUE;
358 final String cidr = subnet != null ? subnet.getCidr() : null;
359 final int mask = getMaskLenFromCidr(cidr);
361 logger.trace("programFlowsForNeutronRouterInterface called for interface {} isDelete {}",
362 destNeutronRouterInterface, isDelete);
364 if (destinationSegmentationId == null || destinationSegmentationId.isEmpty() ||
365 cidr == null || cidr.isEmpty() ||
366 macAddress == null || macAddress.isEmpty() ||
367 ipList == null || ipList.isEmpty()) {
368 logger.debug("programFlowsForNeutronRouterInterface is bailing seg:{} cidr:{} mac:{} ip:{}",
369 destinationSegmentationId, cidr, macAddress, ipList);
370 return; // done: go no further w/out all the info needed...
373 final Action action = isDelete ? Action.DELETE : Action.ADD;
375 // Keep cache for finding router's mac from network uuid -- add
378 networkIdToRouterMacCache.put(neutronNetwork.getNetworkUUID(), macAddress);
379 subnetIdToRouterInterfaceCache.put(subnet.getSubnetUUID(), destNeutronRouterInterface);
383 List<Node> nodes = connectionService.getNodes();*/
384 List<Node> nodes = new ArrayList<>(); // TODO SB_MIGRATION
385 if (nodes.isEmpty()) {
386 logger.trace("programFlowsForNeutronRouterInterface has no nodes to work with");
388 for (Node node : nodes) {
389 final Long dpid = getDpid(node);
390 final Action actionForNode =
391 tenantNetworkManager.isTenantNetworkPresentInNode(node, destinationSegmentationId) ?
392 action : Action.DELETE;
394 for (Neutron_IPs neutronIP : ipList) {
395 final String ipStr = neutronIP.getIpAddress();
396 if (ipStr.isEmpty()) {
397 logger.debug("programFlowsForNeutronRouterInterface is skipping node {} ip {}",
398 node.getNodeId().getValue(), ipStr);
402 // Iterate through all other interfaces and add/remove reflexive flows to this interface
404 for (NeutronRouter_Interface srcNeutronRouterInterface : subnetIdToRouterInterfaceCache.values()) {
405 programFlowsForNeutronRouterInterfacePair(node, dpid,
406 srcNeutronRouterInterface, destNeutronRouterInterface,
407 neutronNetwork, destinationSegmentationId,
408 macAddress, ipStr, mask, actionForNode,
409 true /*isReflexsive*/);
412 programStaticArpStage1(node, dpid, destinationSegmentationId, macAddress, ipStr, actionForNode);
415 // Compute action to be programmed. In the case of rewrite exclusions, we must never program rules
416 // for the external neutron networks.
419 final Action actionForRewriteExclusion = isExternal ? Action.DELETE : actionForNode;
420 programIpRewriteExclusionStage1(node, dpid, destinationSegmentationId, true /* isInbound */,
421 cidr, actionForRewriteExclusion);
422 programIpRewriteExclusionStage1(node, dpid, destinationSegmentationId, false /* isInbound */,
423 cidr, actionForRewriteExclusion);
426 // Default route. For non-external subnet, make sure that there is none configured.
428 if (gatewayIp != null && !gatewayIp.isEmpty()) {
429 final Action actionForNodeDefaultRoute =
430 isExternal ? actionForNode : Action.DELETE;
431 final String defaultGatewayMacAddress = configurationService.getDefaultGatewayMacAddress(node);
432 programDefaultRouteStage1(node, dpid, destinationSegmentationId, defaultGatewayMacAddress, gatewayIp,
433 actionForNodeDefaultRoute);
437 // Keep cache for finding router's mac from network uuid -- remove
440 networkIdToRouterMacCache.remove(neutronNetwork.getNetworkUUID());
441 subnetIdToRouterInterfaceCache.remove(subnet.getSubnetUUID());
445 private void programFlowsForNeutronRouterInterfacePair(final Node node,
447 final NeutronRouter_Interface srcNeutronRouterInterface,
448 final NeutronRouter_Interface dstNeutronRouterInterface,
449 final NeutronNetwork dstNeutronNetwork,
450 final String destinationSegmentationId,
451 final String dstMacAddress,
452 final String destIpStr,
454 final Action actionForNode,
455 Boolean isReflexsive) {
456 Preconditions.checkNotNull(srcNeutronRouterInterface);
457 Preconditions.checkNotNull(dstNeutronRouterInterface);
459 final String sourceSubnetId = srcNeutronRouterInterface.getSubnetUUID();
460 if (sourceSubnetId == null) {
461 logger.error("Could not get provider Subnet ID from router interface {}",
462 srcNeutronRouterInterface.getID());
466 final NeutronSubnet sourceSubnet = neutronSubnetCache.getSubnet(sourceSubnetId);
467 final String sourceNetworkId = sourceSubnet == null ? null : sourceSubnet.getNetworkUUID();
468 if (sourceNetworkId == null) {
469 logger.error("Could not get provider Network ID from subnet {}", sourceSubnetId);
473 final NeutronNetwork sourceNetwork = neutronNetworkCache.getNetwork(sourceNetworkId);
474 if (sourceNetwork == null) {
475 logger.error("Could not get provider Network for Network ID {}", sourceNetworkId);
479 if (! sourceNetwork.getTenantID().equals(dstNeutronNetwork.getTenantID())) {
480 // Isolate subnets from different tenants within the same router
483 final String sourceSegmentationId = sourceNetwork.getProviderSegmentationID();
484 if (sourceSegmentationId == null) {
485 logger.error("Could not get provider Segmentation ID for Subnet {}", sourceSubnetId);
488 if (sourceSegmentationId.equals(destinationSegmentationId)) {
493 programRouterInterfaceStage1(node, dpid, sourceSegmentationId, destinationSegmentationId,
494 dstMacAddress, destIpStr, destMask, actionForNode);
496 // Flip roles src->dst; dst->src
498 final NeutronPort sourceNeutronPort = neutronPortCache.getPort(srcNeutronRouterInterface.getPortUUID());
499 final String macAddress2 = sourceNeutronPort != null ? sourceNeutronPort.getMacAddress() : null;
500 final List<Neutron_IPs> ipList2 = sourceNeutronPort != null ? sourceNeutronPort.getFixedIPs() : null;
501 final String cidr2 = sourceSubnet.getCidr();
502 final int mask2 = getMaskLenFromCidr(cidr2);
504 if (cidr2 == null || cidr2.isEmpty() ||
505 macAddress2 == null || macAddress2.isEmpty() ||
506 ipList2 == null || ipList2.isEmpty()) {
507 logger.trace("programFlowsForNeutronRouterInterfacePair reflexive is bailing seg:{} cidr:{} mac:{} ip:{}",
508 sourceSegmentationId, cidr2, macAddress2, ipList2);
509 return; // done: go no further w/out all the info needed...
512 for (Neutron_IPs neutronIP2 : ipList2) {
513 final String ipStr2 = neutronIP2.getIpAddress();
514 if (ipStr2.isEmpty()) {
517 programFlowsForNeutronRouterInterfacePair(node, dpid, dstNeutronRouterInterface,
518 srcNeutronRouterInterface,
519 sourceNetwork, sourceSegmentationId,
520 macAddress2, ipStr2, mask2, actionForNode,
521 false /*isReflexsive*/);
526 private void programRouterInterfaceStage1(Node node, Long dpid, String sourceSegmentationId,
527 String destinationSegmentationId,
528 String macAddress, String ipStr, int mask,
529 Action actionForNode) {
530 // Based on the local cache, figure out whether programming needs to occur. To do this, we
531 // will look at desired action for node.
533 final String cacheKey = node.toString() + ":" + sourceSegmentationId + ":" + destinationSegmentationId + ":" +
534 ipStr + "/" + Integer.toString(mask);
535 final Boolean isProgrammed = routerInterfacesCache.contains(cacheKey);
537 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
538 logger.trace("programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
539 "action {} is already done",
540 node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
541 ipStr, mask, actionForNode);
544 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
545 logger.trace("programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
546 "action {} is already done",
547 node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
548 ipStr, mask, actionForNode);
552 Status status = this.programRouterInterfaceStage2(node, dpid, sourceSegmentationId, destinationSegmentationId,
553 macAddress, ipStr, mask, actionForNode);
554 if (status.isSuccess()) {
556 if (actionForNode == Action.ADD) {
557 // TODO: multiTenantAwareRouter.addInterface(UUID.fromString(tenant), ...);
558 routerInterfacesCache.add(cacheKey);
560 // TODO: multiTenantAwareRouter.removeInterface(...);
561 routerInterfacesCache.remove(cacheKey);
566 private Status programRouterInterfaceStage2(Node node, Long dpid, String sourceSegmentationId,
567 String destinationSegmentationId,
569 String address, int mask,
570 Action actionForNode) {
573 InetAddress inetAddress = InetAddress.getByName(address);
574 status = routingProvider == null ?
575 new Status(StatusCode.SUCCESS) :
576 routingProvider.programRouterInterface(dpid, sourceSegmentationId, destinationSegmentationId,
577 macAddress, inetAddress, mask, actionForNode);
578 } catch (UnknownHostException e) {
579 status = new Status(StatusCode.BADREQUEST);
582 if (status.isSuccess()) {
583 logger.debug("ProgramRouterInterface {} for mac:{} addr:{}/{} node:{} action:{}",
584 routingProvider == null ? "skipped" : "programmed",
585 macAddress, address, mask, node, actionForNode);
587 logger.error("ProgramRouterInterface failed for mac:{} addr:{}/{} node:{} action:{} status:{}",
588 macAddress, address, mask, node, actionForNode, status);
593 private void programStaticArpStage1(Node node, Long dpid, String providerSegmentationId,
594 String macAddress, String ipStr,
595 Action actionForNode) {
596 // Based on the local cache, figure out whether programming needs to occur. To do this, we
597 // will look at desired action for node.
599 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + ipStr;
600 final Boolean isProgrammed = staticArpEntryCache.contains(cacheKey);
602 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
603 logger.trace("programStaticArpStage1 node {} providerId {} mac {} ip {} action {} is already done",
604 node.getNodeId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
607 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
608 logger.trace("programStaticArpStage1 node {} providerId {} mac {} ip {} action {} is already done",
609 node.getNodeId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
613 Status status = this.programStaticArpStage2(node, dpid, providerSegmentationId,
614 macAddress, ipStr, actionForNode);
615 if (status.isSuccess()) {
617 if (actionForNode == Action.ADD) {
618 staticArpEntryCache.add(cacheKey);
620 staticArpEntryCache.remove(cacheKey);
625 private Status programStaticArpStage2(Node node, Long dpid, String providerSegmentationId,
628 Action actionForNode) {
631 InetAddress inetAddress = InetAddress.getByName(address);
632 status = arpProvider == null ?
633 new Status(StatusCode.SUCCESS) :
634 arpProvider.programStaticArpEntry(dpid, providerSegmentationId,
635 macAddress, inetAddress, actionForNode);
636 } catch (UnknownHostException e) {
637 status = new Status(StatusCode.BADREQUEST);
640 if (status.isSuccess()) {
641 logger.debug("ProgramStaticArp {} for mac:{} addr:{} node:{} action:{}",
642 arpProvider == null ? "skipped" : "programmed",
643 macAddress, address, node, actionForNode);
645 logger.error("ProgramStaticArp failed for mac:{} addr:{} node:{} action:{} status:{}",
646 macAddress, address, node, actionForNode, status);
651 private void programIpRewriteExclusionStage1(Node node, Long dpid, String providerSegmentationId,
652 final boolean isInbound, String cidr,
653 Action actionForRewriteExclusion) {
654 // Based on the local cache, figure out whether programming needs to occur. To do this, we
655 // will look at desired action for node.
657 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + cidr;
658 final Boolean isProgrammed = isInbound ?
659 inboundIpRewriteExclusionCache.contains(cacheKey):
660 outboundIpRewriteExclusionCache.contains(cacheKey);
662 if (actionForRewriteExclusion == Action.DELETE && isProgrammed == Boolean.FALSE) {
663 logger.trace("programIpRewriteExclusionStage1 node {} providerId {} {} cidr {} action {} is already done",
664 node.getNodeId().getValue(), providerSegmentationId, isInbound ? "inbound" : "outbound", cidr,
665 actionForRewriteExclusion);
668 if (actionForRewriteExclusion == Action.ADD && isProgrammed == Boolean.TRUE) {
669 logger.trace("programIpRewriteExclusionStage1 node {} providerId {} {} cidr {} action {} is already done",
670 node.getNodeId().getValue(), providerSegmentationId, isInbound ? "inbound" : "outbound", cidr,
671 actionForRewriteExclusion);
675 Status status = this.programIpRewriteExclusionStage2(node, dpid, providerSegmentationId, cidr,
676 isInbound, actionForRewriteExclusion);
677 if (status.isSuccess()) {
679 if (actionForRewriteExclusion == Action.ADD) {
681 inboundIpRewriteExclusionCache.add(cacheKey);
683 outboundIpRewriteExclusionCache.add(cacheKey);
687 inboundIpRewriteExclusionCache.remove(cacheKey);
689 outboundIpRewriteExclusionCache.remove(cacheKey);
695 private Status programIpRewriteExclusionStage2(Node node, Long dpid, String providerSegmentationId, String cidr,
696 final boolean isInbound, Action actionForNode) {
699 status = inboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
700 inboundNatProvider.programIpRewriteExclusion(dpid, providerSegmentationId, cidr,
703 status = outboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
704 outboundNatProvider.programIpRewriteExclusion(dpid, providerSegmentationId, cidr,
708 if (status.isSuccess()) {
709 final boolean isSkipped = isInbound ? inboundNatProvider == null : outboundNatProvider == null;
710 logger.debug("IpRewriteExclusion {} {} for cidr:{} node:{} action:{}",
711 (isInbound ? "inbound" : "outbound"), (isSkipped ? "skipped" : "programmed"),
712 cidr, node, actionForNode);
714 logger.error("IpRewriteExclusion {} failed for cidr:{} node:{} action:{} status:{}",
715 (isInbound ? "inbound" : "outbound"), cidr, node, actionForNode, status);
720 private void programDefaultRouteStage1(Node node, Long dpid, String providerSegmentationId,
721 String defaultGatewayMacAddress, String gatewayIp,
722 Action actionForNodeDefaultRoute) {
723 // Based on the local cache, figure out whether programming needs to occur. To do this, we
724 // will look at desired action for node.
726 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + gatewayIp;
727 final Boolean isProgrammed = defaultRouteCache.contains(cacheKey);
729 if (actionForNodeDefaultRoute == Action.DELETE && isProgrammed == Boolean.FALSE) {
730 logger.trace("programDefaultRouteStage1 node {} providerId {} mac {} gw {} action {} is already done",
731 node.getNodeId().getValue(), providerSegmentationId, defaultGatewayMacAddress, gatewayIp,
732 actionForNodeDefaultRoute);
735 if (actionForNodeDefaultRoute == Action.ADD && isProgrammed == Boolean.TRUE) {
736 logger.trace("programDefaultRouteStage1 node {} providerId {} mac {} gw {} action {} is already done",
737 node.getNodeId().getValue(), providerSegmentationId, defaultGatewayMacAddress, gatewayIp,
738 actionForNodeDefaultRoute);
742 Status status = this.programDefaultRouteStage2(node, dpid, providerSegmentationId,
743 defaultGatewayMacAddress, gatewayIp, actionForNodeDefaultRoute);
744 if (status.isSuccess()) {
746 if (actionForNodeDefaultRoute == Action.ADD) {
747 defaultRouteCache.add(cacheKey);
749 defaultRouteCache.remove(cacheKey);
754 private Status programDefaultRouteStage2(Node node, Long dpid, String providerSegmentationId,
755 String defaultGatewayMacAddress,
757 Action actionForNodeDefaultRoute) {
758 // TODO: As of Helium, mac address for default gateway is required (bug 1705).
759 if (defaultGatewayMacAddress == null) {
760 logger.error("ProgramDefaultRoute mac not provided. gatewayIp:{} node:{} action:{}",
761 gatewayIp, node, actionForNodeDefaultRoute);
762 return new Status(StatusCode.NOTIMPLEMENTED); // Bug 1705
767 InetAddress inetAddress = InetAddress.getByName(gatewayIp);
768 status = routingProvider == null ?
769 new Status(StatusCode.SUCCESS) :
770 routingProvider.programDefaultRouteEntry(dpid, providerSegmentationId,
771 defaultGatewayMacAddress, inetAddress,
772 actionForNodeDefaultRoute);
773 } catch (UnknownHostException e) {
774 status = new Status(StatusCode.BADREQUEST);
777 if (status.isSuccess()) {
778 logger.debug("ProgramDefaultRoute {} for mac:{} gatewayIp:{} node:{} action:{}",
779 routingProvider == null ? "skipped" : "programmed",
780 defaultGatewayMacAddress, gatewayIp, node, actionForNodeDefaultRoute);
782 logger.error("ProgramDefaultRoute failed for mac:{} gatewayIp:{} node:{} action:{} status:{}",
783 defaultGatewayMacAddress, gatewayIp, node, actionForNodeDefaultRoute, status);
788 private void programFlowsForFloatingIP(final NeutronFloatingIP neutronFloatingIP, Boolean isDelete) {
789 Preconditions.checkNotNull(neutronFloatingIP);
791 final String networkUUID = neutronFloatingIP.getFloatingNetworkUUID();
792 final String routerMacAddress = networkIdToRouterMacCache.get(networkUUID);
794 // If there is no router interface handling the networkUUID, we are done
795 if (routerMacAddress == null || routerMacAddress.isEmpty()) {
799 final NeutronNetwork neutronNetwork = neutronNetworkCache.getNetwork(networkUUID);
800 final String providerSegmentationId = neutronNetwork != null ?
801 neutronNetwork.getProviderSegmentationID() : null;
802 final String fixedIPAddress = neutronFloatingIP.getFixedIPAddress();
803 final String floatingIpAddress = neutronFloatingIP.getFloatingIPAddress();
805 if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
806 // routerMacAddress == null || routerMacAddress.isEmpty() ||
807 fixedIPAddress == null || fixedIPAddress.isEmpty() ||
808 floatingIpAddress == null || floatingIpAddress.isEmpty()) {
809 return; // done: go no further w/out all the info needed...
812 final Action action = isDelete ? Action.DELETE : Action.ADD;
814 List<Node> nodes = connectionService.getNodes();*/
815 List<Node> nodes = new ArrayList<>(); // TODO SB_MIGRATION
816 if (nodes.isEmpty()) {
817 logger.trace("programFlowsForFloatingIP has no nodes to work with");
819 for (Node node : nodes) {
820 final Long dpid = getDpid(node);
821 final Action actionForNode =
822 tenantNetworkManager.isTenantNetworkPresentInNode(node, providerSegmentationId) ?
823 action : Action.DELETE;
825 // Rewrite from float to fixed and vice-versa
827 programIpRewriteStage1(node, dpid, providerSegmentationId, true /* isInbound */,
828 floatingIpAddress, fixedIPAddress, actionForNode);
829 programIpRewriteStage1(node, dpid, providerSegmentationId, false /* isInboubd */,
830 fixedIPAddress, floatingIpAddress, actionForNode);
832 // Respond to arps for the floating ip address
834 programStaticArpStage1(node, dpid, providerSegmentationId, routerMacAddress, floatingIpAddress,
839 private void programIpRewriteStage1(Node node, Long dpid, String providerSegmentationId,
840 final boolean isInbound,
841 String matchAddress, String rewriteAddress,
842 Action actionForNode) {
843 // Based on the local cache, figure out whether programming needs to occur. To do this, we
844 // will look at desired action for node.
846 final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" +
847 matchAddress + ":" + rewriteAddress;
848 final Boolean isProgrammed = isInbound ?
849 inboundIpRewriteCache.contains(cacheKey) :
850 outboundIpRewriteCache.contains(cacheKey);
852 if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
853 logger.trace("programIpRewriteStage1 node {} providerId {} {} matchAddr {} rewriteAddr {} action {}" +
855 node.getNodeId().getValue(), providerSegmentationId, isInbound ? "inbound": "outbound",
856 matchAddress, rewriteAddress, actionForNode);
859 if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
860 logger.trace("programIpRewriteStage1 node {} providerId {} {} matchAddr {} rewriteAddr {} action {}" +
862 node.getNodeId().getValue(), providerSegmentationId, isInbound ? "inbound": "outbound",
863 matchAddress, rewriteAddress, actionForNode);
867 Status status = this.programIpRewriteStage2(node, dpid, providerSegmentationId, isInbound,
868 matchAddress, rewriteAddress, actionForNode);
869 if (status.isSuccess()) {
871 if (actionForNode == Action.ADD) {
873 inboundIpRewriteCache.add(cacheKey);
875 outboundIpRewriteCache.add(cacheKey);
879 inboundIpRewriteCache.remove(cacheKey);
881 outboundIpRewriteCache.remove(cacheKey);
887 private Status programIpRewriteStage2(Node node, Long dpid, String providerSegmentationId,
888 final boolean isInbound,
889 String matchAddress, String rewriteAddress,
890 Action actionForNode) {
893 InetAddress inetMatchAddress = InetAddress.getByName(matchAddress);
894 InetAddress inetRewriteAddress = InetAddress.getByName(rewriteAddress);
896 status = inboundNatProvider == null ?
897 new Status(StatusCode.SUCCESS) :
898 inboundNatProvider.programIpRewriteRule(dpid, providerSegmentationId,
899 inetMatchAddress, inetRewriteAddress, actionForNode);
901 status = outboundNatProvider == null ?
902 new Status(StatusCode.SUCCESS) :
903 outboundNatProvider.programIpRewriteRule(dpid, providerSegmentationId,
904 inetMatchAddress, inetRewriteAddress, actionForNode);
906 } catch (UnknownHostException e) {
907 status = new Status(StatusCode.BADREQUEST);
910 if (status.isSuccess()) {
911 final boolean isSkipped = isInbound ? inboundNatProvider == null : outboundNatProvider == null;
912 logger.debug("ProgramIpRewrite {} {} for match:{} rewrite:{} node:{} action:{}",
913 (isInbound ? "inbound" : "outbound"), (isSkipped ? "skipped" : "programmed"),
914 matchAddress, rewriteAddress, node, actionForNode);
916 logger.error("ProgramIpRewrite {} failed for match:{} rewrite:{} node:{} action:{} status:{}",
917 (isInbound ? "inbound" : "outbound"),
918 matchAddress, rewriteAddress, node, actionForNode, status);
927 private int getMaskLenFromCidr(String cidr) {
928 if (cidr == null) return 0;
929 String[] splits = cidr.split("/");
930 if (splits.length != 2) return 0;
934 result = Integer.parseInt(splits[1].trim());
936 catch (NumberFormatException nfe)
943 private Long getDpid (Node node) {
944 /* TODO SB_MIGRATION */
945 Preconditions.checkNotNull(ovsdbConfigurationService);
947 String bridgeName = configurationService.getIntegrationBridgeName();
948 String bridgeUuid = this.getInternalBridgeUUID(node, bridgeName);
949 if (bridgeUuid == null) {
950 logger.error("Unable to spot Bridge Identifier for {} in {}", bridgeName, node);
955 Row bridgeRow = ovsdbConfigurationService
956 .getRow(node, ovsdbConfigurationService.getTableName(node, Bridge.class), bridgeUuid);
957 Bridge bridge = ovsdbConfigurationService.getTypedRow(node, Bridge.class, bridgeRow);
958 Set<String> dpids = bridge.getDatapathIdColumn().getData();
959 if (dpids == null || dpids.size() == 0) return 0L;
960 return StringConvertor.dpidStringToLong((String) dpids.toArray()[0]);
961 } catch (Exception e) {
962 logger.error("Error finding Bridge's OF DPID", e);
967 private String getInternalBridgeUUID (Node node, String bridgeName) {
968 /* TODO SB_MIGRATION */
969 Preconditions.checkNotNull(ovsdbConfigurationService);
971 Map<String, Row> bridgeTable =
972 ovsdbConfigurationService.getRows(node,
973 ovsdbConfigurationService.getTableName(node, Bridge.class));
974 if (bridgeTable == null) return null;
975 for (String key : bridgeTable.keySet()) {
976 Bridge bridge = ovsdbConfigurationService.getTypedRow(node, Bridge.class, bridgeTable.get(key));
977 if (bridge.getName().equals(bridgeName)) return key;
979 } catch (Exception e) {
980 logger.error("Error getting Bridge Identifier for {} / {}", node, bridgeName, e);