blob: db324d75bee5d756da206873088671923819b2fb [file] [log] [blame]
/*
* Copyright 2016-present Open Networking Laboratory
*
* 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.switching;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onlab.packet.Ethernet;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.IpAddress;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DeviceService;
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.net.flowobjective.DefaultForwardingObjective;
import org.onosproject.net.flowobjective.FlowObjectiveService;
import org.onosproject.net.flowobjective.ForwardingObjective;
import org.onosproject.openstacknetworking.OpenstackSwitchingService;
import org.onosproject.openstacknetworking.AbstractVmHandler;
import org.onosproject.openstacknode.OpenstackNodeService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Objects;
import java.util.Optional;
import static org.onosproject.openstacknetworking.Constants.*;
import static org.onosproject.openstacknetworking.RulePopulatorUtil.buildExtension;
/**
* Populates switching flow rules.
*/
@Service
@Component(immediate = true)
public final class OpenstackSwitchingManager extends AbstractVmHandler
implements OpenstackSwitchingService {
private final Logger log = LoggerFactory.getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected FlowObjectiveService flowObjectiveService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected OpenstackNodeService nodeService;
private ApplicationId appId;
@Activate
protected void activate() {
super.activate();
appId = coreService.registerApplication(SWITCHING_APP_ID);
}
@Deactivate
protected void deactivate() {
super.deactivate();
}
private void populateSwitchingRules(Host host) {
populateFlowRulesForTunnelTag(host);
populateFlowRulesForTrafficToSameCnode(host);
populateFlowRulesForTrafficToDifferentCnode(host);
log.debug("Populated switching rule for {}", host);
}
private void populateFlowRulesForTunnelTag(Host host) {
TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
sBuilder.matchEthType(Ethernet.TYPE_IPV4)
.matchInPort(host.location().port());
tBuilder.setTunnelId(Long.valueOf(getVni(host)));
ForwardingObjective fo = DefaultForwardingObjective.builder()
.withSelector(sBuilder.build())
.withTreatment(tBuilder.build())
.withPriority(TUNNELTAG_RULE_PRIORITY)
.withFlag(ForwardingObjective.Flag.SPECIFIC)
.fromApp(appId)
.add();
flowObjectiveService.forward(host.location().deviceId(), fo);
}
private void populateFlowRulesForTrafficToSameCnode(Host host) {
//For L2 Switching Case
TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
sBuilder.matchEthType(Ethernet.TYPE_IPV4)
.matchIPDst(getIp(host).toIpPrefix())
.matchTunnelId(Long.valueOf(getVni(host)));
tBuilder.setOutput(host.location().port());
ForwardingObjective fo = DefaultForwardingObjective.builder()
.withSelector(sBuilder.build())
.withTreatment(tBuilder.build())
.withPriority(SWITCHING_RULE_PRIORITY)
.withFlag(ForwardingObjective.Flag.SPECIFIC)
.fromApp(appId)
.add();
flowObjectiveService.forward(host.location().deviceId(), fo);
}
private void populateFlowRulesForTrafficToDifferentCnode(Host host) {
Ip4Address localVmIp = getIp(host);
DeviceId localDeviceId = host.location().deviceId();
Optional<IpAddress> localDataIp = nodeService.dataIp(localDeviceId);
if (!localDataIp.isPresent()) {
log.debug("Failed to get data IP for device {}",
host.location().deviceId());
return;
}
String vni = getVni(host);
getVmsInDifferentCnode(host).forEach(remoteVm -> {
Optional<IpAddress> remoteDataIp = nodeService.dataIp(remoteVm.location().deviceId());
if (remoteDataIp.isPresent()) {
setVxLanFlowRule(vni,
localDeviceId,
remoteDataIp.get().getIp4Address(),
getIp(remoteVm));
setVxLanFlowRule(vni,
remoteVm.location().deviceId(),
localDataIp.get().getIp4Address(),
localVmIp);
}
});
}
private void setVxLanFlowRule(String vni, DeviceId deviceId, Ip4Address remoteIp,
Ip4Address vmIp) {
Optional<PortNumber> tunnelPort = nodeService.tunnelPort(deviceId);
if (!tunnelPort.isPresent()) {
log.warn("Failed to get tunnel port from {}", deviceId);
return;
}
TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
sBuilder.matchEthType(Ethernet.TYPE_IPV4)
.matchTunnelId(Long.parseLong(vni))
.matchIPDst(vmIp.toIpPrefix());
tBuilder.extension(buildExtension(deviceService, deviceId, remoteIp), deviceId)
.setOutput(tunnelPort.get());
ForwardingObjective fo = DefaultForwardingObjective.builder()
.withSelector(sBuilder.build())
.withTreatment(tBuilder.build())
.withPriority(SWITCHING_RULE_PRIORITY)
.withFlag(ForwardingObjective.Flag.SPECIFIC)
.fromApp(appId)
.add();
flowObjectiveService.forward(deviceId, fo);
}
private void removeSwitchingRules(Host host) {
removeFlowRuleForTunnelTag(host);
removeFlowRuleForVMsInSameCnode(host);
removeFlowRuleForVMsInDiffrentCnode(host);
log.debug("Removed switching rule for {}", host);
}
private void removeFlowRuleForTunnelTag(Host host) {
TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
sBuilder.matchEthType(Ethernet.TYPE_IPV4)
.matchInPort(host.location().port());
ForwardingObjective fo = DefaultForwardingObjective.builder()
.withSelector(sBuilder.build())
.withTreatment(tBuilder.build())
.withPriority(TUNNELTAG_RULE_PRIORITY)
.withFlag(ForwardingObjective.Flag.SPECIFIC)
.fromApp(appId)
.remove();
flowObjectiveService.forward(host.location().deviceId(), fo);
}
private void removeFlowRuleForVMsInSameCnode(Host host) {
TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
sBuilder.matchEthType(Ethernet.TYPE_IPV4)
.matchIPDst(getIp(host).toIpPrefix())
.matchTunnelId(Long.valueOf(getVni(host)));
ForwardingObjective fo = DefaultForwardingObjective.builder()
.withSelector(sBuilder.build())
.withTreatment(DefaultTrafficTreatment.builder().build())
.withFlag(ForwardingObjective.Flag.SPECIFIC)
.withPriority(SWITCHING_RULE_PRIORITY)
.fromApp(appId)
.remove();
flowObjectiveService.forward(host.location().deviceId(), fo);
}
private void removeFlowRuleForVMsInDiffrentCnode(Host host) {
DeviceId deviceId = host.location().deviceId();
final boolean anyPortRemainedInSameCnode = hostService.getConnectedHosts(deviceId)
.stream()
.filter(this::isValidHost)
.anyMatch(h -> Objects.equals(getVni(h), getVni(host)));
getVmsInDifferentCnode(host).forEach(h -> {
removeVxLanFlowRule(h.location().deviceId(), getIp(host), getVni(host));
if (!anyPortRemainedInSameCnode) {
removeVxLanFlowRule(deviceId, getIp(h), getVni(host));
}
});
}
private void removeVxLanFlowRule(DeviceId deviceId, Ip4Address vmIp, String vni) {
TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
sBuilder.matchEthType(Ethernet.TYPE_IPV4)
.matchTunnelId(Long.valueOf(vni))
.matchIPDst(vmIp.toIpPrefix());
ForwardingObjective fo = DefaultForwardingObjective.builder()
.withSelector(sBuilder.build())
.withTreatment(DefaultTrafficTreatment.builder().build())
.withFlag(ForwardingObjective.Flag.SPECIFIC)
.withPriority(SWITCHING_RULE_PRIORITY)
.fromApp(appId)
.remove();
flowObjectiveService.forward(deviceId, fo);
}
@Override
protected void hostDetected(Host host) {
populateSwitchingRules(host);
log.info("Added new virtual machine to switching service {}", host);
}
@Override
protected void hostRemoved(Host host) {
removeSwitchingRules(host);
log.info("Removed virtual machine from switching service {}", host);
}
@Override
public void reinstallVmFlow(Host host) {
if (host == null) {
hostService.getHosts().forEach(h -> {
populateSwitchingRules(h);
log.info("Re-Install data plane flow of virtual machine {}", h);
});
} else {
populateSwitchingRules(host);
log.info("Re-Install data plane flow of virtual machine {}", host);
}
}
@Override
public void purgeVmFlow(Host host) {
if (host == null) {
hostService.getHosts().forEach(h -> {
removeSwitchingRules(h);
log.info("Purge data plane flow of virtual machine {}", h);
});
} else {
removeSwitchingRules(host);
log.info("Purge data plane flow of virtual machine {}", host);
}
}
}