Ray Milkey

Add a Multiple source to single destination intent

This is a very simplistic implementation of an intent that
represents multiple sources with a single destination.
The implementaiton is simple, it just computes the union
of the set of path segments needed to satisfy all the connections
and installs them.

The unit test is just a skeleton with a single test case and
needs to be expanded.
1 +package org.onlab.onos.cli.net;
2 +
3 +import java.util.HashSet;
4 +import java.util.Set;
5 +
6 +import org.apache.karaf.shell.commands.Argument;
7 +import org.apache.karaf.shell.commands.Command;
8 +import org.onlab.onos.cli.AbstractShellCommand;
9 +import org.onlab.onos.net.ConnectPoint;
10 +import org.onlab.onos.net.DeviceId;
11 +import org.onlab.onos.net.PortNumber;
12 +import org.onlab.onos.net.flow.DefaultTrafficSelector;
13 +import org.onlab.onos.net.flow.DefaultTrafficTreatment;
14 +import org.onlab.onos.net.flow.TrafficSelector;
15 +import org.onlab.onos.net.flow.TrafficTreatment;
16 +import org.onlab.onos.net.intent.Intent;
17 +import org.onlab.onos.net.intent.IntentId;
18 +import org.onlab.onos.net.intent.IntentService;
19 +import org.onlab.onos.net.intent.MultiPointToSinglePointIntent;
20 +import org.onlab.packet.Ethernet;
21 +
22 +/**
23 + * Installs point-to-point connectivity intents.
24 + */
25 +@Command(scope = "onos", name = "add-multi-to-single-intent",
26 + description = "Installs point-to-point connectivity intent")
27 +public class AddMultiPointToSinglePointIntentCommand extends AbstractShellCommand {
28 +
29 + @Argument(index = 0, name = "ingressDevices",
30 + description = "Ingress Device/Port Description",
31 + required = true, multiValued = true)
32 + String[] deviceStrings = null;
33 +
34 + private static long id = 0x7070001;
35 +
36 + @Override
37 + protected void execute() {
38 + IntentService service = get(IntentService.class);
39 +
40 + if (deviceStrings.length < 2) {
41 + return;
42 + }
43 +
44 + String egressDeviceString = deviceStrings[deviceStrings.length - 1];
45 + DeviceId egressDeviceId = DeviceId.deviceId(getDeviceId(egressDeviceString));
46 + PortNumber egressPortNumber =
47 + PortNumber.portNumber(getPortNumber(egressDeviceString));
48 + ConnectPoint egress = new ConnectPoint(egressDeviceId, egressPortNumber);
49 + Set<ConnectPoint> ingressPoints = new HashSet<>();
50 +
51 + for (int index = 0; index < deviceStrings.length - 1; index++) {
52 + String ingressDeviceString = deviceStrings[index];
53 + DeviceId ingressDeviceId = DeviceId.deviceId(getDeviceId(ingressDeviceString));
54 + PortNumber ingressPortNumber =
55 + PortNumber.portNumber(getPortNumber(ingressDeviceString));
56 + ConnectPoint ingress = new ConnectPoint(ingressDeviceId, ingressPortNumber);
57 + ingressPoints.add(ingress);
58 + }
59 +
60 +
61 + TrafficSelector selector = DefaultTrafficSelector.builder()
62 + .matchEthType(Ethernet.TYPE_IPV4)
63 + .build();
64 + TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
65 +
66 + Intent intent =
67 + new MultiPointToSinglePointIntent(new IntentId(id++),
68 + selector,
69 + treatment,
70 + ingressPoints,
71 + egress);
72 + service.submit(intent);
73 + }
74 +
75 + /**
76 + * Extracts the port number portion of the ConnectPoint.
77 + *
78 + * @param deviceString string representing the device/port
79 + * @return port number as a string, empty string if the port is not found
80 + */
81 + private String getPortNumber(String deviceString) {
82 + int slash = deviceString.indexOf('/');
83 + if (slash <= 0) {
84 + return "";
85 + }
86 + return deviceString.substring(slash + 1, deviceString.length());
87 + }
88 +
89 + /**
90 + * Extracts the device ID portion of the ConnectPoint.
91 + *
92 + * @param deviceString string representing the device/port
93 + * @return device ID string
94 + */
95 + private String getDeviceId(String deviceString) {
96 + int slash = deviceString.indexOf('/');
97 + if (slash <= 0) {
98 + return "";
99 + }
100 + return deviceString.substring(0, slash);
101 + }
102 +}
...@@ -83,6 +83,12 @@ ...@@ -83,6 +83,12 @@
83 </completers> 83 </completers>
84 </command> 84 </command>
85 <command> 85 <command>
86 + <action class="org.onlab.onos.cli.net.AddMultiPointToSinglePointIntentCommand"/>
87 + <completers>
88 + <ref component-id="connectPointCompleter"/>
89 + </completers>
90 + </command>
91 + <command>
86 <action class="org.onlab.onos.cli.net.IntentPushTestCommand"/> 92 <action class="org.onlab.onos.cli.net.IntentPushTestCommand"/>
87 <completers> 93 <completers>
88 <ref component-id="connectPointCompleter"/> 94 <ref component-id="connectPointCompleter"/>
......
1 +package org.onlab.onos.net.intent;
2 +
3 +import java.util.Collection;
4 +import java.util.Objects;
5 +import java.util.Set;
6 +
7 +import org.onlab.onos.net.Link;
8 +import org.onlab.onos.net.flow.TrafficSelector;
9 +import org.onlab.onos.net.flow.TrafficTreatment;
10 +
11 +import com.google.common.base.MoreObjects;
12 +
13 +/**
14 + * Abstraction of a connectivity intent that is implemented by a set of path
15 + * segments.
16 + */
17 +public class LinkCollectionIntent extends ConnectivityIntent implements InstallableIntent {
18 +
19 + private final Set<Link> links;
20 +
21 + /**
22 + * Creates a new point-to-point intent with the supplied ingress/egress
23 + * ports and using the specified explicit path.
24 + *
25 + * @param id intent identifier
26 + * @param selector traffic match
27 + * @param treatment action
28 + * @param links traversed links
29 + * @throws NullPointerException {@code path} is null
30 + */
31 + public LinkCollectionIntent(IntentId id,
32 + TrafficSelector selector,
33 + TrafficTreatment treatment,
34 + Set<Link> links) {
35 + super(id, selector, treatment);
36 + this.links = links;
37 + }
38 +
39 + protected LinkCollectionIntent() {
40 + super();
41 + this.links = null;
42 + }
43 +
44 + @Override
45 + public Collection<Link> requiredLinks() {
46 + return links;
47 + }
48 +
49 + public Set<Link> links() {
50 + return links;
51 + }
52 +
53 + @Override
54 + public boolean equals(Object o) {
55 + if (this == o) {
56 + return true;
57 + }
58 + if (o == null || getClass() != o.getClass()) {
59 + return false;
60 + }
61 + if (!super.equals(o)) {
62 + return false;
63 + }
64 +
65 + LinkCollectionIntent that = (LinkCollectionIntent) o;
66 +
67 + return Objects.equals(this.links, that.links);
68 + }
69 +
70 + @Override
71 + public int hashCode() {
72 + return Objects.hash(super.hashCode(), links);
73 + }
74 +
75 + @Override
76 + public String toString() {
77 + return MoreObjects.toStringHelper(getClass())
78 + .add("id", id())
79 + .add("match", selector())
80 + .add("action", treatment())
81 + .add("links", links())
82 + .toString();
83 + }
84 +}
1 +package org.onlab.onos.net.intent.impl;
2 +
3 +import java.util.List;
4 +import java.util.concurrent.Future;
5 +
6 +import org.apache.felix.scr.annotations.Activate;
7 +import org.apache.felix.scr.annotations.Component;
8 +import org.apache.felix.scr.annotations.Deactivate;
9 +import org.apache.felix.scr.annotations.Reference;
10 +import org.apache.felix.scr.annotations.ReferenceCardinality;
11 +import org.onlab.onos.ApplicationId;
12 +import org.onlab.onos.CoreService;
13 +import org.onlab.onos.net.Link;
14 +import org.onlab.onos.net.flow.CompletedBatchOperation;
15 +import org.onlab.onos.net.flow.DefaultFlowRule;
16 +import org.onlab.onos.net.flow.DefaultTrafficSelector;
17 +import org.onlab.onos.net.flow.FlowRule;
18 +import org.onlab.onos.net.flow.FlowRuleBatchEntry;
19 +import org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation;
20 +import org.onlab.onos.net.flow.FlowRuleBatchOperation;
21 +import org.onlab.onos.net.flow.FlowRuleService;
22 +import org.onlab.onos.net.flow.TrafficSelector;
23 +import org.onlab.onos.net.flow.TrafficTreatment;
24 +import org.onlab.onos.net.intent.IntentExtensionService;
25 +import org.onlab.onos.net.intent.IntentInstaller;
26 +import org.onlab.onos.net.intent.LinkCollectionIntent;
27 +import org.onlab.onos.net.intent.PathIntent;
28 +import org.slf4j.Logger;
29 +
30 +import com.google.common.collect.Lists;
31 +
32 +import static org.onlab.onos.net.flow.DefaultTrafficTreatment.builder;
33 +import static org.slf4j.LoggerFactory.getLogger;
34 +
35 +/**
36 + * Installer for {@link org.onlab.onos.net.intent.LinkCollectionIntent}
37 + * path segment intents.
38 + */
39 +@Component(immediate = true)
40 +public class LinkCollectionIntentInstaller implements IntentInstaller<LinkCollectionIntent> {
41 +
42 + private final Logger log = getLogger(getClass());
43 +
44 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
45 + protected IntentExtensionService intentManager;
46 +
47 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
48 + protected FlowRuleService flowRuleService;
49 +
50 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
51 + protected CoreService coreService;
52 +
53 + private ApplicationId appId;
54 +
55 + @Activate
56 + public void activate() {
57 + appId = coreService.registerApplication("org.onlab.onos.net.intent");
58 + intentManager.registerInstaller(LinkCollectionIntent.class, this);
59 + }
60 +
61 + @Deactivate
62 + public void deactivate() {
63 + intentManager.unregisterInstaller(PathIntent.class);
64 + }
65 +
66 + /**
67 + * Apply a list of FlowRules.
68 + *
69 + * @param rules rules to apply
70 + */
71 + private Future<CompletedBatchOperation> applyBatch(List<FlowRuleBatchEntry> rules) {
72 + FlowRuleBatchOperation batch = new FlowRuleBatchOperation(rules);
73 + return flowRuleService.applyBatch(batch);
74 + }
75 +
76 + @Override
77 + public Future<CompletedBatchOperation> install(LinkCollectionIntent intent) {
78 + TrafficSelector.Builder builder =
79 + DefaultTrafficSelector.builder(intent.selector());
80 + List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
81 + for (Link link : intent.links()) {
82 + TrafficTreatment treatment = builder()
83 + .setOutput(link.src().port()).build();
84 +
85 + FlowRule rule = new DefaultFlowRule(link.src().deviceId(),
86 + builder.build(), treatment,
87 + 123, appId, 600);
88 + rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, rule));
89 + }
90 +
91 + return applyBatch(rules);
92 + }
93 +
94 + @Override
95 + public Future<CompletedBatchOperation> uninstall(LinkCollectionIntent intent) {
96 + TrafficSelector.Builder builder =
97 + DefaultTrafficSelector.builder(intent.selector());
98 + List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
99 +
100 + for (Link link : intent.links()) {
101 + TrafficTreatment treatment = builder()
102 + .setOutput(link.src().port()).build();
103 + FlowRule rule = new DefaultFlowRule(link.src().deviceId(),
104 + builder.build(), treatment,
105 + 123, appId, 600);
106 + rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, rule));
107 + }
108 + return applyBatch(rules);
109 + }
110 +}
1 +package org.onlab.onos.net.intent.impl;
2 +
3 +import java.util.Arrays;
4 +import java.util.HashSet;
5 +import java.util.List;
6 +import java.util.Set;
7 +
8 +import org.apache.felix.scr.annotations.Activate;
9 +import org.apache.felix.scr.annotations.Component;
10 +import org.apache.felix.scr.annotations.Deactivate;
11 +import org.apache.felix.scr.annotations.Reference;
12 +import org.apache.felix.scr.annotations.ReferenceCardinality;
13 +import org.onlab.onos.net.ConnectPoint;
14 +import org.onlab.onos.net.Link;
15 +import org.onlab.onos.net.Path;
16 +import org.onlab.onos.net.intent.IdGenerator;
17 +import org.onlab.onos.net.intent.Intent;
18 +import org.onlab.onos.net.intent.IntentCompiler;
19 +import org.onlab.onos.net.intent.IntentExtensionService;
20 +import org.onlab.onos.net.intent.IntentId;
21 +import org.onlab.onos.net.intent.LinkCollectionIntent;
22 +import org.onlab.onos.net.intent.MultiPointToSinglePointIntent;
23 +import org.onlab.onos.net.intent.PointToPointIntent;
24 +import org.onlab.onos.net.topology.PathService;
25 +
26 +/**
27 + * An intent compiler for
28 + * {@link org.onlab.onos.net.intent.MultiPointToSinglePointIntent}.
29 + */
30 +@Component(immediate = true)
31 +public class MultiPointToSinglePointIntentCompiler
32 + implements IntentCompiler<MultiPointToSinglePointIntent> {
33 +
34 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
35 + protected IntentExtensionService intentManager;
36 +
37 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
38 + protected PathService pathService;
39 +
40 + private IdGenerator<IntentId> intentIdGenerator;
41 +
42 + @Activate
43 + public void activate() {
44 + IdBlockAllocator idBlockAllocator = new DummyIdBlockAllocator();
45 + intentIdGenerator = new IdBlockAllocatorBasedIntentIdGenerator(idBlockAllocator);
46 + intentManager.registerCompiler(MultiPointToSinglePointIntent.class, this);
47 + }
48 +
49 + @Deactivate
50 + public void deactivate() {
51 + intentManager.unregisterCompiler(PointToPointIntent.class);
52 + }
53 +
54 + @Override
55 + public List<Intent> compile(MultiPointToSinglePointIntent intent) {
56 + Set<Link> links = new HashSet<>();
57 +
58 + for (ConnectPoint ingressPoint : intent.ingressPoints()) {
59 + Path path = getPath(ingressPoint, intent.egressPoint());
60 + links.addAll(path.links());
61 + }
62 +
63 + Intent result = new LinkCollectionIntent(intentIdGenerator.getNewId(),
64 + intent.selector(), intent.treatment(),
65 + links);
66 + return Arrays.asList(result);
67 + }
68 +
69 + /**
70 + * Computes a path between two ConnectPoints.
71 + *
72 + * @param one start of the path
73 + * @param two end of the path
74 + * @return Path between the two
75 + * @throws org.onlab.onos.net.intent.impl.PathNotFoundException if a path cannot be found
76 + */
77 + private Path getPath(ConnectPoint one, ConnectPoint two) {
78 + Set<Path> paths = pathService.getPaths(one.deviceId(), two.deviceId());
79 + if (paths.isEmpty()) {
80 + throw new PathNotFoundException("No path from " + one + " to " + two);
81 + }
82 + // TODO: let's be more intelligent about this eventually
83 + return paths.iterator().next();
84 + }
85 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import java.util.ArrayList;
4 +import java.util.HashSet;
5 +import java.util.List;
6 +import java.util.Set;
7 +
8 +import org.junit.Test;
9 +import org.onlab.onos.net.Link;
10 +import org.onlab.onos.net.flow.TrafficSelector;
11 +import org.onlab.onos.net.flow.TrafficTreatment;
12 +import org.onlab.onos.net.flow.criteria.Criterion;
13 +import org.onlab.onos.net.flow.instructions.Instruction;
14 +
15 +import static org.hamcrest.MatcherAssert.assertThat;
16 +import static org.hamcrest.Matchers.is;
17 +
18 +public class TestLinkCollectionIntent {
19 +
20 + private static class MockSelector implements TrafficSelector {
21 + @Override
22 + public Set<Criterion> criteria() {
23 + return new HashSet<Criterion>();
24 + }
25 + }
26 +
27 + private static class MockTreatment implements TrafficTreatment {
28 + @Override
29 + public List<Instruction> instructions() {
30 + return new ArrayList<>();
31 + }
32 + }
33 +
34 + @Test
35 + public void testComparison() {
36 + TrafficSelector selector = new MockSelector();
37 + TrafficTreatment treatment = new MockTreatment();
38 + Set<Link> links = new HashSet<>();
39 + LinkCollectionIntent i1 = new LinkCollectionIntent(new IntentId(12),
40 + selector, treatment, links);
41 + LinkCollectionIntent i2 = new LinkCollectionIntent(new IntentId(12),
42 + selector, treatment, links);
43 +
44 + assertThat(i1.equals(i2), is(true));
45 + }
46 +
47 +}