| /* |
| * Copyright 2016-present Open Networking Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.onosproject.openstacknetworking.impl; |
| |
| import com.google.common.base.Strings; |
| import org.onlab.packet.Ethernet; |
| import org.onlab.packet.VlanId; |
| import org.onosproject.cfg.ComponentConfigService; |
| import org.onosproject.cfg.ConfigProperty; |
| import org.onosproject.cluster.ClusterService; |
| import org.onosproject.cluster.LeadershipService; |
| import org.onosproject.cluster.NodeId; |
| import org.onosproject.core.ApplicationId; |
| import org.onosproject.core.CoreService; |
| import org.onosproject.mastership.MastershipService; |
| import org.onosproject.net.PortNumber; |
| import org.onosproject.net.device.DeviceService; |
| import org.onosproject.net.driver.DriverService; |
| import org.onosproject.net.flow.DefaultTrafficSelector; |
| import org.onosproject.net.flow.DefaultTrafficTreatment; |
| import org.onosproject.net.flow.TrafficSelector; |
| import org.onosproject.net.flow.TrafficTreatment; |
| import org.onosproject.openstacknetworking.api.InstancePort; |
| import org.onosproject.openstacknetworking.api.InstancePortEvent; |
| import org.onosproject.openstacknetworking.api.InstancePortListener; |
| import org.onosproject.openstacknetworking.api.InstancePortService; |
| import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService; |
| import org.onosproject.openstacknetworking.api.OpenstackNetwork.Type; |
| import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent; |
| import org.onosproject.openstacknetworking.api.OpenstackNetworkListener; |
| import org.onosproject.openstacknetworking.api.OpenstackNetworkService; |
| import org.onosproject.openstacknode.api.OpenstackNode; |
| import org.onosproject.openstacknode.api.OpenstackNodeService; |
| import org.openstack4j.model.network.Network; |
| import org.openstack4j.model.network.Port; |
| import org.osgi.service.component.annotations.Activate; |
| import org.osgi.service.component.annotations.Component; |
| import org.osgi.service.component.annotations.Deactivate; |
| import org.osgi.service.component.annotations.Reference; |
| import org.osgi.service.component.annotations.ReferenceCardinality; |
| import org.slf4j.Logger; |
| |
| import java.util.Objects; |
| import java.util.Set; |
| import java.util.concurrent.ExecutorService; |
| |
| import static java.util.concurrent.Executors.newSingleThreadExecutor; |
| import static org.onlab.util.Tools.groupedThreads; |
| import static org.onosproject.openstacknetworking.api.Constants.ACL_EGRESS_TABLE; |
| import static org.onosproject.openstacknetworking.api.Constants.ARP_BROADCAST_MODE; |
| import static org.onosproject.openstacknetworking.api.Constants.ARP_TABLE; |
| import static org.onosproject.openstacknetworking.api.Constants.DHCP_TABLE; |
| import static org.onosproject.openstacknetworking.api.Constants.FLAT_TABLE; |
| import static org.onosproject.openstacknetworking.api.Constants.FORWARDING_TABLE; |
| import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID; |
| import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ADMIN_RULE; |
| import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLAT_DOWNSTREAM_RULE; |
| import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLAT_JUMP_DOWNSTREAM_RULE; |
| import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLAT_JUMP_UPSTREAM_RULE; |
| import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLAT_UPSTREAM_RULE; |
| import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_SWITCHING_RULE; |
| import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_TUNNEL_TAG_RULE; |
| import static org.onosproject.openstacknetworking.api.Constants.STAT_FLAT_OUTBOUND_TABLE; |
| import static org.onosproject.openstacknetworking.api.Constants.VTAG_TABLE; |
| import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_MIGRATION_STARTED; |
| import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getPropertyValue; |
| import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.swapStaleLocation; |
| import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.tunnelPortNumByNetId; |
| import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildExtension; |
| import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE; |
| import static org.slf4j.LoggerFactory.getLogger; |
| |
| |
| /** |
| * Populates switching flow rules on OVS for the basic connectivity among the |
| * virtual instances in the same network. |
| */ |
| @Component(immediate = true) |
| public class OpenstackSwitchingHandler { |
| |
| private final Logger log = getLogger(getClass()); |
| |
| private static final String ARP_MODE = "arpMode"; |
| private static final String ERR_SET_FLOWS_VNI = "Failed to set flows for " + |
| "%s: Failed to get VNI for %s"; |
| private static final String STR_NONE = "<none>"; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected CoreService coreService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected MastershipService mastershipService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected DeviceService deviceService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected DriverService driverService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected ClusterService clusterService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected ComponentConfigService configService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| |
| protected LeadershipService leadershipService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected OpenstackFlowRuleService osFlowRuleService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected InstancePortService instancePortService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected OpenstackNetworkService osNetworkService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected OpenstackNodeService osNodeService; |
| |
| private final ExecutorService eventExecutor = newSingleThreadExecutor( |
| groupedThreads(this.getClass().getSimpleName(), "event-handler")); |
| private final InstancePortListener instancePortListener = new InternalInstancePortListener(); |
| private final InternalOpenstackNetworkListener osNetworkListener = |
| new InternalOpenstackNetworkListener(); |
| private ApplicationId appId; |
| private NodeId localNodeId; |
| |
| @Activate |
| protected void activate() { |
| appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID); |
| localNodeId = clusterService.getLocalNode().id(); |
| instancePortService.addListener(instancePortListener); |
| osNetworkService.addListener(osNetworkListener); |
| |
| log.info("Started"); |
| } |
| |
| @Deactivate |
| protected void deactivate() { |
| osNetworkService.removeListener(osNetworkListener); |
| instancePortService.removeListener(instancePortListener); |
| eventExecutor.shutdown(); |
| |
| log.info("Stopped"); |
| } |
| |
| private void setFlatJumpRules(InstancePort port, boolean install) { |
| TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); |
| selector.matchInPort(port.portNumber()); |
| |
| TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); |
| treatment.transition(STAT_FLAT_OUTBOUND_TABLE); |
| |
| osFlowRuleService.setRule( |
| appId, |
| port.deviceId(), |
| selector.build(), |
| treatment.build(), |
| PRIORITY_FLAT_JUMP_UPSTREAM_RULE, |
| DHCP_TABLE, |
| install); |
| |
| Network network = osNetworkService.network(port.networkId()); |
| |
| if (network == null) { |
| log.warn("The network does not exist"); |
| return; |
| } |
| PortNumber portNumber = osNodeService.node(port.deviceId()) |
| .phyIntfPortNum(network.getProviderPhyNet()); |
| |
| if (portNumber == null) { |
| log.warn("The port number does not exist"); |
| return; |
| } |
| |
| selector = DefaultTrafficSelector.builder(); |
| selector.matchInPort(portNumber) |
| .matchEthType(Ethernet.TYPE_IPV4) |
| .matchIPDst(port.ipAddress().toIpPrefix()); |
| |
| osFlowRuleService.setRule( |
| appId, |
| port.deviceId(), |
| selector.build(), |
| treatment.build(), |
| PRIORITY_FLAT_JUMP_DOWNSTREAM_RULE, |
| DHCP_TABLE, |
| install); |
| |
| selector = DefaultTrafficSelector.builder(); |
| selector.matchInPort(portNumber) |
| .matchEthType(Ethernet.TYPE_ARP) |
| .matchArpTpa(port.ipAddress().getIp4Address()); |
| |
| osFlowRuleService.setRule( |
| appId, |
| port.deviceId(), |
| selector.build(), |
| treatment.build(), |
| PRIORITY_FLAT_JUMP_DOWNSTREAM_RULE, |
| DHCP_TABLE, |
| install); |
| } |
| |
| private void setDownstreamRulesForFlat(InstancePort instPort, boolean install) { |
| TrafficSelector selector = DefaultTrafficSelector.builder() |
| .matchEthType(Ethernet.TYPE_IPV4) |
| .matchIPDst(instPort.ipAddress().toIpPrefix()) |
| .build(); |
| TrafficTreatment treatment = DefaultTrafficTreatment.builder() |
| .setOutput(instPort.portNumber()) |
| .build(); |
| |
| osFlowRuleService.setRule( |
| appId, |
| instPort.deviceId(), |
| selector, |
| treatment, |
| PRIORITY_FLAT_DOWNSTREAM_RULE, |
| FLAT_TABLE, |
| install); |
| |
| selector = DefaultTrafficSelector.builder() |
| .matchEthType(Ethernet.TYPE_ARP) |
| .matchArpTpa(instPort.ipAddress().getIp4Address()) |
| .build(); |
| |
| osFlowRuleService.setRule( |
| appId, |
| instPort.deviceId(), |
| selector, |
| treatment, |
| PRIORITY_FLAT_DOWNSTREAM_RULE, |
| FLAT_TABLE, |
| install); |
| } |
| |
| private void setUpstreamRulesForFlat(InstancePort instPort, boolean install) { |
| TrafficSelector selector = DefaultTrafficSelector.builder() |
| .matchInPort(instPort.portNumber()) |
| .build(); |
| |
| Network network = osNetworkService.network(instPort.networkId()); |
| |
| if (network == null) { |
| log.warn("The network does not exist"); |
| return; |
| } |
| |
| PortNumber portNumber = osNodeService.node(instPort.deviceId()) |
| .phyIntfPortNum(network.getProviderPhyNet()); |
| |
| if (portNumber == null) { |
| log.warn("The port number does not exist"); |
| return; |
| } |
| |
| TrafficTreatment treatment = DefaultTrafficTreatment.builder() |
| .setOutput(portNumber) |
| .build(); |
| |
| osFlowRuleService.setRule( |
| appId, |
| instPort.deviceId(), |
| selector, |
| treatment, |
| PRIORITY_FLAT_UPSTREAM_RULE, |
| FLAT_TABLE, |
| install); |
| } |
| |
| /** |
| * Configures the flow rules which are used for L2 packet switching. |
| * Note that these rules will be inserted in switching table (table 5). |
| * |
| * @param instPort instance port object |
| * @param install install flag, add the rule if true, remove it otherwise |
| */ |
| private void setForwardingRulesForTunnel(InstancePort instPort, boolean install) { |
| // switching rules for the instPorts in the same node |
| TrafficSelector selector = DefaultTrafficSelector.builder() |
| // TODO: need to handle IPv6 in near future |
| .matchEthType(Ethernet.TYPE_IPV4) |
| .matchIPDst(instPort.ipAddress().toIpPrefix()) |
| .matchTunnelId(getVni(instPort)) |
| .build(); |
| |
| TrafficTreatment treatment = DefaultTrafficTreatment.builder() |
| // TODO: this might not be necessary for the VMs located in the same subnet |
| .setEthDst(instPort.macAddress()) |
| .setOutput(instPort.portNumber()) |
| .build(); |
| |
| osFlowRuleService.setRule( |
| appId, |
| instPort.deviceId(), |
| selector, |
| treatment, |
| PRIORITY_SWITCHING_RULE, |
| FORWARDING_TABLE, |
| install); |
| |
| // switching rules for the instPorts in the remote node |
| OpenstackNode localNode = osNodeService.node(instPort.deviceId()); |
| if (localNode == null) { |
| final String error = String.format("Cannot find openstack node for %s", |
| instPort.deviceId()); |
| throw new IllegalStateException(error); |
| } |
| osNodeService.completeNodes(COMPUTE).stream() |
| .filter(remoteNode -> !remoteNode.intgBridge().equals(localNode.intgBridge())) |
| .forEach(remoteNode -> { |
| PortNumber portNum = tunnelPortNumByNetId(instPort.networkId(), |
| osNetworkService, remoteNode); |
| TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder() |
| .extension(buildExtension( |
| deviceService, |
| remoteNode.intgBridge(), |
| localNode.dataIp().getIp4Address()), |
| remoteNode.intgBridge()) |
| .setOutput(portNum) |
| .build(); |
| |
| osFlowRuleService.setRule( |
| appId, |
| remoteNode.intgBridge(), |
| selector, |
| treatmentToRemote, |
| PRIORITY_SWITCHING_RULE, |
| FORWARDING_TABLE, |
| install); |
| }); |
| } |
| |
| /** |
| * Configures the flow rules which are used for L2 VLAN packet switching. |
| * Note that these rules will be inserted in switching table (table 5). |
| * |
| * @param instPort instance port object |
| * @param install install flag, add the rule if true, remove it otherwise |
| */ |
| private void setForwardingRulesForVlan(InstancePort instPort, boolean install) { |
| // switching rules for the instPorts in the same node |
| TrafficSelector selector = DefaultTrafficSelector.builder() |
| // TODO: need to handle IPv6 in near future |
| .matchEthType(Ethernet.TYPE_IPV4) |
| .matchIPDst(instPort.ipAddress().toIpPrefix()) |
| .matchVlanId(getVlanId(instPort)) |
| .build(); |
| |
| TrafficTreatment treatment = DefaultTrafficTreatment.builder() |
| .popVlan() |
| // TODO: this might not be necessary for the VMs located in the same subnet |
| .setEthDst(instPort.macAddress()) |
| .setOutput(instPort.portNumber()) |
| .build(); |
| |
| osFlowRuleService.setRule( |
| appId, |
| instPort.deviceId(), |
| selector, |
| treatment, |
| PRIORITY_SWITCHING_RULE, |
| FORWARDING_TABLE, |
| install); |
| |
| // switching rules for the instPorts in the remote node |
| osNodeService.completeNodes(COMPUTE).stream() |
| .filter(remoteNode -> !remoteNode.intgBridge().equals(instPort.deviceId()) && |
| remoteNode.vlanIntf() != null) |
| .forEach(remoteNode -> { |
| TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder() |
| .setEthDst(instPort.macAddress()) |
| .setOutput(remoteNode.vlanPortNum()) |
| .build(); |
| |
| osFlowRuleService.setRule( |
| appId, |
| remoteNode.intgBridge(), |
| selector, |
| treatmentToRemote, |
| PRIORITY_SWITCHING_RULE, |
| FORWARDING_TABLE, |
| install); |
| }); |
| } |
| |
| private void setTunnelTagArpFlowRules(InstancePort instPort, boolean install) { |
| setTunnelTagFlowRules(instPort, Ethernet.TYPE_ARP, install); |
| } |
| |
| private void setTunnelTagIpFlowRules(InstancePort instPort, boolean install) { |
| setTunnelTagFlowRules(instPort, Ethernet.TYPE_IPV4, install); |
| } |
| |
| /** |
| * Configures the flow rule which is for using VXLAN/GRE to tag the packet |
| * based on the in_port number of a virtual instance. |
| * Note that this rule will be inserted in vTag table. |
| * |
| * @param instPort instance port object |
| * @param install install flag, add the rule if true, remove it otherwise |
| */ |
| private void setTunnelTagFlowRules(InstancePort instPort, |
| short ethType, |
| boolean install) { |
| TrafficSelector selector = DefaultTrafficSelector.builder() |
| .matchEthType(ethType) |
| .matchInPort(instPort.portNumber()) |
| .build(); |
| |
| TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder() |
| .setTunnelId(getVni(instPort)); |
| |
| |
| if (ethType == Ethernet.TYPE_ARP) { |
| tBuilder.transition(ARP_TABLE); |
| } else if (ethType == Ethernet.TYPE_IPV4) { |
| tBuilder.transition(ACL_EGRESS_TABLE); |
| } |
| |
| osFlowRuleService.setRule( |
| appId, |
| instPort.deviceId(), |
| selector, |
| tBuilder.build(), |
| PRIORITY_TUNNEL_TAG_RULE, |
| VTAG_TABLE, |
| install); |
| } |
| |
| private void setVlanTagIpFlowRules(InstancePort instPort, boolean install) { |
| setVlanTagFlowRules(instPort, Ethernet.TYPE_IPV4, install); |
| } |
| |
| private void setVlanTagArpFlowRules(InstancePort instPort, boolean install) { |
| setVlanTagFlowRules(instPort, Ethernet.TYPE_ARP, install); |
| } |
| |
| /** |
| * Configures the flow rule which is for using VLAN to tag the packet |
| * based on the in_port number of a virtual instance. |
| * Note that this rule will be inserted in vTag table. |
| * |
| * @param instPort instance port object |
| * @param install install flag, add the rule if true, remove it otherwise |
| */ |
| private void setVlanTagFlowRules(InstancePort instPort, |
| short ethType, |
| boolean install) { |
| TrafficSelector selector = DefaultTrafficSelector.builder() |
| .matchEthType(ethType) |
| .matchInPort(instPort.portNumber()) |
| .build(); |
| |
| TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder() |
| .pushVlan() |
| .setVlanId(getVlanId(instPort)); |
| |
| if (ethType == Ethernet.TYPE_ARP) { |
| tBuilder.transition(ARP_TABLE); |
| } else if (ethType == Ethernet.TYPE_IPV4) { |
| tBuilder.transition(ACL_EGRESS_TABLE); |
| } |
| |
| osFlowRuleService.setRule( |
| appId, |
| instPort.deviceId(), |
| selector, |
| tBuilder.build(), |
| PRIORITY_TUNNEL_TAG_RULE, |
| VTAG_TABLE, |
| install); |
| } |
| |
| private void setNetworkBlockRules(Network network, boolean install) { |
| |
| Type type = osNetworkService.networkType(network.getId()); |
| |
| // TODO: we block a network traffic by referring to segment ID for now |
| // we might need to find a better way to block the traffic of a network |
| // in case the segment ID is overlapped in different types network (VXLAN, GRE, VLAN) |
| switch (type) { |
| case VXLAN: |
| case GRE: |
| case GENEVE: |
| setNetworkBlockRulesForTunnel(network.getProviderSegID(), install); |
| break; |
| case VLAN: |
| setNetworkBlockRulesForVlan(network.getProviderSegID(), install); |
| break; |
| case FLAT: |
| // TODO: need to find a way to block flat typed network |
| break; |
| default: |
| break; |
| } |
| } |
| |
| private void setNetworkBlockRulesForTunnel(String segmentId, boolean install) { |
| TrafficSelector selector = DefaultTrafficSelector.builder() |
| .matchTunnelId(Long.valueOf(segmentId)) |
| .build(); |
| |
| TrafficTreatment treatment = DefaultTrafficTreatment.builder() |
| .drop() |
| .build(); |
| |
| osNodeService.completeNodes(COMPUTE) |
| .forEach(osNode -> |
| osFlowRuleService.setRule( |
| appId, |
| osNode.intgBridge(), |
| selector, |
| treatment, |
| PRIORITY_ADMIN_RULE, |
| ACL_EGRESS_TABLE, |
| install) |
| ); |
| } |
| |
| private void setNetworkBlockRulesForVlan(String segmentId, boolean install) { |
| TrafficSelector selector = DefaultTrafficSelector.builder() |
| .matchTunnelId(Long.valueOf(segmentId)) |
| .build(); |
| |
| TrafficTreatment treatment = DefaultTrafficTreatment.builder() |
| .drop() |
| .build(); |
| |
| osNodeService.completeNodes(COMPUTE) |
| .forEach(osNode -> |
| osFlowRuleService.setRule( |
| appId, |
| osNode.intgBridge(), |
| selector, |
| treatment, |
| PRIORITY_ADMIN_RULE, |
| ACL_EGRESS_TABLE, |
| install) |
| ); |
| } |
| |
| private void setPortBlockRules(InstancePort instPort, boolean install) { |
| TrafficSelector selector = DefaultTrafficSelector.builder() |
| .matchInPort(instPort.portNumber()) |
| .build(); |
| |
| TrafficTreatment treatment = DefaultTrafficTreatment.builder() |
| .drop() |
| .build(); |
| |
| osFlowRuleService.setRule( |
| appId, |
| instPort.deviceId(), |
| selector, |
| treatment, |
| PRIORITY_ADMIN_RULE, |
| VTAG_TABLE, |
| install); |
| } |
| |
| /** |
| * Obtains the VLAN ID from the given instance port. |
| * |
| * @param instPort instance port object |
| * @return VLAN ID |
| */ |
| private VlanId getVlanId(InstancePort instPort) { |
| Network osNet = osNetworkService.network(instPort.networkId()); |
| |
| if (osNet == null || Strings.isNullOrEmpty(osNet.getProviderSegID())) { |
| final String error = |
| String.format(ERR_SET_FLOWS_VNI, |
| instPort, osNet == null ? STR_NONE : osNet.getName()); |
| throw new IllegalStateException(error); |
| } |
| |
| return VlanId.vlanId(osNet.getProviderSegID()); |
| } |
| |
| /** |
| * Obtains the VNI from the given instance port. |
| * |
| * @param instPort instance port object |
| * @return Virtual Network Identifier (VNI) |
| */ |
| private Long getVni(InstancePort instPort) { |
| Network osNet = osNetworkService.network(instPort.networkId()); |
| if (osNet == null || Strings.isNullOrEmpty(osNet.getProviderSegID())) { |
| final String error = |
| String.format(ERR_SET_FLOWS_VNI, |
| instPort, osNet == null ? STR_NONE : osNet.getName()); |
| throw new IllegalStateException(error); |
| } |
| return Long.valueOf(osNet.getProviderSegID()); |
| } |
| |
| private String getArpMode() { |
| Set<ConfigProperty> properties = |
| configService.getProperties(OpenstackSwitchingArpHandler.class.getName()); |
| return getPropertyValue(properties, ARP_MODE); |
| } |
| |
| /** |
| * An internal instance port listener which listens the port events generated |
| * from VM. The corresponding L2 forwarding rules will be generated and |
| * inserted to integration bridge only if a new VM port is detected. If the |
| * existing detected VM port is removed due to VM purge, we will remove the |
| * corresponding L2 forwarding to as well for the sake of resource saving. |
| */ |
| private class InternalInstancePortListener implements InstancePortListener { |
| |
| private boolean isRelevantHelper(InstancePortEvent event) { |
| return mastershipService.isLocalMaster(event.subject().deviceId()); |
| } |
| |
| @Override |
| public void event(InstancePortEvent event) { |
| InstancePort instPort = event.subject(); |
| |
| switch (event.type()) { |
| case OPENSTACK_INSTANCE_PORT_DETECTED: |
| case OPENSTACK_INSTANCE_PORT_UPDATED: |
| case OPENSTACK_INSTANCE_MIGRATION_STARTED: |
| case OPENSTACK_INSTANCE_RESTARTED: |
| eventExecutor.execute(() -> |
| processInstanceDetection(event, instPort)); |
| break; |
| case OPENSTACK_INSTANCE_TERMINATED: |
| eventExecutor.execute(() -> |
| processInstanceTermination(event, instPort)); |
| break; |
| case OPENSTACK_INSTANCE_PORT_VANISHED: |
| eventExecutor.execute(() -> |
| processInstanceRemoval(event, instPort)); |
| break; |
| case OPENSTACK_INSTANCE_MIGRATION_ENDED: |
| eventExecutor.execute(() -> |
| processInstanceMigrationEnd(event, instPort)); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| private void processInstanceDetection(InstancePortEvent event, |
| InstancePort instPort) { |
| if (!isRelevantHelper(event)) { |
| return; |
| } |
| |
| if (event.type() == OPENSTACK_INSTANCE_MIGRATION_STARTED) { |
| log.info("SwitchingHandler: Migration started at MAC:{} IP:{}", |
| instPort.macAddress(), |
| instPort.ipAddress()); |
| } else { |
| log.info("SwitchingHandler: Instance port detected MAC:{} IP:{}", |
| instPort.macAddress(), |
| instPort.ipAddress()); |
| } |
| |
| instPortDetected(instPort); |
| |
| Port osPort = osNetworkService.port(instPort.portId()); |
| |
| if (osPort != null) { |
| setPortBlockRules(instPort, !osPort.isAdminStateUp()); |
| } |
| } |
| |
| private void processInstanceTermination(InstancePortEvent event, |
| InstancePort instPort) { |
| if (!isRelevantHelper(event)) { |
| return; |
| } |
| |
| log.info("SwitchingHandler: Instance port terminated MAC:{} IP:{}", |
| instPort.macAddress(), |
| instPort.ipAddress()); |
| |
| removeVportRules(instPort); |
| } |
| |
| private void processInstanceRemoval(InstancePortEvent event, |
| InstancePort instPort) { |
| if (!isRelevantHelper(event)) { |
| return; |
| } |
| |
| log.info("SwitchingHandler: Instance port vanished MAC:{} IP:{}", |
| instPort.macAddress(), |
| instPort.ipAddress()); |
| |
| instPortRemoved(instPort); |
| |
| Port osPort = osNetworkService.port(instPort.portId()); |
| |
| if (osPort != null) { |
| setPortBlockRules(instPort, false); |
| } |
| } |
| |
| private void processInstanceMigrationEnd(InstancePortEvent event, |
| InstancePort instPort) { |
| if (!isRelevantHelper(event)) { |
| return; |
| } |
| |
| log.info("SwitchingHandler: Migration finished for MAC:{} IP:{}", |
| instPort.macAddress(), |
| instPort.ipAddress()); |
| |
| InstancePort revisedInstPort = swapStaleLocation(instPort); |
| |
| removeVportRules(revisedInstPort); |
| } |
| |
| /** |
| * Configures L2 forwarding rules. |
| * Currently, SONA supports Flat, VXLAN, GRE, GENEVE and VLAN modes. |
| * |
| * @param instPort instance port object |
| * @param install install flag, add the rule if true, remove it otherwise |
| */ |
| private void setNetworkRules(InstancePort instPort, boolean install) { |
| Type type = osNetworkService.networkType(instPort.networkId()); |
| |
| switch (type) { |
| case VXLAN: |
| case GRE: |
| case GENEVE: |
| setNetworkRulesForTunnel(instPort, install); |
| break; |
| case VLAN: |
| setNetworkRulesForVlan(instPort, install); |
| break; |
| case FLAT: |
| setNetworkRulesForFlat(instPort, install); |
| break; |
| default: |
| log.warn("Unsupported network tunnel type {}", type.name()); |
| break; |
| } |
| } |
| |
| private void setNetworkRulesForTunnel(InstancePort instPort, boolean install) { |
| setTunnelTagIpFlowRules(instPort, install); |
| setForwardingRulesForTunnel(instPort, install); |
| |
| if (ARP_BROADCAST_MODE.equals(getArpMode())) { |
| setTunnelTagArpFlowRules(instPort, install); |
| } |
| } |
| |
| private void setNetworkRulesForVlan(InstancePort instPort, boolean install) { |
| setVlanTagIpFlowRules(instPort, install); |
| setForwardingRulesForVlan(instPort, install); |
| |
| if (ARP_BROADCAST_MODE.equals(getArpMode())) { |
| setVlanTagArpFlowRules(instPort, install); |
| } |
| } |
| |
| private void setNetworkRulesForFlat(InstancePort instPort, boolean install) { |
| setFlatJumpRules(instPort, install); |
| setDownstreamRulesForFlat(instPort, install); |
| setUpstreamRulesForFlat(instPort, install); |
| } |
| |
| /** |
| * Removes virtual port related flow rules. |
| * |
| * @param instPort instance port |
| */ |
| private void removeVportRules(InstancePort instPort) { |
| Type type = osNetworkService.networkType(instPort.networkId()); |
| |
| switch (type) { |
| case VXLAN: |
| case GRE: |
| case GENEVE: |
| removeVportRulesForTunnel(instPort); |
| break; |
| case VLAN: |
| removeVportRulesForVlan(instPort); |
| break; |
| case FLAT: |
| removeVportRulesForFlat(instPort); |
| break; |
| default: |
| log.warn("Unsupported network type {}", type.name()); |
| break; |
| } |
| } |
| |
| private void removeVportRulesForTunnel(InstancePort instPort) { |
| setTunnelTagIpFlowRules(instPort, false); |
| |
| if (ARP_BROADCAST_MODE.equals(getArpMode())) { |
| setTunnelTagArpFlowRules(instPort, false); |
| } |
| } |
| |
| private void removeVportRulesForVlan(InstancePort instPort) { |
| setVlanTagIpFlowRules(instPort, false); |
| |
| if (ARP_BROADCAST_MODE.equals(getArpMode())) { |
| setVlanTagArpFlowRules(instPort, false); |
| } |
| } |
| |
| private void removeVportRulesForFlat(InstancePort instPort) { |
| setFlatJumpRules(instPort, false); |
| setUpstreamRulesForFlat(instPort, false); |
| setDownstreamRulesForFlat(instPort, false); |
| } |
| |
| private void instPortDetected(InstancePort instPort) { |
| setNetworkRules(instPort, true); |
| // TODO add something else if needed |
| } |
| |
| private void instPortRemoved(InstancePort instPort) { |
| setNetworkRules(instPort, false); |
| // TODO add something else if needed |
| } |
| } |
| |
| private class InternalOpenstackNetworkListener implements OpenstackNetworkListener { |
| |
| @Override |
| public boolean isRelevant(OpenstackNetworkEvent event) { |
| return event.subject() != null && event.port() != null; |
| } |
| |
| private boolean isRelevantHelper() { |
| return Objects.equals(localNodeId, leadershipService.getLeader(appId.name())); |
| } |
| |
| @Override |
| public void event(OpenstackNetworkEvent event) { |
| switch (event.type()) { |
| case OPENSTACK_NETWORK_CREATED: |
| case OPENSTACK_NETWORK_UPDATED: |
| eventExecutor.execute(() -> processNetworkAddition(event)); |
| break; |
| case OPENSTACK_NETWORK_REMOVED: |
| eventExecutor.execute(() -> processNetworkRemoval(event)); |
| break; |
| case OPENSTACK_PORT_CREATED: |
| case OPENSTACK_PORT_UPDATED: |
| eventExecutor.execute(() -> processPortAddition(event)); |
| break; |
| case OPENSTACK_PORT_REMOVED: |
| eventExecutor.execute(() -> processPortRemoval(event)); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| private void processNetworkAddition(OpenstackNetworkEvent event) { |
| if (!isRelevantHelper()) { |
| return; |
| } |
| |
| boolean isNwAdminStateUp = event.subject().isAdminStateUp(); |
| setNetworkBlockRules(event.subject(), !isNwAdminStateUp); |
| } |
| |
| private void processNetworkRemoval(OpenstackNetworkEvent event) { |
| if (!isRelevantHelper()) { |
| return; |
| } |
| |
| setNetworkBlockRules(event.subject(), false); |
| } |
| |
| private void processPortAddition(OpenstackNetworkEvent event) { |
| if (!isRelevantHelper()) { |
| return; |
| } |
| |
| boolean isPortAdminStateUp = event.port().isAdminStateUp(); |
| String portId = event.port().getId(); |
| InstancePort instPort = instancePortService.instancePort(portId); |
| if (instPort != null) { |
| setPortBlockRules(instPort, !isPortAdminStateUp); |
| } |
| } |
| |
| private void processPortRemoval(OpenstackNetworkEvent event) { |
| if (!isRelevantHelper()) { |
| return; |
| } |
| |
| String portId = event.port().getId(); |
| InstancePort instPort = instancePortService.instancePort(portId); |
| if (instPort != null) { |
| setPortBlockRules(instPort, false); |
| } |
| } |
| } |
| } |