Michele Santuari
Committed by Gerrit Code Review

SinglePoint to MultiPoint Intent initial implementation

Change-Id: I1010997ce4ea993ae34afb8dab4b6c0ae112448d
package org.onlab.onos.cli.net;
import static org.onlab.onos.net.DeviceId.deviceId;
import static org.onlab.onos.net.PortNumber.portNumber;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.flow.DefaultTrafficTreatment;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
import org.onlab.onos.net.intent.Constraint;
import org.onlab.onos.net.intent.IntentService;
import org.onlab.onos.net.intent.SinglePointToMultiPointIntent;
@Command(scope = "onos", name = "add-single-to-multi-intent",
description = "Installs connectivity intent between multiple egress devices and a single ingress device")
public class AddSinglePointToMultiPointIntentCommand extends ConnectivityIntentCommand {
@Argument(index = 0, name = "egressDevices ingressDevice",
description = "egress Device/Port...egress Device/Port ingressDevice/port",
required = true, multiValued = true)
String[] deviceStrings = null;
@Override
protected void execute() {
IntentService service = get(IntentService.class);
if (deviceStrings.length < 2) {
return;
}
String ingressDeviceString = deviceStrings[deviceStrings.length - 1];
DeviceId ingressDeviceId = deviceId(getDeviceId(ingressDeviceString));
PortNumber ingressPortNumber = portNumber(getPortNumber(ingressDeviceString));
ConnectPoint ingressPoint = new ConnectPoint(ingressDeviceId,
ingressPortNumber);
Set<ConnectPoint> egressPoints = new HashSet<>();
for (int index = 0; index < deviceStrings.length - 1; index++) {
String egressDeviceString = deviceStrings[index];
DeviceId egressDeviceId = deviceId(getDeviceId(egressDeviceString));
PortNumber egressPortNumber = portNumber(getPortNumber(egressDeviceString));
ConnectPoint egress = new ConnectPoint(egressDeviceId,
egressPortNumber);
egressPoints.add(egress);
}
TrafficSelector selector = buildTrafficSelector();
TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
List<Constraint> constraints = buildConstraints();
SinglePointToMultiPointIntent intent = new SinglePointToMultiPointIntent(
appId(),
selector,
treatment,
ingressPoint,
egressPoints,
constraints);
service.submit(intent);
}
/**
* Extracts the port number portion of the ConnectPoint.
*
* @param deviceString string representing the device/port
* @return port number as a string, empty string if the port is not found
*/
private String getPortNumber(String deviceString) {
int slash = deviceString.indexOf('/');
if (slash <= 0) {
return "";
}
return deviceString.substring(slash + 1, deviceString.length());
}
/**
* Extracts the device ID portion of the ConnectPoint.
*
* @param deviceString string representing the device/port
* @return device ID string
*/
private String getDeviceId(String deviceString) {
int slash = deviceString.indexOf('/');
if (slash <= 0) {
return "";
}
return deviceString.substring(0, slash);
}
}
......@@ -340,7 +340,7 @@ public class IntentsListCommand extends AbstractShellCommand {
} else if (intent instanceof LinkCollectionIntent) {
LinkCollectionIntent li = (LinkCollectionIntent) intent;
print(" links=%s", li.links());
print(" egress=%s", li.egressPoint());
print(" egress=%s", li.egressPoints());
}
List<Intent> installable = service.getInstallableIntents(intent.id());
......
......@@ -161,6 +161,15 @@
</optional-completers>
</command>
<command>
<action class="org.onlab.onos.cli.net.AddSinglePointToMultiPointIntentCommand"/>
<completers>
<ref component-id="connectPointCompleter"/>
</completers>
<optional-completers>
<entry key="-t" value-ref="ethTypeCompleter"/>
</optional-completers>
</command>
<command>
<action class="org.onlab.onos.cli.net.IntentPushTestCommand"/>
<completers>
<ref component-id="connectPointCompleter"/>
......
......@@ -16,6 +16,8 @@
package org.onlab.onos.net.intent;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableSet;
import org.onlab.onos.core.ApplicationId;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.Link;
......@@ -34,11 +36,11 @@ public final class LinkCollectionIntent extends ConnectivityIntent {
private final Set<Link> links;
private final ConnectPoint egressPoint;
private final Set<ConnectPoint> egressPoints;
/**
* Creates a new actionable intent capable of funneling the selected
* traffic along the specified convergent tree and out the given egress point.
* Creates a new actionable intent capable of funneling the selected traffic
* along the specified convergent tree and out the given egress point.
*
* @param appId application identifier
* @param selector traffic match
......@@ -77,7 +79,31 @@ public final class LinkCollectionIntent extends ConnectivityIntent {
super(id(LinkCollectionIntent.class, selector, treatment, links, egressPoint, constraints),
appId, resources(links), selector, treatment, constraints);
this.links = links;
this.egressPoint = egressPoint;
this.egressPoints = ImmutableSet.of(egressPoint);
}
/**
* Creates a new actionable intent capable of funneling the selected traffic
* along the specified convergent tree and out the given egress point.
*
* @param appId application identifier
* @param selector traffic match
* @param treatment action
* @param links traversed links
* @param egressPoints Set of egress point
* @throws NullPointerException {@code path} is null
*/
public LinkCollectionIntent(ApplicationId appId,
TrafficSelector selector,
TrafficTreatment treatment,
Set<Link> links,
Set<ConnectPoint> egressPoints,
List<Constraint> constraints) {
super(id(LinkCollectionIntent.class, selector, treatment, links,
egressPoints), appId, resources(links), selector, treatment);
this.links = links;
this.egressPoints = ImmutableSet.copyOf(egressPoints);
}
/**
......@@ -86,7 +112,7 @@ public final class LinkCollectionIntent extends ConnectivityIntent {
protected LinkCollectionIntent() {
super();
this.links = null;
this.egressPoint = null;
this.egressPoints = null;
}
/**
......@@ -104,8 +130,8 @@ public final class LinkCollectionIntent extends ConnectivityIntent {
*
* @return the egress point
*/
public ConnectPoint egressPoint() {
return egressPoint;
public Set<ConnectPoint> egressPoints() {
return egressPoints;
}
@Override
......@@ -121,7 +147,7 @@ public final class LinkCollectionIntent extends ConnectivityIntent {
.add("selector", selector())
.add("treatment", treatment())
.add("links", links())
.add("egress", egressPoint())
.add("egress", egressPoints())
.toString();
}
}
......
......@@ -17,12 +17,15 @@ package org.onlab.onos.net.intent;
import com.google.common.base.MoreObjects;
import com.google.common.collect.Sets;
import org.onlab.onos.core.ApplicationId;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
import java.util.Collections;
import java.util.Set;
import java.util.List;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
......@@ -38,30 +41,50 @@ public class SinglePointToMultiPointIntent extends ConnectivityIntent {
/**
* Creates a new single-to-multi point connectivity intent.
*
* @param appId application identifier
* @param selector traffic selector
* @param treatment treatment
* @param appId application identifier
* @param selector traffic selector
* @param treatment treatment
* @param ingressPoint port on which traffic will ingress
* @param egressPoints set of ports on which traffic will egress
* @throws NullPointerException if {@code ingressPoint} or
* {@code egressPoints} is null
* @throws IllegalArgumentException if the size of {@code egressPoints} is
* not more than 1
*/
public SinglePointToMultiPointIntent(ApplicationId appId,
TrafficSelector selector, TrafficTreatment treatment,
ConnectPoint ingressPoint, Set<ConnectPoint> egressPoints) {
this(appId, selector, treatment, ingressPoint, egressPoints, Collections.emptyList());
}
/**
* Creates a new single-to-multi point connectivity intent.
*
* @param appId application identifier
* @param selector traffic selector
* @param treatment treatment
* @param ingressPoint port on which traffic will ingress
* @param egressPoints set of ports on which traffic will egress
* @throws NullPointerException if {@code ingressPoint} or
* {@code egressPoints} is null
* @param constraints constraints to apply to the intent
* @throws NullPointerException if {@code ingressPoint} or
* {@code egressPoints} is null
* @throws IllegalArgumentException if the size of {@code egressPoints} is
* not more than 1
* not more than 1
*/
public SinglePointToMultiPointIntent(ApplicationId appId,
TrafficSelector selector,
TrafficTreatment treatment,
ConnectPoint ingressPoint,
Set<ConnectPoint> egressPoints) {
TrafficSelector selector, TrafficTreatment treatment,
ConnectPoint ingressPoint, Set<ConnectPoint> egressPoints,
List<Constraint> constraints) {
super(id(SinglePointToMultiPointIntent.class, selector, treatment,
ingressPoint, egressPoints), appId, null, selector, treatment);
ingressPoint, egressPoints), appId, null, selector, treatment,
constraints);
checkNotNull(egressPoints);
checkNotNull(ingressPoint);
checkArgument(!egressPoints.isEmpty(), "Egress point set cannot be empty");
checkArgument(!egressPoints.contains(ingressPoint),
"Set of egresses should not contain ingress (ingress: %s)", ingressPoint);
this.ingressPoint = ingressPoint;
this.ingressPoint = checkNotNull(ingressPoint);
this.egressPoints = Sets.newHashSet(egressPoints);
}
......@@ -75,7 +98,8 @@ public class SinglePointToMultiPointIntent extends ConnectivityIntent {
}
/**
* Returns the port on which the ingress traffic should be connected to the egress.
* Returns the port on which the ingress traffic should be connected to the
* egress.
*
* @return ingress port
*/
......@@ -101,6 +125,7 @@ public class SinglePointToMultiPointIntent extends ConnectivityIntent {
.add("treatment", treatment())
.add("ingress", ingressPoint)
.add("egress", egressPoints)
.add("constraints", constraints())
.toString();
}
......
......@@ -28,6 +28,7 @@ import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.intent.constraint.LambdaConstraint;
import org.onlab.onos.net.resource.Lambda;
import com.google.common.collect.ImmutableSet;
import com.google.common.testing.EqualsTester;
import static org.hamcrest.MatcherAssert.assertThat;
......@@ -111,7 +112,7 @@ public class LinkCollectionIntentTest {
assertThat(collectionIntent.isInstallable(), is(true));
assertThat(collectionIntent.treatment(), is(treatment));
assertThat(collectionIntent.selector(), is(selector));
assertThat(collectionIntent.egressPoint(), is(egress));
assertThat(collectionIntent.egressPoints(), is(ImmutableSet.of(egress)));
assertThat(collectionIntent.resources(), hasSize(1));
final List<Constraint> createdConstraints = collectionIntent.constraints();
assertThat(createdConstraints, hasSize(0));
......@@ -140,7 +141,7 @@ public class LinkCollectionIntentTest {
assertThat(collectionIntent.isInstallable(), is(true));
assertThat(collectionIntent.treatment(), is(treatment));
assertThat(collectionIntent.selector(), is(selector));
assertThat(collectionIntent.egressPoint(), is(egress));
assertThat(collectionIntent.egressPoints(), is(ImmutableSet.of(egress)));
final List<Constraint> createdConstraints = collectionIntent.constraints();
assertThat(createdConstraints, hasSize(1));
......@@ -161,7 +162,7 @@ public class LinkCollectionIntentTest {
assertThat(collectionIntent.isInstallable(), is(true));
assertThat(collectionIntent.treatment(), nullValue());
assertThat(collectionIntent.selector(), nullValue());
assertThat(collectionIntent.egressPoint(), nullValue());
assertThat(collectionIntent.egressPoints(), nullValue());
final List<Constraint> createdConstraints = collectionIntent.constraints();
assertThat(createdConstraints, hasSize(0));
......
......@@ -15,9 +15,12 @@
*/
package org.onlab.onos.net.intent.impl;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
......@@ -26,6 +29,7 @@ import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.onos.core.ApplicationId;
import org.onlab.onos.core.CoreService;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.PortNumber;
......@@ -42,18 +46,16 @@ import org.onlab.onos.net.intent.IntentExtensionService;
import org.onlab.onos.net.intent.IntentInstaller;
import org.onlab.onos.net.intent.LinkCollectionIntent;
import org.onlab.onos.net.intent.PathIntent;
import org.slf4j.Logger;
import com.google.common.collect.Lists;
/**
* Installer for {@link org.onlab.onos.net.intent.LinkCollectionIntent}
* path segment intents.
* Installer for {@link org.onlab.onos.net.intent.LinkCollectionIntent} path
* segment intents.
*/
@Component(immediate = true)
public class LinkCollectionIntentInstaller implements IntentInstaller<LinkCollectionIntent> {
private final Logger log = getLogger(getClass());
public class LinkCollectionIntentInstaller
implements IntentInstaller<LinkCollectionIntent> {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected IntentExtensionService intentManager;
......@@ -76,37 +78,58 @@ public class LinkCollectionIntentInstaller implements IntentInstaller<LinkCollec
@Override
public List<FlowRuleBatchOperation> install(LinkCollectionIntent intent) {
Map<DeviceId, Set<PortNumber>> outputMap = new HashMap<DeviceId, Set<PortNumber>>();
List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
for (Link link : intent.links()) {
rules.add(createBatchEntry(FlowRuleOperation.ADD,
intent,
link.src().deviceId(),
link.src().port()));
if (outputMap.get(link.src().deviceId()) == null) {
outputMap.put(link.src().deviceId(), new HashSet<PortNumber>());
}
outputMap.get(link.src().deviceId()).add(link.src().port());
}
for (ConnectPoint egressPoint : intent.egressPoints()) {
if (outputMap.get(egressPoint.deviceId()) == null) {
outputMap
.put(egressPoint.deviceId(), new HashSet<PortNumber>());
}
outputMap.get(egressPoint.deviceId()).add(egressPoint.port());
}
rules.add(createBatchEntry(FlowRuleOperation.ADD,
intent,
intent.egressPoint().deviceId(),
intent.egressPoint().port()));
for (Entry<DeviceId, Set<PortNumber>> entry : outputMap.entrySet()) {
rules.add(createBatchEntry(FlowRuleOperation.ADD, intent,
entry.getKey(), entry.getValue()));
}
return Lists.newArrayList(new FlowRuleBatchOperation(rules));
}
@Override
public List<FlowRuleBatchOperation> uninstall(LinkCollectionIntent intent) {
Map<DeviceId, Set<PortNumber>> outputMap = new HashMap<DeviceId, Set<PortNumber>>();
List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
for (Link link : intent.links()) {
rules.add(createBatchEntry(FlowRuleOperation.REMOVE,
intent,
link.src().deviceId(),
link.src().port()));
if (outputMap.get(link.src().deviceId()) == null) {
outputMap.put(link.src().deviceId(), new HashSet<PortNumber>());
}
outputMap.get(link.src().deviceId()).add(link.src().port());
}
for (ConnectPoint egressPoint : intent.egressPoints()) {
if (outputMap.get(egressPoint.deviceId()) == null) {
outputMap
.put(egressPoint.deviceId(), new HashSet<PortNumber>());
}
outputMap.get(egressPoint.deviceId()).add(egressPoint.port());
}
rules.add(createBatchEntry(FlowRuleOperation.REMOVE,
intent,
intent.egressPoint().deviceId(),
intent.egressPoint().port()));
for (Entry<DeviceId, Set<PortNumber>> entry : outputMap.entrySet()) {
rules.add(createBatchEntry(FlowRuleOperation.REMOVE, intent,
entry.getKey(), entry.getValue()));
}
return Lists.newArrayList(new FlowRuleBatchOperation(rules));
}
......@@ -128,17 +151,20 @@ public class LinkCollectionIntentInstaller implements IntentInstaller<LinkCollec
* @return the new flow rule batch entry
*/
private FlowRuleBatchEntry createBatchEntry(FlowRuleOperation operation,
LinkCollectionIntent intent,
DeviceId deviceId,
PortNumber outPort) {
LinkCollectionIntent intent,
DeviceId deviceId,
Set<PortNumber> outPorts) {
TrafficTreatment.Builder treatmentBuilder =
DefaultTrafficTreatment.builder(intent.treatment());
TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
.builder(intent.treatment());
TrafficTreatment treatment = treatmentBuilder.setOutput(outPort).build();
for (PortNumber outPort : outPorts) {
treatmentBuilder.setOutput(outPort);
}
TrafficTreatment treatment = treatmentBuilder.build();
TrafficSelector selector = DefaultTrafficSelector.builder(intent.selector())
.build();
TrafficSelector selector = DefaultTrafficSelector
.builder(intent.selector()).build();
FlowRule rule = new DefaultFlowRule(deviceId,
selector, treatment, 123,
......
package org.onlab.onos.net.intent.impl;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.Path;
import org.onlab.onos.net.intent.Intent;
import org.onlab.onos.net.intent.LinkCollectionIntent;
import org.onlab.onos.net.intent.SinglePointToMultiPointIntent;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.net.resource.LinkResourceAllocations;
@Component(immediate = true)
public class SinglePointToMultiPointIntentCompiler
extends ConnectivityIntentCompiler<SinglePointToMultiPointIntent> {
// TODO: use off-the-shell core provider ID
private static final ProviderId PID =
new ProviderId("core", "org.onlab.onos.core", true);
@Activate
public void activate() {
intentManager.registerCompiler(SinglePointToMultiPointIntent.class,
this);
}
@Deactivate
public void deactivate() {
intentManager.unregisterCompiler(SinglePointToMultiPointIntent.class);
}
@Override
public List<Intent> compile(SinglePointToMultiPointIntent intent,
List<Intent> installable,
Set<LinkResourceAllocations> resources) {
Set<Link> links = new HashSet<>();
//FIXME: need to handle the case where ingress/egress points are on same switch
for (ConnectPoint egressPoint : intent.egressPoints()) {
Path path = getPath(intent, intent.ingressPoint().deviceId(), egressPoint.deviceId());
links.addAll(path.links());
}
Intent result = new LinkCollectionIntent(intent.appId(),
intent.selector(),
intent.treatment(), links,
intent.egressPoints(), null);
return Arrays.asList(result);
}
}
......@@ -80,6 +80,7 @@ import org.onlab.onos.net.intent.OpticalPathIntent;
import org.onlab.onos.net.intent.PathIntent;
import org.onlab.onos.net.intent.PointToPointIntent;
import org.onlab.onos.net.intent.constraint.AnnotationConstraint;
import org.onlab.onos.net.intent.SinglePointToMultiPointIntent;
import org.onlab.onos.net.intent.constraint.BandwidthConstraint;
import org.onlab.onos.net.intent.constraint.BooleanConstraint;
import org.onlab.onos.net.intent.constraint.LambdaConstraint;
......@@ -251,6 +252,7 @@ public final class KryoNamespaces {
HostToHostIntent.class,
PointToPointIntent.class,
MultiPointToSinglePointIntent.class,
SinglePointToMultiPointIntent.class,
LinkCollectionIntent.class,
OpticalConnectivityIntent.class,
OpticalPathIntent.class,
......