Hyunsun Moon

[Falcon] CORD-366 Implemented CORD service dependency API and pipeline

Done
- Implement service dependency APIs
- Populate or remove basic tenant connectivity rules when VM created or removed
- Populate direct/indirect service access rules when service dependency created
- Remove service dependency rules

Todo
- Add/remove bucket to proper group when a VM is created or terminated
- Populate service dependency rules for existing VMs when service is activated
- Cleanup flow rules remove

Change-Id: I1daaf7ac9b41d7f2694605cb9b75f12d42144dbd
......@@ -18,8 +18,18 @@ package org.onosproject.cordvtn;
import com.google.common.base.MoreObjects;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onosproject.net.Host;
import org.onosproject.openstackswitching.OpenstackNetwork;
import org.onosproject.openstackswitching.OpenstackSubnet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onosproject.cordvtn.CordService.ServiceType.*;
import static org.onosproject.cordvtn.CordService.ServiceType.PRIVATE;
import static org.onosproject.cordvtn.CordService.ServiceType.PUBLIC_INDIRECT;
public final class CordService {
......@@ -36,23 +46,25 @@ public final class CordService {
private final ServiceType serviceType;
private final IpPrefix serviceIpRange;
private final IpAddress serviceIp;
private final Map<Host, IpAddress> hosts;
private final Set<CordServiceId> tenantServices;
/**
* Default constructor.
*
* @param id service id, which is identical to OpenStack network id
* @param segmentationId segmentation id, which is identical to VNI
* @param serviceType service type
* @param serviceIpRange service ip range
* @param serviceIp service ip
* @param vNet OpenStack network
* @param hosts host and tunnel ip map
* @param tenantServices list of tenant service ids
*/
public CordService(CordServiceId id, long segmentationId, ServiceType serviceType,
IpPrefix serviceIpRange, IpAddress serviceIp) {
this.id = id;
this.segmentationId = segmentationId;
this.serviceType = serviceType;
this.serviceIpRange = serviceIpRange;
this.serviceIp = serviceIp;
public CordService(OpenstackNetwork vNet, OpenstackSubnet subnet,
Map<Host, IpAddress> hosts, Set<CordServiceId> tenantServices) {
this.id = CordServiceId.of(vNet.id());
this.segmentationId = Long.parseLong(vNet.segmentId());
this.serviceType = getServiceType(vNet.name());
this.serviceIpRange = IpPrefix.valueOf(subnet.cidr());
this.serviceIp = IpAddress.valueOf(subnet.gatewayIp());
this.hosts = hosts;
this.tenantServices = tenantServices;
}
/**
......@@ -100,6 +112,24 @@ public final class CordService {
return serviceIp;
}
/**
* Returns hosts associated with this service.
*
* @return list of hosts
*/
public Map<Host, IpAddress> hosts() {
return hosts;
}
/**
* Returns tenant service IDs.
*
* @return list of tenant service id
*/
public Set<CordServiceId> tenantServices() {
return tenantServices;
}
@Override
public int hashCode() {
return Objects.hash(id);
......@@ -125,6 +155,33 @@ public final class CordService {
.add("serviceType", serviceType)
.add("serviceIpRange", serviceIpRange)
.add("serviceIp", serviceIp)
.add("tenantServices", tenantServices)
.toString();
}
/**
* Returns network type from network name.
* It assumes that network name contains network type.
*
* @param netName network name
* @return network type, or null if it doesn't match any type
*/
private ServiceType getServiceType(String netName) {
checkNotNull(netName);
String name = netName.toUpperCase();
if (name.contains(PRIVATE_DIRECT.toString())) {
return PRIVATE_DIRECT;
} else if (name.contains(PRIVATE_INDIRECT.toString())) {
return PRIVATE_INDIRECT;
} else if (name.contains(PUBLIC_DIRECT.toString())) {
return PUBLIC_DIRECT;
} else if (name.contains(PUBLIC_INDIRECT.toString())) {
return PUBLIC_INDIRECT;
} else if (name.contains(PRIVATE.toString())) {
return PRIVATE;
} else {
return null;
}
}
}
......
......@@ -15,7 +15,6 @@
*/
package org.onosproject.cordvtn;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.felix.scr.annotations.Activate;
......@@ -24,21 +23,23 @@ 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.Ip4Address;
import org.onlab.util.ItemNotFoundException;
import org.onlab.packet.IpAddress;
import org.onlab.util.KryoNamespace;
import org.onosproject.cluster.ClusterService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.HostId;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.behaviour.BridgeConfig;
import org.onosproject.net.behaviour.BridgeName;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.behaviour.ControllerInfo;
import org.onosproject.net.behaviour.DefaultTunnelDescription;
import org.onosproject.net.behaviour.TunnelConfig;
......@@ -50,12 +51,14 @@ import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.driver.DriverHandler;
import org.onosproject.net.driver.DriverService;
import org.onosproject.net.flowobjective.FlowObjectiveService;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.group.GroupService;
import org.onosproject.net.host.HostEvent;
import org.onosproject.net.host.HostListener;
import org.onosproject.net.host.HostService;
import org.onosproject.openstackswitching.OpenstackNetwork;
import org.onosproject.openstackswitching.OpenstackPort;
import org.onosproject.openstackswitching.OpenstackSubnet;
import org.onosproject.openstackswitching.OpenstackSwitchingService;
import org.onosproject.ovsdb.controller.OvsdbClientService;
import org.onosproject.ovsdb.controller.OvsdbController;
......@@ -71,6 +74,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
......@@ -99,6 +103,7 @@ public class CordVtn implements CordVtnService {
.register(NodeState.class);
private static final String DEFAULT_BRIDGE = "br-int";
private static final String VPORT_PREFIX = "tap";
private static final String GWPORT_PREFIX = "qr-";
private static final String DEFAULT_TUNNEL = "vxlan";
private static final Map<String, String> DEFAULT_TUNNEL_OPTIONS = new HashMap<String, String>() {
{
......@@ -128,7 +133,7 @@ public class CordVtn implements CordVtnService {
protected DeviceAdminService adminService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected FlowObjectiveService flowObjectiveService;
protected FlowRuleService flowRuleService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected OvsdbController controller;
......@@ -137,6 +142,12 @@ public class CordVtn implements CordVtnService {
protected ClusterService clusterService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected MastershipService mastershipService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected GroupService groupService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected OpenstackSwitchingService openstackService;
private final ExecutorService eventExecutor = Executors
......@@ -149,8 +160,9 @@ public class CordVtn implements CordVtnService {
private final BridgeHandler bridgeHandler = new BridgeHandler();
private final VmHandler vmHandler = new VmHandler();
private ApplicationId appId;
private ConsistentMap<CordVtnNode, NodeState> nodeStore;
private Map<HostId, String> hostNetworkMap = Maps.newHashMap();
private Map<HostId, OpenstackNetwork> hostNetMap = Maps.newHashMap();
private CordVtnRuleInstaller ruleInstaller;
private enum NodeState {
......@@ -198,15 +210,20 @@ public class CordVtn implements CordVtnService {
@Activate
protected void activate() {
ApplicationId appId = coreService.registerApplication("org.onosproject.cordvtn");
appId = coreService.registerApplication("org.onosproject.cordvtn");
nodeStore = storageService.<CordVtnNode, NodeState>consistentMapBuilder()
.withSerializer(Serializer.using(NODE_SERIALIZER.build()))
.withName("cordvtn-nodestore")
.withApplicationId(appId)
.build();
ruleInstaller = new CordVtnRuleInstaller(appId, flowObjectiveService,
driverService, DEFAULT_TUNNEL);
ruleInstaller = new CordVtnRuleInstaller(appId, flowRuleService,
deviceService,
driverService,
groupService,
mastershipService,
DEFAULT_TUNNEL);
deviceService.addListener(deviceListener);
hostService.addListener(hostListener);
......@@ -277,19 +294,29 @@ public class CordVtn implements CordVtnService {
}
@Override
public void createServiceDependency(CordServiceId tenantCordServiceId,
CordServiceId providerCordServiceId) {
CordService tenantService = getCordService(tenantCordServiceId);
CordService providerService = getCordService(providerCordServiceId);
public void createServiceDependency(CordServiceId tServiceId, CordServiceId pServiceId) {
CordService tService = getCordService(tServiceId);
CordService pService = getCordService(pServiceId);
if (tService == null || pService == null) {
log.error("Failed to create CordService for {}", tServiceId.id());
return;
}
// TODO populate flow rules to create service dependency
ruleInstaller.populateServiceDependencyRules(tService, pService);
}
@Override
public void removeServiceDependency(CordServiceId tenantCordServiceId) {
CordService tenantService = getCordService(tenantCordServiceId);
public void removeServiceDependency(CordServiceId tServiceId, CordServiceId pServiceId) {
CordService tService = getCordService(tServiceId);
CordService pService = getCordService(pServiceId);
if (tService == null || pService == null) {
log.error("Failed to create CordService for {}", tServiceId.id());
return;
}
//TODO uninstall flow rules to remove service dependency
ruleInstaller.removeServiceDependencyRules(tService, pService);
}
/**
......@@ -352,21 +379,13 @@ public class CordVtn implements CordVtnService {
* @param node cordvtn node
*/
private void postInit(CordVtnNode node) {
log.info("Initializing {}", node.hostname());
disconnect(node);
Set<OpenstackNetwork> vNets = Sets.newHashSet();
ruleInstaller.init(node.intBrId(), getTunnelPort(node.intBrId()));
hostService.getConnectedHosts(node.intBrId())
.stream()
.forEach(host -> {
OpenstackNetwork vNet = getOpenstackNetworkByHost(host);
if (vNet != null) {
log.info("VM {} is detected", host.id());
hostNetworkMap.put(host.id(), vNet.id());
vNets.add(vNet);
}
});
vNets.stream().forEach(this::installFlowRules);
.forEach(vmHandler::connected);
}
/**
......@@ -558,14 +577,14 @@ public class CordVtn implements CordVtnService {
* Returns tunnel port of the device.
*
* @param bridgeId device id
* @return port, null if no tunnel port exists on a given device
* @return port number, null if no tunnel port exists on a given device
*/
private Port getTunnelPort(DeviceId bridgeId) {
private PortNumber getTunnelPort(DeviceId bridgeId) {
try {
return deviceService.getPorts(bridgeId).stream()
.filter(p -> p.annotations().value("portName").contains(DEFAULT_TUNNEL)
&& p.isEnabled())
.findFirst().get();
.findFirst().get().number();
} catch (NoSuchElementException e) {
return null;
}
......@@ -577,67 +596,17 @@ public class CordVtn implements CordVtnService {
* @param bridgeId device id
* @return ip address, null if no such device exists
*/
private IpAddress getRemoteIp(DeviceId bridgeId) {
private Ip4Address getRemoteIp(DeviceId bridgeId) {
CordVtnNode node = getNodeByBridgeId(bridgeId);
if (node != null) {
// TODO get data plane IP for tunneling
return node.ovsdbIp();
return node.ovsdbIp().getIp4Address();
} else {
return null;
}
}
/**
* Returns destination information of all ports associated with a given
* OpenStack network. Output of the destination information is set to local
* port or tunnel port according to a given device id.
*
* @param deviceId device id to install flow rules
* @param vNet OpenStack network
* @return list of flow information, empty list if no flow information exists
*/
private List<DestinationInfo> getSameNetworkPortsInfo(DeviceId deviceId, OpenstackNetwork vNet) {
List<DestinationInfo> dstInfos = Lists.newArrayList();
long tunnelId = Long.valueOf(vNet.segmentId());
for (OpenstackPort vPort : openstackService.ports(vNet.id())) {
ConnectPoint cp = getConnectPoint(vPort);
if (cp == null) {
log.debug("Couldn't find connection point for OpenStack port {}", vPort.id());
continue;
}
DestinationInfo.Builder dBuilder = cp.deviceId().equals(deviceId) ?
DestinationInfo.builder(deviceService.getPort(cp.deviceId(), cp.port())) :
DestinationInfo.builder(getTunnelPort(deviceId))
.setRemoteIp(getRemoteIp(cp.deviceId()));
dBuilder.setMac(vPort.macAddress())
.setTunnelId(tunnelId);
dstInfos.add(dBuilder.build());
}
return dstInfos;
}
/**
* Returns local ports associated with a given OpenStack network.
*
* @param bridgeId device id
* @param vNet OpenStack network
* @return port list, empty list if no port exists
*/
private List<Port> getLocalSameNetworkPorts(DeviceId bridgeId, OpenstackNetwork vNet) {
List<Port> ports = new ArrayList<>();
openstackService.ports(vNet.id()).stream().forEach(port -> {
ConnectPoint cp = getConnectPoint(port);
if (cp != null && cp.deviceId().equals(bridgeId)) {
ports.add(deviceService.getPort(cp.deviceId(), cp.port()));
}
});
return ports;
}
/**
* Returns OpenStack port associated with a given host.
*
* @param host host
......@@ -646,6 +615,10 @@ public class CordVtn implements CordVtnService {
private OpenstackPort getOpenstackPortByHost(Host host) {
Port port = deviceService.getPort(host.location().deviceId(),
host.location().port());
if (port == null) {
log.debug("Failed to get port for {}", host.id());
return null;
}
return openstackService.port(port);
}
......@@ -665,6 +638,44 @@ public class CordVtn implements CordVtnService {
}
/**
* Returns hosts associated with a given OpenStack network.
*
* @param vNet openstack network
* @return set of hosts
*/
private Set<Host> getHostsWithOpenstackNetwork(OpenstackNetwork vNet) {
checkNotNull(vNet);
return openstackService.ports(vNet.id()).stream()
.filter(port -> port.deviceOwner().contains("compute"))
.map(port -> hostService.getHostsByMac(port.macAddress())
.stream()
.findFirst()
.orElse(null))
.collect(Collectors.toSet());
}
/**
* Returns host IP assigned by OpenStack.
*
* @param host host
* @return IPv4 prefix, or null if it fails to get IP from OpenStack
*/
private IpAddress getHostIpFromOpenstack(Host host) {
OpenstackPort vPort = getOpenstackPortByHost(host);
if (vPort == null || vPort.fixedIps().isEmpty()) {
log.error("Failed to get VM IP for {}", host.id());
return null;
}
// Assumes there's only one fixed IP is assigned to a port
return (Ip4Address) vPort.fixedIps().values()
.stream()
.findFirst()
.orElse(null);
}
/**
* Returns port name with OpenStack port information.
*
* @param vPort OpenStack port
......@@ -676,27 +687,20 @@ public class CordVtn implements CordVtnService {
}
/**
* Returns connect point of a given OpenStack port.
* It assumes there's only one physical port associated with an OpenStack port.
* Returns if the host is gateway interface.
* This codes should be removed after adding proxy arp for the gateway.
*
* @param vPort openstack port
* @return connect point, null if no such port exists
* @param host host
* @return true if the host is gateway
*/
private ConnectPoint getConnectPoint(OpenstackPort vPort) {
try {
Host host = hostService.getHostsByMac(vPort.macAddress())
.stream()
.findFirst()
.get();
return new ConnectPoint(host.location().deviceId(), host.location().port());
} catch (NoSuchElementException e) {
log.debug("Not a valid host with {}", vPort.macAddress());
return null;
}
private boolean isGateway(Host host) {
Port port = deviceService.getPort(host.location().deviceId(),
host.location().port());
return port.annotations().value("portName").contains(GWPORT_PREFIX);
}
/**
* Returns OpenStack network associated with a given CORD service.
* Returns CordService by service ID.
*
* @param serviceId service id
* @return cord service, or null if it fails to get network from OpenStack
......@@ -708,73 +712,52 @@ public class CordVtn implements CordVtnService {
return null;
}
// TODO create CordService with network/subnet information from Neutron
OpenstackSubnet subnet = vNet.subnets().stream()
.findFirst()
.orElse(null);
if (subnet == null) {
log.warn("Couldn't find OpenStack subnet for service {}", serviceId.id());
return null;
}
/**
* Installs flow rules for a given OpenStack network.
*
* @param vNet OpenStack network
*/
private void installFlowRules(OpenstackNetwork vNet) {
checkNotNull(vNet, "Tenant network should not be null");
for (Device device : deviceService.getAvailableDevices(SWITCH)) {
List<DestinationInfo> dstInfos = getSameNetworkPortsInfo(device.id(), vNet);
Set<CordServiceId> tServices = Sets.newHashSet();
// TODO get tenant services from XOS
for (Port inPort : getLocalSameNetworkPorts(device.id(), vNet)) {
List<DestinationInfo> localInInfos = dstInfos.stream()
.filter(info -> !info.output().equals(inPort))
.collect(Collectors.toList());
ruleInstaller.installFlowRulesLocalIn(device.id(), inPort, localInInfos);
}
Map<Host, IpAddress> hosts = getHostsWithOpenstackNetwork(vNet)
.stream()
.collect(Collectors.toMap(host -> host,
host -> getRemoteIp(host.location().deviceId())));
Port tunPort = getTunnelPort(device.id());
List<DestinationInfo> tunnelInInfos = dstInfos.stream()
.filter(info -> !info.output().equals(tunPort))
.collect(Collectors.toList());
ruleInstaller.installFlowRulesTunnelIn(device.id(), tunPort, tunnelInInfos);
}
return new CordService(vNet, subnet, hosts, tServices);
}
/**
* Uninstalls flow rules associated with a given host for a given OpenStack network.
* Returns CordService by OpenStack network.
*
* @param vNet OpenStack network
* @param host removed host
* @return cord service
*/
private void uninstallFlowRules(OpenstackNetwork vNet, Host host) {
checkNotNull(vNet, "Tenant network should not be null");
Port removedPort = deviceService.getPort(host.location().deviceId(),
host.location().port());
private CordService getCordService(OpenstackNetwork vNet) {
checkNotNull(vNet);
for (Device device : deviceService.getAvailableDevices(SWITCH)) {
List<DestinationInfo> dstInfos = getSameNetworkPortsInfo(device.id(), vNet);
for (Port inPort : getLocalSameNetworkPorts(device.id(), vNet)) {
List<DestinationInfo> localInInfos = Lists.newArrayList(
DestinationInfo.builder(getTunnelPort(device.id()))
.setTunnelId(Long.valueOf(vNet.segmentId()))
.setMac(host.mac())
.setRemoteIp(getRemoteIp(host.location().deviceId()))
.build());
ruleInstaller.uninstallFlowRules(device.id(), inPort, localInInfos);
CordServiceId serviceId = CordServiceId.of(vNet.id());
OpenstackSubnet subnet = vNet.subnets().stream()
.findFirst()
.orElse(null);
if (subnet == null) {
log.warn("Couldn't find OpenStack subnet for service {}", serviceId);
return null;
}
if (device.id().equals(host.location().deviceId())) {
Port tunPort = getTunnelPort(device.id());
List<DestinationInfo> tunnelInInfo = Lists.newArrayList(
DestinationInfo.builder(removedPort)
.setTunnelId(Long.valueOf(vNet.segmentId()))
.setMac(host.mac())
.build());
Set<CordServiceId> tServices = Sets.newHashSet();
// TODO get tenant services from XOS
ruleInstaller.uninstallFlowRules(device.id(), tunPort, tunnelInInfo);
ruleInstaller.uninstallFlowRules(device.id(), removedPort, dstInfos);
}
}
Map<Host, IpAddress> hosts = getHostsWithOpenstackNetwork(vNet)
.stream()
.collect(Collectors.toMap(host -> host,
host -> getRemoteIp(host.location().deviceId())));
return new CordService(vNet, subnet, hosts, tServices);
}
private class InternalDeviceListener implements DeviceListener {
......@@ -873,6 +856,7 @@ public class CordVtn implements CordVtnService {
* @param port port
*/
public void portAdded(Port port) {
// TODO add host by updating network config
if (!port.annotations().value("portName").contains(DEFAULT_TUNNEL)) {
return;
}
......@@ -891,6 +875,7 @@ public class CordVtn implements CordVtnService {
* @param port port
*/
public void portRemoved(Port port) {
// TODO remove host by updating network config
if (!port.annotations().value("portName").contains(DEFAULT_TUNNEL)) {
return;
}
......@@ -907,8 +892,13 @@ public class CordVtn implements CordVtnService {
@Override
public void connected(Host host) {
// TODO remove check gateway here after applying network config host provider
if (isGateway(host)) {
return;
}
CordVtnNode node = getNodeByBridgeId(host.location().deviceId());
if (node == null || !getNodeState(node).equals(NodeState.COMPLETE)) {
if (node == null || !Objects.equals(getNodeState(node), NodeState.COMPLETE)) {
// do nothing for the host on unregistered or unprepared device
return;
}
......@@ -918,29 +908,43 @@ public class CordVtn implements CordVtnService {
return;
}
// TODO host ip should be set in host information after applying network config host provider
IpAddress hostIp = getHostIpFromOpenstack(host);
if (hostIp == null) {
log.error("Failed to get host IP of {}", host.id());
return;
}
log.info("VM {} is detected", host.id());
hostNetMap.put(host.id(), vNet);
ruleInstaller.populateBasicConnectionRules(
host,
hostIp,
checkNotNull(getRemoteIp(host.location().deviceId())).getIp4Address(),
vNet);
hostNetworkMap.put(host.id(), vNet.id());
installFlowRules(vNet);
// TODO add new VM to related service group if exists
}
@Override
public void disconnected(Host host) {
CordVtnNode node = getNodeByBridgeId(host.location().deviceId());
if (node == null || !getNodeState(node).equals(NodeState.COMPLETE)) {
if (node == null || !Objects.equals(getNodeState(node), NodeState.COMPLETE)) {
// do nothing for the host on unregistered or unprepared device
return;
}
OpenstackNetwork vNet = openstackService.network(hostNetworkMap.get(host.id()));
OpenstackNetwork vNet = hostNetMap.get(host.id());
if (vNet == null) {
return;
}
log.info("VM {} is vanished", host.id());
ruleInstaller.removeBasicConnectionRules(host);
uninstallFlowRules(vNet, host);
hostNetworkMap.remove(host.id());
// TODO remove the VM from related service group if exists
hostNetMap.remove(host.id());
}
}
}
......
......@@ -15,195 +15,859 @@
*/
package org.onosproject.cordvtn;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.onlab.packet.Ethernet;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.Ip4Prefix;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.util.ItemNotFoundException;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.DefaultGroupId;
import org.onosproject.core.GroupId;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Port;
import org.onosproject.net.Host;
import org.onosproject.net.PortNumber;
import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.driver.DefaultDriverData;
import org.onosproject.net.driver.DefaultDriverHandler;
import org.onosproject.net.driver.Driver;
import org.onosproject.net.driver.DriverHandler;
import org.onosproject.net.driver.DriverService;
import org.onosproject.net.flow.DefaultFlowRule;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.FlowRuleOperations;
import org.onosproject.net.flow.FlowRuleOperationsContext;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criterion;
import org.onosproject.net.flow.criteria.EthCriterion;
import org.onosproject.net.flow.criteria.IPCriterion;
import org.onosproject.net.flow.criteria.PortCriterion;
import org.onosproject.net.flow.instructions.ExtensionPropertyException;
import org.onosproject.net.flow.instructions.ExtensionTreatment;
import org.onosproject.net.flowobjective.DefaultForwardingObjective;
import org.onosproject.net.flowobjective.FlowObjectiveService;
import org.onosproject.net.flowobjective.ForwardingObjective;
import org.onosproject.net.flow.instructions.Instruction;
import org.onosproject.net.flow.instructions.Instructions;
import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
import org.onosproject.net.group.DefaultGroupBucket;
import org.onosproject.net.group.DefaultGroupDescription;
import org.onosproject.net.group.DefaultGroupKey;
import org.onosproject.net.group.Group;
import org.onosproject.net.group.GroupBucket;
import org.onosproject.net.group.GroupBuckets;
import org.onosproject.net.group.GroupDescription;
import org.onosproject.net.group.GroupKey;
import org.onosproject.net.group.GroupService;
import org.onosproject.openstackswitching.OpenstackNetwork;
import org.onosproject.openstackswitching.OpenstackSubnet;
import org.slf4j.Logger;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onosproject.net.Device.Type.SWITCH;
import static org.onosproject.net.flow.criteria.Criterion.Type.IN_PORT;
import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_DST;
import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_SRC;
import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.ETH_DST;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Populates rules for virtual tenant network.
* Populates rules for CORD VTN service.
*/
public final class CordVtnRuleInstaller {
public class CordVtnRuleInstaller {
protected final Logger log = getLogger(getClass());
private static final int TABLE_IN_PORT = 0;
private static final int TABLE_ACCESS_TYPE = 1;
private static final int TABLE_IN_SERVICE = 2;
private static final int TABLE_DST_IP = 3;
private static final int TABLE_TUNNEL_IN = 4;
private static final int DEFAULT_PRIORITY = 5000;
private static final int LOWER_PRIORITY = 4000;
private static final int LOWEST_PRIORITY = 0;
private final ApplicationId appId;
private final FlowObjectiveService flowObjectiveService;
private final FlowRuleService flowRuleService;
private final DeviceService deviceService;
private final DriverService driverService;
private final GroupService groupService;
private final MastershipService mastershipService;
private final String tunnelType;
/**
* Creates a new rule installer.
* Creates a new rule populator.
*
* @param appId application id
* @param flowObjectiveService flow objective service
* @param flowRuleService flow rule service
* @param deviceService device service
* @param driverService driver service
* @param tunnelType tunnel type
*/
public CordVtnRuleInstaller(ApplicationId appId,
FlowObjectiveService flowObjectiveService,
FlowRuleService flowRuleService,
DeviceService deviceService,
DriverService driverService,
GroupService groupService,
MastershipService mastershipService,
String tunnelType) {
this.appId = appId;
this.flowObjectiveService = flowObjectiveService;
this.flowRuleService = flowRuleService;
this.deviceService = deviceService;
this.driverService = driverService;
this.groupService = groupService;
this.mastershipService = mastershipService;
this.tunnelType = checkNotNull(tunnelType);
}
/**
* Installs flow rules for tunnel in traffic.
* Installs table miss rule to a give device.
*
* @param deviceId device id to install flow rules
* @param inPort in port
* @param dstInfos list of destination info
* @param deviceId device id to install the rules
* @param tunnelPort tunnel port number of the device
*/
public void installFlowRulesTunnelIn(DeviceId deviceId, Port inPort, List<DestinationInfo> dstInfos) {
dstInfos.stream().forEach(dstInfo -> {
ForwardingObjective.Builder fBuilder = vtnRulesSameNode(inPort, dstInfo);
if (fBuilder != null) {
flowObjectiveService.forward(deviceId, fBuilder.add());
public void init(DeviceId deviceId, PortNumber tunnelPort) {
// default is drop packets which can be accomplished without
// a table miss entry for all table.
populateTunnelInPortRule(deviceId, tunnelPort);
processAccessTypeTable(deviceId);
}
});
/**
* Populates basic rules that connect a VM to the other VMs in the system.
*
* @param host host
* @param hostIp host ip
* @param tunnelIp tunnel ip
* @param vNet openstack network
*/
public void populateBasicConnectionRules(Host host, IpAddress hostIp, IpAddress tunnelIp,
OpenstackNetwork vNet) {
// TODO we can get host ip from host.ip() after applying NetworkConfig host provider
checkNotNull(host);
checkNotNull(vNet);
DeviceId deviceId = host.location().deviceId();
if (!mastershipService.isLocalMaster(deviceId)) {
return;
}
PortNumber inPort = host.location().port();
MacAddress dstMac = host.mac();
long tunnelId = Long.parseLong(vNet.segmentId());
OpenstackSubnet subnet = vNet.subnets().stream()
.findFirst()
.orElse(null);
if (subnet == null) {
log.error("Failed to get subnet for {}", host.id());
return;
}
populateLocalInPortRule(deviceId, inPort, hostIp);
populateDirectAccessRule(Ip4Prefix.valueOf(subnet.cidr()), Ip4Prefix.valueOf(subnet.cidr()));
populateDstIpRule(deviceId, inPort, dstMac, hostIp, tunnelId, tunnelIp);
populateTunnelInRule(deviceId, inPort, dstMac, tunnelId);
}
/**
* Installs flow rules for local in traffic.
* Populates service dependency rules.
*
* @param deviceId device id to install flow rules
* @param inPort in port
* @param dstInfos list of destination info
* @param tService tenant cord service
* @param pService provider cord service
*/
public void populateServiceDependencyRules(CordService tService, CordService pService) {
checkNotNull(tService);
checkNotNull(pService);
Ip4Prefix srcRange = tService.serviceIpRange().getIp4Prefix();
Ip4Prefix dstRange = pService.serviceIpRange().getIp4Prefix();
Ip4Address serviceIp = pService.serviceIp().getIp4Address();
Map<DeviceId, GroupId> outGroups = Maps.newHashMap();
Map<DeviceId, Set<PortNumber>> inPorts = Maps.newHashMap();
for (Device device : deviceService.getAvailableDevices(SWITCH)) {
GroupId groupId = createServiceGroup(device.id(), pService);
outGroups.put(device.id(), groupId);
Set<PortNumber> vms = tService.hosts().keySet()
.stream()
.filter(host -> host.location().deviceId().equals(device.id()))
.map(host -> host.location().port())
.collect(Collectors.toSet());
inPorts.put(device.id(), vms);
}
populateIndirectAccessRule(srcRange, serviceIp, outGroups);
populateDirectAccessRule(srcRange, dstRange);
populateInServiceRule(inPorts, outGroups);
}
/**
* Removes basic rules related to a given flow information.
*
* @param host host to be removed
*/
public void installFlowRulesLocalIn(DeviceId deviceId, Port inPort, List<DestinationInfo> dstInfos) {
dstInfos.stream().forEach(dstInfo -> {
ForwardingObjective.Builder fBuilder = isTunnelPort(dstInfo.output()) ?
vtnRulesRemoteNode(deviceId, inPort, dstInfo) : vtnRulesSameNode(inPort, dstInfo);
public void removeBasicConnectionRules(Host host) {
checkNotNull(host);
DeviceId deviceId = host.location().deviceId();
MacAddress mac = host.mac();
PortNumber port = host.location().port();
IpAddress ip = host.ipAddresses().stream().findFirst().orElse(null);
if (fBuilder != null) {
flowObjectiveService.forward(deviceId, fBuilder.add());
if (!mastershipService.isLocalMaster(deviceId)) {
return;
}
for (FlowRule flowRule : flowRuleService.getFlowRulesById(appId)) {
if (flowRule.deviceId().equals(deviceId)) {
PortNumber inPort = getInPort(flowRule);
if (inPort != null && inPort.equals(port)) {
processFlowRule(false, flowRule);
continue;
}
}
MacAddress dstMac = getDstMacFromTreatment(flowRule);
if (dstMac != null && dstMac.equals(mac)) {
processFlowRule(false, flowRule);
continue;
}
dstMac = getDstMacFromSelector(flowRule);
if (dstMac != null && dstMac.equals(mac)) {
processFlowRule(false, flowRule);
continue;
}
IpPrefix dstIp = getDstIpFromSelector(flowRule);
if (dstIp != null && dstIp.equals(ip.toIpPrefix())) {
processFlowRule(false, flowRule);
}
}
// TODO uninstall same network access rule in access table if no vm exists in the network
}
/**
* Removes service dependency rules.
*
* @param tService tenant cord service
* @param pService provider cord service
*/
public void removeServiceDependencyRules(CordService tService, CordService pService) {
checkNotNull(tService);
checkNotNull(pService);
Ip4Prefix srcRange = tService.serviceIpRange().getIp4Prefix();
Ip4Prefix dstRange = pService.serviceIpRange().getIp4Prefix();
IpPrefix serviceIp = pService.serviceIp().toIpPrefix();
Map<DeviceId, GroupId> outGroups = Maps.newHashMap();
GroupKey groupKey = new DefaultGroupKey(pService.id().id().getBytes());
deviceService.getAvailableDevices(SWITCH).forEach(device -> {
Group group = groupService.getGroup(device.id(), groupKey);
if (group != null) {
outGroups.put(device.id(), group.id());
}
});
for (FlowRule flowRule : flowRuleService.getFlowRulesById(appId)) {
IpPrefix dstIp = getDstIpFromSelector(flowRule);
IpPrefix srcIp = getSrcIpFromSelector(flowRule);
if (dstIp != null && dstIp.equals(serviceIp)) {
processFlowRule(false, flowRule);
continue;
}
if (dstIp != null && srcIp != null) {
if (dstIp.equals(dstRange) && srcIp.equals(srcRange)) {
processFlowRule(false, flowRule);
continue;
}
if (dstIp.equals(srcRange) && srcIp.equals(dstRange)) {
processFlowRule(false, flowRule);
continue;
}
}
GroupId groupId = getGroupIdFromTreatment(flowRule);
if (groupId != null && groupId.equals(outGroups.get(flowRule.deviceId()))) {
processFlowRule(false, flowRule);
}
}
// TODO remove the group if it is not in use
}
/**
* Creates a new group for a given service.
*
* @param deviceId device id to create a group
* @param service cord service
* @return group id, or null if it fails to create
*/
private GroupId createServiceGroup(DeviceId deviceId, CordService service) {
checkNotNull(service);
GroupKey groupKey = getGroupKey(service.id());
Group group = groupService.getGroup(deviceId, groupKey);
GroupId groupId = getGroupId(service.id(), deviceId);
if (group != null) {
log.debug("Group {} is already exist in {}", service.id(), deviceId);
return groupId;
}
GroupBuckets buckets = getServiceGroupBuckets(deviceId, service.segmentationId(), service.hosts());
GroupDescription groupDescription = new DefaultGroupDescription(
deviceId,
GroupDescription.Type.SELECT,
buckets,
groupKey,
groupId.id(),
appId);
groupService.addGroup(groupDescription);
return groupId;
}
/**
* Uninstalls flow rules associated with a given port from a given device.
* Returns group buckets for a given device.
*
* @param deviceId device id
* @param inPort port associated with removed host
* @param dstInfos list of destination info
* @param tunnelId tunnel id
* @param hosts list of host
* @return group buckets
*/
public void uninstallFlowRules(DeviceId deviceId, Port inPort, List<DestinationInfo> dstInfos) {
dstInfos.stream().forEach(dstInfo -> {
ForwardingObjective.Builder fBuilder = isTunnelPort(dstInfo.output()) ?
vtnRulesRemoteNode(deviceId, inPort, dstInfo) : vtnRulesSameNode(inPort, dstInfo);
private GroupBuckets getServiceGroupBuckets(DeviceId deviceId, long tunnelId, Map<Host, IpAddress> hosts) {
List<GroupBucket> buckets = Lists.newArrayList();
for (Map.Entry<Host, IpAddress> entry : hosts.entrySet()) {
Host host = entry.getKey();
Ip4Address remoteIp = entry.getValue().getIp4Address();
DeviceId hostDevice = host.location().deviceId();
if (fBuilder != null) {
flowObjectiveService.forward(deviceId, fBuilder.remove());
TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
.builder()
.setEthDst(host.mac());
if (deviceId.equals(hostDevice)) {
tBuilder.setOutput(host.location().port());
} else {
ExtensionTreatment tunnelDst = getTunnelDst(deviceId, remoteIp);
if (tunnelDst == null) {
continue;
}
});
tBuilder.extension(tunnelDst, deviceId)
.setTunnelId(tunnelId)
.setOutput(getTunnelPort(hostDevice));
}
buckets.add(DefaultGroupBucket.createSelectGroupBucket(tBuilder.build()));
}
return new GroupBuckets(buckets);
}
/**
* Returns globally unique group ID.
*
* @param serviceId service id
* @param deviceId device id
* @return group id
*/
private GroupId getGroupId(CordServiceId serviceId, DeviceId deviceId) {
return new DefaultGroupId(Objects.hash(serviceId, deviceId));
}
/**
* Returns group key of a service.
*
* @param serviceId service id
* @return group key
*/
private GroupKey getGroupKey(CordServiceId serviceId) {
return new DefaultGroupKey(serviceId.id().getBytes());
}
/**
* Forward table miss rules in ACCESS_TYPE table to IN_SERVICE table.
*
* @param deviceId device id
*/
private void processAccessTypeTable(DeviceId deviceId) {
TrafficSelector selector = DefaultTrafficSelector.builder()
.build();
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.transition(TABLE_IN_SERVICE)
.build();
FlowRule flowRule = DefaultFlowRule.builder()
.fromApp(appId)
.withSelector(selector)
.withTreatment(treatment)
.withPriority(LOWEST_PRIORITY)
.forDevice(deviceId)
.forTable(TABLE_ACCESS_TYPE)
.makePermanent()
.build();
processFlowRule(true, flowRule);
}
/**
* Populates rules for tunnel flows in port in IN_PORT table.
* All flows from tunnel port are forwarded to TUNNEL_ID table.
*
* @param deviceId device id to install the rules
* @param tunnelPort tunnel port
*/
private void populateTunnelInPortRule(DeviceId deviceId, PortNumber tunnelPort) {
checkNotNull(tunnelPort);
TrafficSelector selector = DefaultTrafficSelector.builder()
.matchInPort(tunnelPort)
.build();
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.transition(TABLE_TUNNEL_IN)
.build();
FlowRule flowRule = DefaultFlowRule.builder()
.fromApp(appId)
.withSelector(selector)
.withTreatment(treatment)
.withPriority(DEFAULT_PRIORITY)
.forDevice(deviceId)
.forTable(TABLE_IN_PORT)
.makePermanent()
.build();
processFlowRule(true, flowRule);
}
/**
* Returns forwarding objective builder to provision basic virtual tenant network.
* This method cares for the traffics whose source and destination device is the same.
* Populates rules for local in port in IN_PORT table.
* Flows from a given in port, whose source IP is service IP transition
* to DST_TYPE table. Other flows transition to IN_SERVICE table.
*
* @param deviceId device id to install the rules
* @param inPort in port
* @param dstInfo destination information
* @return forwarding objective builder
* @param srcIp source ip
*/
private void populateLocalInPortRule(DeviceId deviceId, PortNumber inPort, IpAddress srcIp) {
TrafficSelector selector = DefaultTrafficSelector.builder()
.matchInPort(inPort)
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPSrc(srcIp.toIpPrefix())
.build();
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.transition(TABLE_ACCESS_TYPE)
.build();
FlowRule flowRule = DefaultFlowRule.builder()
.fromApp(appId)
.withSelector(selector)
.withTreatment(treatment)
.withPriority(DEFAULT_PRIORITY)
.forDevice(deviceId)
.forTable(TABLE_IN_PORT)
.makePermanent()
.build();
processFlowRule(true, flowRule);
selector = DefaultTrafficSelector.builder()
.matchInPort(inPort)
.build();
treatment = DefaultTrafficTreatment.builder()
.transition(TABLE_IN_SERVICE)
.build();
flowRule = DefaultFlowRule.builder()
.fromApp(appId)
.withSelector(selector)
.withTreatment(treatment)
.withPriority(LOWER_PRIORITY)
.forDevice(deviceId)
.forTable(TABLE_IN_PORT)
.makePermanent()
.build();
processFlowRule(true, flowRule);
}
/**
* Populates direct VM access rules for ACCESS_TYPE table.
* These rules are installed to all devices.
*
* @param srcRange source ip range
* @param dstRange destination ip range
*/
private ForwardingObjective.Builder vtnRulesSameNode(Port inPort, DestinationInfo dstInfo) {
checkArgument(inPort.element().id().equals(dstInfo.output().element().id()));
private void populateDirectAccessRule(Ip4Prefix srcRange, Ip4Prefix dstRange) {
TrafficSelector selector = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPSrc(srcRange)
.matchIPDst(dstRange)
.build();
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.transition(TABLE_DST_IP)
.build();
TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
for (Device device : deviceService.getAvailableDevices(SWITCH)) {
FlowRule flowRuleDirect = DefaultFlowRule.builder()
.fromApp(appId)
.withSelector(selector)
.withTreatment(treatment)
.withPriority(LOWER_PRIORITY)
.forDevice(device.id())
.forTable(TABLE_ACCESS_TYPE)
.makePermanent()
.build();
sBuilder.matchInPort(inPort.number())
.matchEthDst(dstInfo.mac());
if (isTunnelPort(inPort)) {
sBuilder.matchTunnelId(dstInfo.tunnelId());
processFlowRule(true, flowRuleDirect);
}
}
tBuilder.setOutput(dstInfo.output().number());
/**
* Populates indirect service access rules for ACCESS_TYPE table.
* These rules are installed to all devices.
*
* @param srcRange source range
* @param serviceIp service ip
* @param outGroups list of output group
*/
private void populateIndirectAccessRule(Ip4Prefix srcRange, Ip4Address serviceIp,
Map<DeviceId, GroupId> outGroups) {
TrafficSelector selector = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPSrc(srcRange)
.matchIPDst(serviceIp.toIpPrefix())
.build();
return DefaultForwardingObjective.builder()
.withSelector(sBuilder.build())
.withTreatment(tBuilder.build())
for (Map.Entry<DeviceId, GroupId> outGroup : outGroups.entrySet()) {
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.group(outGroup.getValue())
.build();
FlowRule flowRule = DefaultFlowRule.builder()
.fromApp(appId)
.withSelector(selector)
.withTreatment(treatment)
.withPriority(DEFAULT_PRIORITY)
.withFlag(ForwardingObjective.Flag.VERSATILE)
.forDevice(outGroup.getKey())
.forTable(TABLE_ACCESS_TYPE)
.makePermanent()
.build();
processFlowRule(true, flowRule);
}
}
/**
* Populates flow rules for IN_SERVICE table.
*
* @param inPorts list of inports related to the service for each device
* @param outGroups set of output groups
*/
private void populateInServiceRule(Map<DeviceId, Set<PortNumber>> inPorts, Map<DeviceId, GroupId> outGroups) {
checkNotNull(inPorts);
checkNotNull(outGroups);
for (Map.Entry<DeviceId, Set<PortNumber>> entry : inPorts.entrySet()) {
Set<PortNumber> ports = entry.getValue();
DeviceId deviceId = entry.getKey();
GroupId groupId = outGroups.get(deviceId);
if (groupId == null) {
continue;
}
ports.stream().forEach(port -> {
TrafficSelector selector = DefaultTrafficSelector.builder()
.matchInPort(port)
.build();
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.group(groupId)
.build();
FlowRule flowRule = DefaultFlowRule.builder()
.fromApp(appId)
.makePermanent();
.withSelector(selector)
.withTreatment(treatment)
.withPriority(DEFAULT_PRIORITY)
.forDevice(deviceId)
.forTable(TABLE_IN_SERVICE)
.makePermanent()
.build();
processFlowRule(true, flowRule);
});
}
}
/**
* Returns forwarding objective builder to provision basic virtual tenant network.
* This method cares for the traffics whose source and destination is not the same.
* Populates flow rules for DST_IP table.
*
* @param deviceId device id to install flow rules
* @param deviceId device id
* @param inPort in port
* @param dstInfo destination information
* @return forwarding objective, or null if it fails to build it
* @param dstMac mac address
* @param dstIp destination ip
* @param tunnelId tunnel id
* @param tunnelIp tunnel remote ip
*/
private ForwardingObjective.Builder vtnRulesRemoteNode(DeviceId deviceId, Port inPort, DestinationInfo dstInfo) {
checkArgument(isTunnelPort(dstInfo.output()));
private void populateDstIpRule(DeviceId deviceId, PortNumber inPort, MacAddress dstMac,
IpAddress dstIp, long tunnelId, IpAddress tunnelIp) {
TrafficSelector selector = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPDst(dstIp.toIpPrefix())
.build();
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.setEthDst(dstMac)
.setOutput(inPort)
.build();
TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
FlowRule flowRule = DefaultFlowRule.builder()
.fromApp(appId)
.withSelector(selector)
.withTreatment(treatment)
.withPriority(DEFAULT_PRIORITY)
.forDevice(deviceId)
.forTable(TABLE_DST_IP)
.makePermanent()
.build();
ExtensionTreatment extTreatment =
getTunnelDstInstruction(deviceId, dstInfo.remoteIp().getIp4Address());
if (extTreatment == null) {
return null;
processFlowRule(true, flowRule);
for (Device device : deviceService.getAvailableDevices(SWITCH)) {
if (device.id().equals(deviceId)) {
continue;
}
sBuilder.matchInPort(inPort.number())
.matchEthDst(dstInfo.mac());
ExtensionTreatment tunnelDst = getTunnelDst(device.id(), tunnelIp.getIp4Address());
if (tunnelDst == null) {
continue;
}
tBuilder.extension(extTreatment, deviceId)
.setTunnelId(dstInfo.tunnelId())
.setOutput(dstInfo.output().number());
treatment = DefaultTrafficTreatment.builder()
.setEthDst(dstMac)
.setTunnelId(tunnelId)
.extension(tunnelDst, device.id())
.setOutput(getTunnelPort(device.id()))
.build();
return DefaultForwardingObjective.builder()
.withSelector(sBuilder.build())
.withTreatment(tBuilder.build())
flowRule = DefaultFlowRule.builder()
.fromApp(appId)
.withSelector(selector)
.withTreatment(treatment)
.withPriority(DEFAULT_PRIORITY)
.withFlag(ForwardingObjective.Flag.VERSATILE)
.forDevice(device.id())
.forTable(TABLE_DST_IP)
.makePermanent()
.build();
processFlowRule(true, flowRule);
}
}
/**
* Populates flow rules for TUNNEL_ID table.
*
* @param deviceId device id
* @param inPort in port
* @param mac mac address
* @param tunnelId tunnel id
*/
private void populateTunnelInRule(DeviceId deviceId, PortNumber inPort, MacAddress mac, long tunnelId) {
TrafficSelector selector = DefaultTrafficSelector.builder()
.matchTunnelId(tunnelId)
.matchEthDst(mac)
.build();
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.setOutput(inPort)
.build();
FlowRule flowRule = DefaultFlowRule.builder()
.fromApp(appId)
.makePermanent();
.withSelector(selector)
.withTreatment(treatment)
.withPriority(DEFAULT_PRIORITY)
.forDevice(deviceId)
.forTable(TABLE_TUNNEL_IN)
.makePermanent()
.build();
processFlowRule(true, flowRule);
}
/**
* Checks if a given port is tunnel interface or not.
* It assumes the tunnel interface contains tunnelType string in its name.
* Installs or uninstall a given rule.
*
* @param port port
* @return true if the port is tunnel interface, false otherwise.
* @param install true to install, false to uninstall
* @param rule rule
*/
private boolean isTunnelPort(Port port) {
return port.annotations().value("portName").contains(tunnelType);
private void processFlowRule(boolean install, FlowRule rule) {
FlowRuleOperations.Builder oBuilder = FlowRuleOperations.builder();
oBuilder = install ? oBuilder.add(rule) : oBuilder.remove(rule);
flowRuleService.apply(oBuilder.build(new FlowRuleOperationsContext() {
@Override
public void onError(FlowRuleOperations ops) {
log.error(String.format("Failed %s, %s", ops.toString(), rule.toString()));
}
}));
}
/**
* Returns tunnel port of the device.
*
* @param deviceId device id
* @return tunnel port number, or null if no tunnel port exists on a given device
*/
private PortNumber getTunnelPort(DeviceId deviceId) {
try {
return deviceService.getPorts(deviceId).stream()
.filter(p -> p.annotations().value("portName").contains(tunnelType))
.findFirst().get().number();
} catch (NoSuchElementException e) {
return null;
}
}
/**
* Returns the inport from a given flow rule if the rule contains the match of it.
*
* @param flowRule flow rule
* @return port number, or null if the rule doesn't have inport match
*/
private PortNumber getInPort(FlowRule flowRule) {
Criterion criterion = flowRule.selector().getCriterion(IN_PORT);
if (criterion != null && criterion instanceof PortCriterion) {
PortCriterion port = (PortCriterion) criterion;
return port.port();
} else {
return null;
}
}
/**
* Returns the destination mac address from a given flow rule if the rule
* contains the instruction of it.
*
* @param flowRule flow rule
* @return mac address, or null if the rule doesn't have destination mac instruction
*/
private MacAddress getDstMacFromTreatment(FlowRule flowRule) {
Instruction instruction = flowRule.treatment().allInstructions().stream()
.filter(inst -> inst instanceof ModEtherInstruction &&
((ModEtherInstruction) inst).subtype().equals(ETH_DST))
.findFirst()
.orElse(null);
if (instruction == null) {
return null;
}
return ((ModEtherInstruction) instruction).mac();
}
/**
* Returns the destination mac address from a given flow rule if the rule
* contains the match of it.
*
* @param flowRule flow rule
* @return mac address, or null if the rule doesn't have destination mac match
*/
private MacAddress getDstMacFromSelector(FlowRule flowRule) {
Criterion criterion = flowRule.selector().getCriterion(Criterion.Type.ETH_DST);
if (criterion != null && criterion instanceof EthCriterion) {
EthCriterion eth = (EthCriterion) criterion;
return eth.mac();
} else {
return null;
}
}
/**
* Returns the destination IP from a given flow rule if the rule contains
* the match of it.
*
* @param flowRule flow rule
* @return ip prefix, or null if the rule doesn't have ip match
*/
private IpPrefix getDstIpFromSelector(FlowRule flowRule) {
Criterion criterion = flowRule.selector().getCriterion(IPV4_DST);
if (criterion != null && criterion instanceof IPCriterion) {
IPCriterion ip = (IPCriterion) criterion;
return ip.ip();
} else {
return null;
}
}
/**
* Returns the source IP from a given flow rule if the rule contains
* the match of it.
*
* @param flowRule flow rule
* @return ip prefix, or null if the rule doesn't have ip match
*/
private IpPrefix getSrcIpFromSelector(FlowRule flowRule) {
Criterion criterion = flowRule.selector().getCriterion(IPV4_SRC);
if (criterion != null && criterion instanceof IPCriterion) {
IPCriterion ip = (IPCriterion) criterion;
return ip.ip();
} else {
return null;
}
}
/**
* Returns the group ID from a given flow rule if the rule contains the
* treatment of it.
*
* @param flowRule flow rule
* @return group id, or null if the rule doesn't have group instruction
*/
private GroupId getGroupIdFromTreatment(FlowRule flowRule) {
Instruction instruction = flowRule.treatment().allInstructions().stream()
.filter(inst -> inst instanceof Instructions.GroupInstruction)
.findFirst()
.orElse(null);
if (instruction == null) {
return null;
}
return ((Instructions.GroupInstruction) instruction).groupId();
}
/**
......@@ -213,19 +877,23 @@ public final class CordVtnRuleInstaller {
* @param remoteIp tunnel destination address
* @return extension treatment or null if it fails to get instruction
*/
private ExtensionTreatment getTunnelDstInstruction(DeviceId deviceId, Ip4Address remoteIp) {
private ExtensionTreatment getTunnelDst(DeviceId deviceId, Ip4Address remoteIp) {
try {
Driver driver = driverService.getDriver(deviceId);
DriverHandler handler = new DefaultDriverHandler(new DefaultDriverData(driver, deviceId));
DefaultDriverData driverData = new DefaultDriverData(driver, deviceId);
DriverHandler handler = new DefaultDriverHandler(driverData);
ExtensionTreatmentResolver resolver = handler.behaviour(ExtensionTreatmentResolver.class);
ExtensionTreatment treatment = resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
ExtensionTreatment treatment =
resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
treatment.setPropertyValue("tunnelDst", remoteIp);
return treatment;
} catch (ItemNotFoundException | UnsupportedOperationException | ExtensionPropertyException e) {
log.error("Failed to get extension instruction to set tunnel dst {}", deviceId);
} catch (ItemNotFoundException | UnsupportedOperationException |
ExtensionPropertyException e) {
log.error("Failed to get extension instruction {}", deviceId);
return null;
}
}
}
......
......@@ -69,15 +69,16 @@ public interface CordVtnService {
/**
* Creates dependencies for a given tenant service.
*
* @param tenantCordServiceId id of the service which has a dependency
* @param providerCordServiceId id of the service which provide dependency
* @param tServiceId id of the service which has a dependency
* @param pServiceId id of the service which provide dependency
*/
void createServiceDependency(CordServiceId tenantCordServiceId, CordServiceId providerCordServiceId);
void createServiceDependency(CordServiceId tServiceId, CordServiceId pServiceId);
/**
* Removes all dependencies from a given tenant service.
*
* @param tenantCordServiceId id of the service which has a dependency
* @param tServiceId id of the service which has a dependency
* @param pServiceId id of the service which provide dependency
*/
void removeServiceDependency(CordServiceId tenantCordServiceId);
void removeServiceDependency(CordServiceId tServiceId, CordServiceId pServiceId);
}
......
/*
* Copyright 2014-2015 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.cordvtn;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onosproject.net.Port;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Contains destination information.
*/
public final class DestinationInfo {
private final Port output;
private final List<IpAddress> ip;
private final MacAddress mac;
private final IpAddress remoteIp;
private final long tunnelId;
/**
* Creates a new destination information.
*
* @param output output port
* @param ip destination ip address
* @param mac destination mac address
* @param remoteIp tunnel remote ip address
* @param tunnelId segment id
*/
public DestinationInfo(Port output, List<IpAddress> ip, MacAddress mac,
IpAddress remoteIp, long tunnelId) {
this.output = checkNotNull(output);
this.ip = ip;
this.mac = mac;
this.remoteIp = remoteIp;
this.tunnelId = tunnelId;
}
/**
* Returns output port.
*
* @return port
*/
public Port output() {
return output;
}
/**
* Returns destination ip addresses.
*
* @return list of ip address
*/
public List<IpAddress> ip() {
return ip;
}
/**
* Returns destination mac address.
*
* @return mac address
*/
public MacAddress mac() {
return mac;
}
/**
* Returns tunnel remote ip address.
*
* @return ip address
*/
public IpAddress remoteIp() {
return remoteIp;
}
/**
* Returns tunnel id.
*
* @return tunnel id
*/
public long tunnelId() {
return tunnelId;
}
/**
* Returns a new destination info builder.
*
* @return destination info builder
*/
public static DestinationInfo.Builder builder(Port output) {
return new Builder(output);
}
/**
* DestinationInfo builder class.
*/
public static final class Builder {
private final Port output;
private List<IpAddress> ip;
private MacAddress mac;
private IpAddress remoteIp;
private long tunnelId;
/**
* Creates a new destination information builder.
*
* @param output output port
*/
public Builder(Port output) {
this.output = checkNotNull(output, "Output port cannot be null");
}
/**
* Sets the destination ip address.
*
* @param ip ip address
* @return destination info builder
*/
public Builder setIp(List<IpAddress> ip) {
this.ip = checkNotNull(ip, "IP cannot be null");
return this;
}
/**
* Sets the destination mac address.
*
* @param mac mac address
* @return destination info builder
*/
public Builder setMac(MacAddress mac) {
this.mac = checkNotNull(mac, "MAC address cannot be null");
return this;
}
/**
* Sets the tunnel remote ip address.
*
* @param remoteIp ip address
* @return destination info builder
*/
public Builder setRemoteIp(IpAddress remoteIp) {
this.remoteIp = checkNotNull(remoteIp, "Remote IP address cannot be null");
return this;
}
/**
* Sets the tunnel id.
*
* @param tunnelId tunnel id
* @return destination info builder
*/
public Builder setTunnelId(long tunnelId) {
this.tunnelId = checkNotNull(tunnelId, "Tunnel ID cannot be null");
return this;
}
/**
* Build a destination information.
*
* @return destination info object
*/
public DestinationInfo build() {
return new DestinationInfo(this);
}
}
private DestinationInfo(Builder builder) {
output = builder.output;
ip = builder.ip;
mac = builder.mac;
remoteIp = builder.remoteIp;
tunnelId = builder.tunnelId;
}
}
......@@ -58,14 +58,16 @@ public class ServiceDependencyWebResource extends AbstractWebResource {
/**
* Removes service dependencies.
*
* @param serviceId service id
* @param tServiceId tenant service id
* @param pServiceId provider service id
* @return 200 OK, or 400 Bad Request
*/
@DELETE
@Path("{serviceId}")
@Path("{tenantServiceId}/{providerServiceId}")
@Produces(MediaType.APPLICATION_JSON)
public Response removeServiceDependency(@PathParam("serviceId") String serviceId) {
service.removeServiceDependency(CordServiceId.of(serviceId));
public Response removeServiceDependency(@PathParam("tenantServiceId") String tServiceId,
@PathParam("providerServiceId") String pServiceId) {
service.removeServiceDependency(CordServiceId.of(tServiceId), CordServiceId.of(pServiceId));
return Response.status(Response.Status.OK).build();
}
......