[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
diff --git a/src/main/java/org/onosproject/cordvtn/CordService.java b/src/main/java/org/onosproject/cordvtn/CordService.java
index 2e4a59e..0d09bb3 100644
--- a/src/main/java/org/onosproject/cordvtn/CordService.java
+++ b/src/main/java/org/onosproject/cordvtn/CordService.java
@@ -18,8 +18,18 @@
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 @@
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 @@
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 @@
.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;
+ }
+ }
}
diff --git a/src/main/java/org/onosproject/cordvtn/CordVtn.java b/src/main/java/org/onosproject/cordvtn/CordVtn.java
index d1ff7f0..d119a5a 100644
--- a/src/main/java/org/onosproject/cordvtn/CordVtn.java
+++ b/src/main/java/org/onosproject/cordvtn/CordVtn.java
@@ -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.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.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.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 @@
.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 @@
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 @@
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 @@
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 @@
@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 @@
}
@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);
- // TODO populate flow rules to create service dependency
+ if (tService == null || pService == null) {
+ log.error("Failed to create CordService for {}", tServiceId.id());
+ return;
+ }
+
+ 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);
- //TODO uninstall flow rules to remove service dependency
+ if (tService == null || pService == null) {
+ log.error("Failed to create CordService for {}", tServiceId.id());
+ return;
+ }
+
+ ruleInstaller.removeServiceDependencyRules(tService, pService);
}
/**
@@ -352,21 +379,13 @@
* @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 @@
* 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 @@
* @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 @@
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 @@
}
/**
+ * 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 @@
}
/**
- * 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 @@
return null;
}
- // TODO create CordService with network/subnet information from Neutron
- return null;
+ OpenstackSubnet subnet = vNet.subnets().stream()
+ .findFirst()
+ .orElse(null);
+ if (subnet == null) {
+ log.warn("Couldn't find OpenStack subnet for service {}", serviceId.id());
+ return null;
+ }
+
+ Set<CordServiceId> tServices = Sets.newHashSet();
+ // TODO get tenant services from XOS
+
+ Map<Host, IpAddress> hosts = getHostsWithOpenstackNetwork(vNet)
+ .stream()
+ .collect(Collectors.toMap(host -> host,
+ host -> getRemoteIp(host.location().deviceId())));
+
+ return new CordService(vNet, subnet, hosts, tServices);
}
/**
- * Installs flow rules for a given OpenStack network.
+ * Returns CordService by OpenStack network.
*
* @param vNet OpenStack network
+ * @return cord service
*/
- private void installFlowRules(OpenstackNetwork vNet) {
- checkNotNull(vNet, "Tenant network should not be null");
+ 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 = dstInfos.stream()
- .filter(info -> !info.output().equals(inPort))
- .collect(Collectors.toList());
- ruleInstaller.installFlowRulesLocalIn(device.id(), inPort, localInInfos);
- }
-
- 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);
+ 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;
}
- }
- /**
- * Uninstalls flow rules associated with a given host for a given OpenStack network.
- *
- * @param vNet OpenStack network
- * @param host removed host
- */
- private void uninstallFlowRules(OpenstackNetwork vNet, Host host) {
- checkNotNull(vNet, "Tenant network should not be null");
+ Set<CordServiceId> tServices = Sets.newHashSet();
+ // TODO get tenant services from XOS
- Port removedPort = deviceService.getPort(host.location().deviceId(),
- host.location().port());
+ Map<Host, IpAddress> hosts = getHostsWithOpenstackNetwork(vNet)
+ .stream()
+ .collect(Collectors.toMap(host -> host,
+ host -> getRemoteIp(host.location().deviceId())));
- 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);
- }
-
- 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());
-
- ruleInstaller.uninstallFlowRules(device.id(), tunPort, tunnelInInfo);
- ruleInstaller.uninstallFlowRules(device.id(), removedPort, dstInfos);
- }
- }
+ return new CordService(vNet, subnet, hosts, tServices);
}
private class InternalDeviceListener implements DeviceListener {
@@ -873,6 +856,7 @@
* @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 @@
* @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 @@
@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 @@
return;
}
- log.info("VM {} is detected", host.id());
+ // 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;
+ }
- hostNetworkMap.put(host.id(), vNet.id());
- installFlowRules(vNet);
+ log.info("VM {} is detected", host.id());
+ hostNetMap.put(host.id(), vNet);
+
+ ruleInstaller.populateBasicConnectionRules(
+ host,
+ hostIp,
+ checkNotNull(getRemoteIp(host.location().deviceId())).getIp4Address(),
+ 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());
}
}
}
diff --git a/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java b/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java
index 9e22997..4e044e1 100644
--- a/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java
+++ b/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java
@@ -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);
}
/**
- * Installs flow rules for local in traffic.
+ * Populates basic rules that connect a VM to the other VMs in the system.
*
- * @param deviceId device id to install flow rules
- * @param inPort in port
- * @param dstInfos list of destination info
+ * @param host host
+ * @param hostIp host ip
+ * @param tunnelIp tunnel ip
+ * @param vNet openstack network
*/
- 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 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);
- if (fBuilder != null) {
- flowObjectiveService.forward(deviceId, fBuilder.add());
- }
- });
- }
-
- /**
- * Uninstalls flow rules associated with a given port from a given device.
- *
- * @param deviceId device id
- * @param inPort port associated with removed host
- * @param dstInfos list of destination info
- */
- 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);
-
- if (fBuilder != null) {
- flowObjectiveService.forward(deviceId, fBuilder.remove());
- }
- });
- }
-
- /**
- * Returns forwarding objective builder to provision basic virtual tenant network.
- * This method cares for the traffics whose source and destination device is the same.
- *
- * @param inPort in port
- * @param dstInfo destination information
- * @return forwarding objective builder
- */
- private ForwardingObjective.Builder vtnRulesSameNode(Port inPort, DestinationInfo dstInfo) {
- checkArgument(inPort.element().id().equals(dstInfo.output().element().id()));
-
- TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
- TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-
- sBuilder.matchInPort(inPort.number())
- .matchEthDst(dstInfo.mac());
- if (isTunnelPort(inPort)) {
- sBuilder.matchTunnelId(dstInfo.tunnelId());
+ DeviceId deviceId = host.location().deviceId();
+ if (!mastershipService.isLocalMaster(deviceId)) {
+ return;
}
- tBuilder.setOutput(dstInfo.output().number());
+ PortNumber inPort = host.location().port();
+ MacAddress dstMac = host.mac();
+ long tunnelId = Long.parseLong(vNet.segmentId());
- return DefaultForwardingObjective.builder()
- .withSelector(sBuilder.build())
- .withTreatment(tBuilder.build())
- .withPriority(DEFAULT_PRIORITY)
- .withFlag(ForwardingObjective.Flag.VERSATILE)
- .fromApp(appId)
- .makePermanent();
+ 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);
}
/**
- * 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 service dependency rules.
*
- * @param deviceId device id to install flow rules
- * @param inPort in port
- * @param dstInfo destination information
- * @return forwarding objective, or null if it fails to build it
+ * @param tService tenant cord service
+ * @param pService provider cord service
*/
- private ForwardingObjective.Builder vtnRulesRemoteNode(DeviceId deviceId, Port inPort, DestinationInfo dstInfo) {
- checkArgument(isTunnelPort(dstInfo.output()));
+ public void populateServiceDependencyRules(CordService tService, CordService pService) {
+ checkNotNull(tService);
+ checkNotNull(pService);
- TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
- TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+ Ip4Prefix srcRange = tService.serviceIpRange().getIp4Prefix();
+ Ip4Prefix dstRange = pService.serviceIpRange().getIp4Prefix();
+ Ip4Address serviceIp = pService.serviceIp().getIp4Address();
- ExtensionTreatment extTreatment =
- getTunnelDstInstruction(deviceId, dstInfo.remoteIp().getIp4Address());
- if (extTreatment == null) {
+ 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 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 (!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;
+ }
+
+ /**
+ * Returns group buckets for a given device.
+ *
+ * @param deviceId device id
+ * @param tunnelId tunnel id
+ * @param hosts list of host
+ * @return group buckets
+ */
+ 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();
+
+ 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);
+ }
+
+ /**
+ * 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 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 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();
+
+ 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();
+
+ processFlowRule(true, flowRuleDirect);
+ }
+ }
+
+ /**
+ * 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();
+
+ 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)
+ .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)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .withPriority(DEFAULT_PRIORITY)
+ .forDevice(deviceId)
+ .forTable(TABLE_IN_SERVICE)
+ .makePermanent()
+ .build();
+
+ processFlowRule(true, flowRule);
+ });
+ }
+ }
+
+ /**
+ * Populates flow rules for DST_IP table.
+ *
+ * @param deviceId device id
+ * @param inPort in port
+ * @param dstMac mac address
+ * @param dstIp destination ip
+ * @param tunnelId tunnel id
+ * @param tunnelIp tunnel remote ip
+ */
+ 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();
+
+ FlowRule flowRule = DefaultFlowRule.builder()
+ .fromApp(appId)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .withPriority(DEFAULT_PRIORITY)
+ .forDevice(deviceId)
+ .forTable(TABLE_DST_IP)
+ .makePermanent()
+ .build();
+
+ processFlowRule(true, flowRule);
+
+ for (Device device : deviceService.getAvailableDevices(SWITCH)) {
+ if (device.id().equals(deviceId)) {
+ continue;
+ }
+
+ ExtensionTreatment tunnelDst = getTunnelDst(device.id(), tunnelIp.getIp4Address());
+ if (tunnelDst == null) {
+ continue;
+ }
+
+ treatment = DefaultTrafficTreatment.builder()
+ .setEthDst(dstMac)
+ .setTunnelId(tunnelId)
+ .extension(tunnelDst, device.id())
+ .setOutput(getTunnelPort(device.id()))
+ .build();
+
+ flowRule = DefaultFlowRule.builder()
+ .fromApp(appId)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .withPriority(DEFAULT_PRIORITY)
+ .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)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .withPriority(DEFAULT_PRIORITY)
+ .forDevice(deviceId)
+ .forTable(TABLE_TUNNEL_IN)
+ .makePermanent()
+ .build();
+
+ processFlowRule(true, flowRule);
+ }
+
+ /**
+ * Installs or uninstall a given rule.
+ *
+ * @param install true to install, false to uninstall
+ * @param rule rule
+ */
+ 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;
}
- sBuilder.matchInPort(inPort.number())
- .matchEthDst(dstInfo.mac());
-
- tBuilder.extension(extTreatment, deviceId)
- .setTunnelId(dstInfo.tunnelId())
- .setOutput(dstInfo.output().number());
-
- return DefaultForwardingObjective.builder()
- .withSelector(sBuilder.build())
- .withTreatment(tBuilder.build())
- .withPriority(DEFAULT_PRIORITY)
- .withFlag(ForwardingObjective.Flag.VERSATILE)
- .fromApp(appId)
- .makePermanent();
+ return ((ModEtherInstruction) instruction).mac();
}
/**
- * Checks if a given port is tunnel interface or not.
- * It assumes the tunnel interface contains tunnelType string in its name.
+ * Returns the destination mac address from a given flow rule if the rule
+ * contains the match of it.
*
- * @param port port
- * @return true if the port is tunnel interface, false otherwise.
+ * @param flowRule flow rule
+ * @return mac address, or null if the rule doesn't have destination mac match
*/
- private boolean isTunnelPort(Port port) {
- return port.annotations().value("portName").contains(tunnelType);
+ 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 @@
* @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));
- ExtensionTreatmentResolver resolver = handler.behaviour(ExtensionTreatmentResolver.class);
+ 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;
}
}
}
+
diff --git a/src/main/java/org/onosproject/cordvtn/CordVtnService.java b/src/main/java/org/onosproject/cordvtn/CordVtnService.java
index 29b4525..2c3c23b 100644
--- a/src/main/java/org/onosproject/cordvtn/CordVtnService.java
+++ b/src/main/java/org/onosproject/cordvtn/CordVtnService.java
@@ -69,15 +69,16 @@
/**
* 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);
}
diff --git a/src/main/java/org/onosproject/cordvtn/DestinationInfo.java b/src/main/java/org/onosproject/cordvtn/DestinationInfo.java
deleted file mode 100644
index 290cc17..0000000
--- a/src/main/java/org/onosproject/cordvtn/DestinationInfo.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * 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;
- }
-}
diff --git a/src/main/java/org/onosproject/cordvtn/rest/ServiceDependencyWebResource.java b/src/main/java/org/onosproject/cordvtn/rest/ServiceDependencyWebResource.java
index d2c5567..c5d1ded 100644
--- a/src/main/java/org/onosproject/cordvtn/rest/ServiceDependencyWebResource.java
+++ b/src/main/java/org/onosproject/cordvtn/rest/ServiceDependencyWebResource.java
@@ -58,14 +58,16 @@
/**
* 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();
}