helenyrwu
Committed by Gerrit Code Review

Implement path protection for point to point intents

Change-Id: I3f3627e7c2a7e3ab017e46655692ab70fdeae413
...@@ -17,6 +17,7 @@ package org.onosproject.cli.net; ...@@ -17,6 +17,7 @@ package org.onosproject.cli.net;
17 17
18 import org.apache.karaf.shell.commands.Argument; 18 import org.apache.karaf.shell.commands.Argument;
19 import org.apache.karaf.shell.commands.Command; 19 import org.apache.karaf.shell.commands.Command;
20 +import org.apache.karaf.shell.commands.Option;
20 import org.onosproject.net.ConnectPoint; 21 import org.onosproject.net.ConnectPoint;
21 import org.onosproject.net.flow.TrafficSelector; 22 import org.onosproject.net.flow.TrafficSelector;
22 import org.onosproject.net.flow.TrafficTreatment; 23 import org.onosproject.net.flow.TrafficTreatment;
...@@ -24,6 +25,7 @@ import org.onosproject.net.intent.Constraint; ...@@ -24,6 +25,7 @@ import org.onosproject.net.intent.Constraint;
24 import org.onosproject.net.intent.Intent; 25 import org.onosproject.net.intent.Intent;
25 import org.onosproject.net.intent.IntentService; 26 import org.onosproject.net.intent.IntentService;
26 import org.onosproject.net.intent.PointToPointIntent; 27 import org.onosproject.net.intent.PointToPointIntent;
28 +import org.onosproject.net.intent.constraint.ProtectionConstraint;
27 29
28 import java.util.List; 30 import java.util.List;
29 31
...@@ -44,6 +46,11 @@ public class AddPointToPointIntentCommand extends ConnectivityIntentCommand { ...@@ -44,6 +46,11 @@ public class AddPointToPointIntentCommand extends ConnectivityIntentCommand {
44 required = true, multiValued = false) 46 required = true, multiValued = false)
45 String egressDeviceString = null; 47 String egressDeviceString = null;
46 48
49 + @Option(name = "-p", aliases = "--protect",
50 + description = "Utilize path protection",
51 + required = false, multiValued = false)
52 + private boolean backup = false;
53 +
47 @Override 54 @Override
48 protected void execute() { 55 protected void execute() {
49 IntentService service = get(IntentService.class); 56 IntentService service = get(IntentService.class);
...@@ -56,6 +63,9 @@ public class AddPointToPointIntentCommand extends ConnectivityIntentCommand { ...@@ -56,6 +63,9 @@ public class AddPointToPointIntentCommand extends ConnectivityIntentCommand {
56 TrafficTreatment treatment = buildTrafficTreatment(); 63 TrafficTreatment treatment = buildTrafficTreatment();
57 64
58 List<Constraint> constraints = buildConstraints(); 65 List<Constraint> constraints = buildConstraints();
66 + if (backup) {
67 + constraints.add(new ProtectionConstraint());
68 + }
59 69
60 Intent intent = PointToPointIntent.builder() 70 Intent intent = PointToPointIntent.builder()
61 .appId(appId()) 71 .appId(appId())
......
...@@ -35,6 +35,7 @@ import static com.google.common.base.Preconditions.checkNotNull; ...@@ -35,6 +35,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
35 public class FlowRuleIntent extends Intent { 35 public class FlowRuleIntent extends Intent {
36 36
37 private final Collection<FlowRule> flowRules; 37 private final Collection<FlowRule> flowRules;
38 + private PathIntent.ProtectionType type;
38 39
39 /** 40 /**
40 * Creates a flow rule intent with the specified flow rules and resources. 41 * Creates a flow rule intent with the specified flow rules and resources.
...@@ -48,7 +49,19 @@ public class FlowRuleIntent extends Intent { ...@@ -48,7 +49,19 @@ public class FlowRuleIntent extends Intent {
48 } 49 }
49 50
50 /** 51 /**
51 - * Creates an flow rule intent with the specified key, flow rules to be set, and 52 + * Creates a flow rule intent with the specified flow rules, resources, and type.
53 + *
54 + * @param appId application id
55 + * @param flowRules flow rules to be set
56 + * @param resources network resource to be set
57 + */
58 + public FlowRuleIntent(ApplicationId appId, List<FlowRule> flowRules, Collection<NetworkResource> resources,
59 + PathIntent.ProtectionType type) {
60 + this(appId, null, flowRules, resources, type);
61 + }
62 +
63 + /**
64 + * Creates a flow rule intent with the specified key, flow rules to be set, and
52 * required network resources. 65 * required network resources.
53 * 66 *
54 * @param appId application id 67 * @param appId application id
...@@ -58,8 +71,32 @@ public class FlowRuleIntent extends Intent { ...@@ -58,8 +71,32 @@ public class FlowRuleIntent extends Intent {
58 */ 71 */
59 public FlowRuleIntent(ApplicationId appId, Key key, Collection<FlowRule> flowRules, 72 public FlowRuleIntent(ApplicationId appId, Key key, Collection<FlowRule> flowRules,
60 Collection<NetworkResource> resources) { 73 Collection<NetworkResource> resources) {
74 + this(appId, key, flowRules, resources, PathIntent.ProtectionType.PRIMARY);
75 + }
76 +
77 + /**
78 + * Creates a flow rule intent with the specified key, flow rules to be set, and
79 + * required network resources.
80 + *
81 + * @param appId application id
82 + * @param key key
83 + * @param flowRules flow rules
84 + * @param resources network resources
85 + */
86 + public FlowRuleIntent(ApplicationId appId, Key key, Collection<FlowRule> flowRules,
87 + Collection<NetworkResource> resources, PathIntent.ProtectionType primary) {
61 super(appId, key, resources, DEFAULT_INTENT_PRIORITY); 88 super(appId, key, resources, DEFAULT_INTENT_PRIORITY);
62 this.flowRules = ImmutableList.copyOf(checkNotNull(flowRules)); 89 this.flowRules = ImmutableList.copyOf(checkNotNull(flowRules));
90 + this.type = primary;
91 + }
92 +
93 + /**
94 + * Creates a flow rule intent with all the same characteristics as the given
95 + * one except for the flow rule type.
96 + */
97 + public FlowRuleIntent(FlowRuleIntent intent, PathIntent.ProtectionType type) {
98 + this(intent.appId(), intent.key(), intent.flowRules(),
99 + intent.resources(), type);
63 } 100 }
64 101
65 /** 102 /**
...@@ -68,6 +105,7 @@ public class FlowRuleIntent extends Intent { ...@@ -68,6 +105,7 @@ public class FlowRuleIntent extends Intent {
68 protected FlowRuleIntent() { 105 protected FlowRuleIntent() {
69 super(); 106 super();
70 this.flowRules = null; 107 this.flowRules = null;
108 + this.type = PathIntent.ProtectionType.PRIMARY;
71 } 109 }
72 110
73 /** 111 /**
...@@ -84,6 +122,10 @@ public class FlowRuleIntent extends Intent { ...@@ -84,6 +122,10 @@ public class FlowRuleIntent extends Intent {
84 return true; 122 return true;
85 } 123 }
86 124
125 + public PathIntent.ProtectionType type() {
126 + return type;
127 + }
128 +
87 @Override 129 @Override
88 public String toString() { 130 public String toString() {
89 return MoreObjects.toStringHelper(this) 131 return MoreObjects.toStringHelper(this)
......
...@@ -31,7 +31,7 @@ public interface IntentCompiler<T extends Intent> { ...@@ -31,7 +31,7 @@ public interface IntentCompiler<T extends Intent> {
31 * Compiles the specified intent into other intents. 31 * Compiles the specified intent into other intents.
32 * 32 *
33 * @param intent intent to be compiled 33 * @param intent intent to be compiled
34 - * @param installable previously compilation result; optional 34 + * @param installable previous compilation result; optional
35 * @return list of resulting intents 35 * @return list of resulting intents
36 * @throws IntentException if issues are encountered while compiling the intent 36 * @throws IntentException if issues are encountered while compiling the intent
37 */ 37 */
......
...@@ -36,10 +36,11 @@ import static com.google.common.base.Preconditions.checkArgument; ...@@ -36,10 +36,11 @@ import static com.google.common.base.Preconditions.checkArgument;
36 public class PathIntent extends ConnectivityIntent { 36 public class PathIntent extends ConnectivityIntent {
37 37
38 private final Path path; 38 private final Path path;
39 + private ProtectionType type;
39 40
40 /** 41 /**
41 * Creates a new point-to-point intent with the supplied ingress/egress 42 * Creates a new point-to-point intent with the supplied ingress/egress
42 - * ports and using the specified explicit path. 43 + * ports and using the specified explicit path. Path is primary by default.
43 * 44 *
44 * @param appId application identifier 45 * @param appId application identifier
45 * @param key intent key 46 * @param key intent key
...@@ -57,10 +58,38 @@ public class PathIntent extends ConnectivityIntent { ...@@ -57,10 +58,38 @@ public class PathIntent extends ConnectivityIntent {
57 Path path, 58 Path path,
58 List<Constraint> constraints, 59 List<Constraint> constraints,
59 int priority) { 60 int priority) {
61 + this(appId, key, selector, treatment, path, constraints, priority,
62 + ProtectionType.PRIMARY);
63 + }
64 +
65 + /**
66 + * Creates a new point-to-point intent with the supplied ingress/egress
67 + * ports and using the specified explicit path, which can be classified
68 + * as PRIMARY or BACKUP.
69 + *
70 + * @param appId application identifier
71 + * @param key intent key
72 + * @param selector traffic selector
73 + * @param treatment treatment
74 + * @param path traversed links
75 + * @param constraints optional list of constraints
76 + * @param priority priority to use for the generated flows
77 + * @param type PRIMARY or BACKUP
78 + * @throws NullPointerException {@code path} is null
79 + */
80 + protected PathIntent(ApplicationId appId,
81 + Key key,
82 + TrafficSelector selector,
83 + TrafficTreatment treatment,
84 + Path path,
85 + List<Constraint> constraints,
86 + int priority,
87 + ProtectionType type) {
60 super(appId, key, resources(path.links()), selector, treatment, constraints, 88 super(appId, key, resources(path.links()), selector, treatment, constraints,
61 priority); 89 priority);
62 PathIntent.validate(path.links()); 90 PathIntent.validate(path.links());
63 this.path = path; 91 this.path = path;
92 + this.type = type;
64 } 93 }
65 94
66 /** 95 /**
...@@ -69,6 +98,7 @@ public class PathIntent extends ConnectivityIntent { ...@@ -69,6 +98,7 @@ public class PathIntent extends ConnectivityIntent {
69 protected PathIntent() { 98 protected PathIntent() {
70 super(); 99 super();
71 this.path = null; 100 this.path = null;
101 + this.type = ProtectionType.PRIMARY;
72 } 102 }
73 103
74 /** 104 /**
...@@ -85,6 +115,7 @@ public class PathIntent extends ConnectivityIntent { ...@@ -85,6 +115,7 @@ public class PathIntent extends ConnectivityIntent {
85 */ 115 */
86 public static class Builder extends ConnectivityIntent.Builder { 116 public static class Builder extends ConnectivityIntent.Builder {
87 Path path; 117 Path path;
118 + ProtectionType type;
88 119
89 protected Builder() { 120 protected Builder() {
90 // Hide default constructor 121 // Hide default constructor
...@@ -131,6 +162,11 @@ public class PathIntent extends ConnectivityIntent { ...@@ -131,6 +162,11 @@ public class PathIntent extends ConnectivityIntent {
131 return this; 162 return this;
132 } 163 }
133 164
165 + public Builder setType(ProtectionType type) {
166 + this.type = type;
167 + return this;
168 + }
169 +
134 /** 170 /**
135 * Builds a path intent from the accumulated parameters. 171 * Builds a path intent from the accumulated parameters.
136 * 172 *
...@@ -145,7 +181,8 @@ public class PathIntent extends ConnectivityIntent { ...@@ -145,7 +181,8 @@ public class PathIntent extends ConnectivityIntent {
145 treatment, 181 treatment,
146 path, 182 path,
147 constraints, 183 constraints,
148 - priority 184 + priority,
185 + type == null ? ProtectionType.PRIMARY : type
149 ); 186 );
150 } 187 }
151 } 188 }
...@@ -183,6 +220,10 @@ public class PathIntent extends ConnectivityIntent { ...@@ -183,6 +220,10 @@ public class PathIntent extends ConnectivityIntent {
183 return path; 220 return path;
184 } 221 }
185 222
223 + public ProtectionType type() {
224 + return type;
225 + }
226 +
186 @Override 227 @Override
187 public String toString() { 228 public String toString() {
188 return MoreObjects.toStringHelper(getClass()) 229 return MoreObjects.toStringHelper(getClass())
...@@ -195,7 +236,25 @@ public class PathIntent extends ConnectivityIntent { ...@@ -195,7 +236,25 @@ public class PathIntent extends ConnectivityIntent {
195 .add("treatment", treatment()) 236 .add("treatment", treatment())
196 .add("constraints", constraints()) 237 .add("constraints", constraints())
197 .add("path", path) 238 .add("path", path)
239 + .add("type", type)
198 .toString(); 240 .toString();
199 } 241 }
200 242
243 + // for path protection purposes
244 + public enum ProtectionType {
245 + /**
246 + * Intent within primary path.
247 + */
248 + PRIMARY,
249 + /**
250 + * Intent within backup path.
251 + */
252 + BACKUP,
253 + /**
254 + * Intent whose flow rule serves as the fast failover
255 + * between primary and backup paths.
256 + */
257 + FAILOVER
258 + }
259 +
201 } 260 }
......
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.net.intent.constraint;
18 +
19 +import com.google.common.annotations.Beta;
20 +import org.onosproject.net.Link;
21 +import org.onosproject.net.Path;
22 +import org.onosproject.net.intent.Constraint;
23 +import org.onosproject.net.intent.Intent;
24 +import org.onosproject.net.intent.PointToPointIntent;
25 +import org.onosproject.net.intent.ResourceContext;
26 +
27 +/**
28 + * Constraint that determines whether to employ path protection.
29 + */
30 +@Beta
31 +public class ProtectionConstraint implements Constraint {
32 + // doesn't use LinkResourceService
33 + @Override
34 + public double cost(Link link, ResourceContext context) {
35 + return 1;
36 + }
37 +
38 + // doesn't use LinkResourceService
39 + @Override
40 + public boolean validate(Path path, ResourceContext context) {
41 + return true;
42 + }
43 +
44 + /**
45 + * Determines whether to utilize path protection for the given intent.
46 + *
47 + * @param intent intent to be inspected
48 + * @return whether the intent has a ProtectionConstraint
49 + */
50 + public static boolean requireProtectedPath(Intent intent) {
51 + if (intent instanceof PointToPointIntent) {
52 + PointToPointIntent pointToPointIntent = (PointToPointIntent) intent;
53 + return pointToPointIntent.constraints().stream()
54 + .anyMatch(p -> p instanceof ProtectionConstraint);
55 + }
56 + return false;
57 + }
58 +}
...@@ -26,7 +26,7 @@ import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutableBa ...@@ -26,7 +26,7 @@ import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutableBa
26 public class PointToPointIntentTest extends ConnectivityIntentTest { 26 public class PointToPointIntentTest extends ConnectivityIntentTest {
27 27
28 /** 28 /**
29 - * Checks that the MultiPointToSinglePointIntent class is immutable. 29 + * Checks that the PointToPointIntent class is immutable.
30 */ 30 */
31 @Test 31 @Test
32 public void checkImmutability() { 32 public void checkImmutability() {
......
...@@ -28,8 +28,11 @@ import org.onosproject.cfg.ComponentConfigService; ...@@ -28,8 +28,11 @@ import org.onosproject.cfg.ComponentConfigService;
28 import org.onosproject.core.CoreService; 28 import org.onosproject.core.CoreService;
29 import org.onosproject.core.IdGenerator; 29 import org.onosproject.core.IdGenerator;
30 import org.onosproject.event.AbstractListenerManager; 30 import org.onosproject.event.AbstractListenerManager;
31 +import org.onosproject.net.DeviceId;
31 import org.onosproject.net.flow.FlowRuleService; 32 import org.onosproject.net.flow.FlowRuleService;
32 import org.onosproject.net.flowobjective.FlowObjectiveService; 33 import org.onosproject.net.flowobjective.FlowObjectiveService;
34 +import org.onosproject.net.group.GroupKey;
35 +import org.onosproject.net.group.GroupService;
33 import org.onosproject.net.intent.Intent; 36 import org.onosproject.net.intent.Intent;
34 import org.onosproject.net.intent.IntentBatchDelegate; 37 import org.onosproject.net.intent.IntentBatchDelegate;
35 import org.onosproject.net.intent.IntentCompiler; 38 import org.onosproject.net.intent.IntentCompiler;
...@@ -42,6 +45,8 @@ import org.onosproject.net.intent.IntentState; ...@@ -42,6 +45,8 @@ import org.onosproject.net.intent.IntentState;
42 import org.onosproject.net.intent.IntentStore; 45 import org.onosproject.net.intent.IntentStore;
43 import org.onosproject.net.intent.IntentStoreDelegate; 46 import org.onosproject.net.intent.IntentStoreDelegate;
44 import org.onosproject.net.intent.Key; 47 import org.onosproject.net.intent.Key;
48 +import org.onosproject.net.intent.PointToPointIntent;
49 +import org.onosproject.net.intent.impl.compiler.PointToPointIntentCompiler;
45 import org.onosproject.net.intent.impl.phase.FinalIntentProcessPhase; 50 import org.onosproject.net.intent.impl.phase.FinalIntentProcessPhase;
46 import org.onosproject.net.intent.impl.phase.IntentProcessPhase; 51 import org.onosproject.net.intent.impl.phase.IntentProcessPhase;
47 import org.osgi.service.component.ComponentContext; 52 import org.osgi.service.component.ComponentContext;
...@@ -123,6 +128,9 @@ public class IntentManager ...@@ -123,6 +128,9 @@ public class IntentManager
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
124 protected ComponentConfigService configService; 129 protected ComponentConfigService configService;
125 130
131 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
132 + protected GroupService groupService;
133 +
126 private ExecutorService batchExecutor; 134 private ExecutorService batchExecutor;
127 private ExecutorService workerExecutor; 135 private ExecutorService workerExecutor;
128 136
...@@ -234,6 +242,15 @@ public class IntentManager ...@@ -234,6 +242,15 @@ public class IntentManager
234 checkNotNull(intent, INTENT_NULL); 242 checkNotNull(intent, INTENT_NULL);
235 IntentData data = new IntentData(intent, IntentState.PURGE_REQ, null); 243 IntentData data = new IntentData(intent, IntentState.PURGE_REQ, null);
236 store.addPending(data); 244 store.addPending(data);
245 +
246 + // remove associated group if there is one
247 + if (intent instanceof PointToPointIntent) {
248 + PointToPointIntent pointIntent = (PointToPointIntent) intent;
249 + DeviceId deviceId = pointIntent.ingressPoint().deviceId();
250 + GroupKey groupKey = PointToPointIntentCompiler.makeGroupKey(intent.id());
251 + groupService.removeGroup(deviceId, groupKey,
252 + intent.appId());
253 + }
237 } 254 }
238 255
239 @Override 256 @Override
......
...@@ -20,6 +20,7 @@ import com.google.common.collect.ImmutableList; ...@@ -20,6 +20,7 @@ import com.google.common.collect.ImmutableList;
20 import org.apache.felix.scr.annotations.Component; 20 import org.apache.felix.scr.annotations.Component;
21 import org.apache.felix.scr.annotations.Reference; 21 import org.apache.felix.scr.annotations.Reference;
22 import org.apache.felix.scr.annotations.ReferenceCardinality; 22 import org.apache.felix.scr.annotations.ReferenceCardinality;
23 +import org.onosproject.net.DisjointPath;
23 import org.onosproject.net.ElementId; 24 import org.onosproject.net.ElementId;
24 import org.onosproject.net.Path; 25 import org.onosproject.net.Path;
25 import org.onosproject.net.intent.ConnectivityIntent; 26 import org.onosproject.net.intent.ConnectivityIntent;
...@@ -108,6 +109,29 @@ public abstract class ConnectivityIntentCompiler<T extends ConnectivityIntent> ...@@ -108,6 +109,29 @@ public abstract class ConnectivityIntentCompiler<T extends ConnectivityIntent>
108 } 109 }
109 110
110 /** 111 /**
112 + * Computes a disjoint path between two ConnectPoints.
113 + *
114 + * @param intent intent on which behalf path is being computed
115 + * @param one start of the path
116 + * @param two end of the path
117 + * @return DisjointPath between the two
118 + * @throws PathNotFoundException if two paths cannot be found
119 + */
120 + protected DisjointPath getDisjointPath(ConnectivityIntent intent,
121 + ElementId one, ElementId two) {
122 + Set<DisjointPath> paths = pathService.getDisjointPaths(one, two, weight(intent.constraints()));
123 + final List<Constraint> constraints = intent.constraints();
124 + ImmutableList<DisjointPath> filtered = FluentIterable.from(paths)
125 + .filter(path -> checkPath(path, constraints))
126 + .toList();
127 + if (filtered.isEmpty()) {
128 + throw new PathNotFoundException(one, two);
129 + }
130 + // TODO: let's be more intelligent about this eventually
131 + return filtered.iterator().next();
132 + }
133 +
134 + /**
111 * Edge-weight capable of evaluating link cost using a set of constraints. 135 * Edge-weight capable of evaluating link cost using a set of constraints.
112 */ 136 */
113 protected class ConstraintBasedLinkWeight implements LinkWeight { 137 protected class ConstraintBasedLinkWeight implements LinkWeight {
......
...@@ -82,7 +82,7 @@ public class PathIntentCompiler ...@@ -82,7 +82,7 @@ public class PathIntentCompiler
82 compile(this, intent, rules, devices); 82 compile(this, intent, rules, devices);
83 83
84 84
85 - return ImmutableList.of(new FlowRuleIntent(appId, null, rules, intent.resources())); 85 + return ImmutableList.of(new FlowRuleIntent(appId, null, rules, intent.resources(), intent.type()));
86 } 86 }
87 87
88 @Override 88 @Override
......
...@@ -18,17 +18,51 @@ package org.onosproject.net.intent.impl.compiler; ...@@ -18,17 +18,51 @@ package org.onosproject.net.intent.impl.compiler;
18 import org.apache.felix.scr.annotations.Activate; 18 import org.apache.felix.scr.annotations.Activate;
19 import org.apache.felix.scr.annotations.Component; 19 import org.apache.felix.scr.annotations.Component;
20 import org.apache.felix.scr.annotations.Deactivate; 20 import org.apache.felix.scr.annotations.Deactivate;
21 +import org.apache.felix.scr.annotations.Reference;
22 +import org.apache.felix.scr.annotations.ReferenceCardinality;
21 import org.onosproject.net.ConnectPoint; 23 import org.onosproject.net.ConnectPoint;
22 import org.onosproject.net.DefaultPath; 24 import org.onosproject.net.DefaultPath;
25 +import org.onosproject.net.DeviceId;
26 +import org.onosproject.net.DisjointPath;
27 +import org.onosproject.net.EdgeLink;
23 import org.onosproject.net.Link; 28 import org.onosproject.net.Link;
24 import org.onosproject.net.Path; 29 import org.onosproject.net.Path;
30 +import org.onosproject.net.Port;
31 +import org.onosproject.net.PortNumber;
32 +import org.onosproject.net.device.DeviceService;
33 +import org.onosproject.net.flow.DefaultFlowRule;
34 +import org.onosproject.net.flow.DefaultTrafficSelector;
35 +import org.onosproject.net.flow.DefaultTrafficTreatment;
36 +import org.onosproject.net.flow.FlowRule;
37 +import org.onosproject.net.flow.TrafficSelector;
38 +import org.onosproject.net.flow.TrafficTreatment;
39 +import org.onosproject.net.flow.instructions.Instruction;
40 +import org.onosproject.net.flow.instructions.Instructions;
41 +import org.onosproject.net.group.DefaultGroupBucket;
42 +import org.onosproject.net.group.DefaultGroupDescription;
43 +import org.onosproject.net.group.DefaultGroupKey;
44 +import org.onosproject.net.group.Group;
45 +import org.onosproject.net.group.GroupBucket;
46 +import org.onosproject.net.group.GroupBuckets;
47 +import org.onosproject.net.group.GroupDescription;
48 +import org.onosproject.net.group.GroupKey;
49 +import org.onosproject.net.group.GroupService;
50 +import org.onosproject.net.intent.FlowRuleIntent;
25 import org.onosproject.net.intent.Intent; 51 import org.onosproject.net.intent.Intent;
52 +import org.onosproject.net.intent.IntentId;
26 import org.onosproject.net.intent.PathIntent; 53 import org.onosproject.net.intent.PathIntent;
27 import org.onosproject.net.intent.PointToPointIntent; 54 import org.onosproject.net.intent.PointToPointIntent;
55 +import org.onosproject.net.intent.constraint.ProtectionConstraint;
56 +import org.onosproject.net.intent.impl.PathNotFoundException;
57 +import org.onosproject.net.link.LinkService;
28 import org.onosproject.net.provider.ProviderId; 58 import org.onosproject.net.provider.ProviderId;
29 59
60 +import java.nio.ByteBuffer;
30 import java.util.ArrayList; 61 import java.util.ArrayList;
62 +import java.util.Collections;
63 +import java.util.Iterator;
31 import java.util.List; 64 import java.util.List;
65 +import java.util.ListIterator;
32 66
33 import static java.util.Arrays.asList; 67 import static java.util.Arrays.asList;
34 import static org.onosproject.net.DefaultEdgeLink.createEdgeLink; 68 import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
...@@ -45,6 +79,18 @@ public class PointToPointIntentCompiler ...@@ -45,6 +79,18 @@ public class PointToPointIntentCompiler
45 new ProviderId("core", "org.onosproject.core", true); 79 new ProviderId("core", "org.onosproject.core", true);
46 // TODO: consider whether the default cost is appropriate or not 80 // TODO: consider whether the default cost is appropriate or not
47 public static final int DEFAULT_COST = 1; 81 public static final int DEFAULT_COST = 1;
82 + protected static final int PRIORITY = Intent.DEFAULT_INTENT_PRIORITY;
83 + protected boolean erasePrimary = false;
84 + protected boolean eraseBackup = false;
85 +
86 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 + protected GroupService groupService;
88 +
89 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 + protected LinkService linkService;
91 +
92 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 + protected DeviceService deviceService;
48 94
49 @Activate 95 @Activate
50 public void activate() { 96 public void activate() {
...@@ -63,10 +109,34 @@ public class PointToPointIntentCompiler ...@@ -63,10 +109,34 @@ public class PointToPointIntentCompiler
63 ConnectPoint egressPoint = intent.egressPoint(); 109 ConnectPoint egressPoint = intent.egressPoint();
64 110
65 if (ingressPoint.deviceId().equals(egressPoint.deviceId())) { 111 if (ingressPoint.deviceId().equals(egressPoint.deviceId())) {
112 + return createZeroHopIntent(ingressPoint, egressPoint, intent);
113 + }
114 +
115 + // proceed with no protected paths
116 + if (!ProtectionConstraint.requireProtectedPath(intent)) {
117 + return createUnprotectedIntent(ingressPoint, egressPoint, intent);
118 + }
119 +
120 + try {
121 + // attempt to compute and implement backup path
122 + return createProtectedIntent(ingressPoint, egressPoint, intent, installable);
123 + } catch (PathNotFoundException e) {
124 + // no disjoint path extant -- maximum one path exists between devices
125 + return createSinglePathIntent(ingressPoint, egressPoint, intent, installable);
126 + }
127 + }
128 +
129 + private List<Intent> createZeroHopIntent(ConnectPoint ingressPoint,
130 + ConnectPoint egressPoint,
131 + PointToPointIntent intent) {
66 List<Link> links = asList(createEdgeLink(ingressPoint, true), createEdgeLink(egressPoint, false)); 132 List<Link> links = asList(createEdgeLink(ingressPoint, true), createEdgeLink(egressPoint, false));
67 - return asList(createPathIntent(new DefaultPath(PID, links, DEFAULT_COST), intent)); 133 + return asList(createPathIntent(new DefaultPath(PID, links, DEFAULT_COST),
134 + intent, PathIntent.ProtectionType.PRIMARY));
68 } 135 }
69 136
137 + private List<Intent> createUnprotectedIntent(ConnectPoint ingressPoint,
138 + ConnectPoint egressPoint,
139 + PointToPointIntent intent) {
70 List<Link> links = new ArrayList<>(); 140 List<Link> links = new ArrayList<>();
71 Path path = getPath(intent, ingressPoint.deviceId(), 141 Path path = getPath(intent, ingressPoint.deviceId(),
72 egressPoint.deviceId()); 142 egressPoint.deviceId());
...@@ -76,7 +146,123 @@ public class PointToPointIntentCompiler ...@@ -76,7 +146,123 @@ public class PointToPointIntentCompiler
76 links.add(createEdgeLink(egressPoint, false)); 146 links.add(createEdgeLink(egressPoint, false));
77 147
78 return asList(createPathIntent(new DefaultPath(PID, links, path.cost(), 148 return asList(createPathIntent(new DefaultPath(PID, links, path.cost(),
79 - path.annotations()), intent)); 149 + path.annotations()), intent,
150 + PathIntent.ProtectionType.PRIMARY));
151 + }
152 +
153 + //FIXME: Compatibility with EncapsulationConstraint
154 + private List<Intent> createProtectedIntent(ConnectPoint ingressPoint,
155 + ConnectPoint egressPoint,
156 + PointToPointIntent intent,
157 + List<Intent> installable) {
158 + DisjointPath path = getDisjointPath(intent, ingressPoint.deviceId(),
159 + egressPoint.deviceId());
160 +
161 + List<Intent> reusableIntents = null;
162 + if (installable != null) {
163 + reusableIntents = filterInvalidSubIntents(installable, intent);
164 + if (reusableIntents.size() == installable.size()) {
165 + // all old paths are still viable
166 + return installable;
167 + }
168 + }
169 +
170 + List<Intent> intentList = new ArrayList<>();
171 +
172 + // primary path intent
173 + List<Link> links = new ArrayList<>();
174 + links.addAll(path.links());
175 + links.add(createEdgeLink(egressPoint, false));
176 +
177 + // backup path intent
178 + List<Link> backupLinks = new ArrayList<>();
179 + backupLinks.addAll(path.backup().links());
180 + backupLinks.add(createEdgeLink(egressPoint, false));
181 +
182 + /*
183 + * One of the old paths is still entirely intact. This old path has
184 + * already been made primary, so we must add a backup path intent
185 + * and modify the failover group treatment accordingly.
186 + */
187 + if (reusableIntents != null && reusableIntents.size() > 1) {
188 + /*
189 + * Ensures that the egress port on source device is different than
190 + * that of existing path so that failover group will be useful
191 + * (would not be useful if both output ports in group bucket were
192 + * the same). Does not necessarily ensure that the new backup path
193 + * is entirely disjoint from the old path.
194 + */
195 + PortNumber primaryPort = getPrimaryPort(intent);
196 + if (primaryPort != null && !links.get(0).src().port().equals(primaryPort)) {
197 + reusableIntents.add(createPathIntent(new DefaultPath(PID, links,
198 + path.cost(), path.annotations()),
199 + intent, PathIntent.ProtectionType.BACKUP));
200 + updateFailoverGroup(intent, links);
201 + return reusableIntents;
202 +
203 + } else {
204 + reusableIntents.add(createPathIntent(new DefaultPath(PID, backupLinks, path.backup().cost(),
205 + path.backup().annotations()), intent, PathIntent.ProtectionType.BACKUP));
206 + updateFailoverGroup(intent, backupLinks);
207 + return reusableIntents;
208 + }
209 + }
210 +
211 + intentList.add(createPathIntent(new DefaultPath(PID, links, path.cost(), path.annotations()),
212 + intent, PathIntent.ProtectionType.PRIMARY));
213 + intentList.add(createPathIntent(new DefaultPath(PID, backupLinks, path.backup().cost(),
214 + path.backup().annotations()), intent, PathIntent.ProtectionType.BACKUP));
215 +
216 + // Create fast failover flow rule intent or, if it already exists,
217 + // add contents appropriately.
218 + if (groupService.getGroup(ingressPoint.deviceId(),
219 + makeGroupKey(intent.id())) == null) {
220 + // manufactured fast failover flow rule intent
221 + createFailoverTreatmentGroup(path.links(), path.backup().links(), intent);
222 +
223 + FlowRuleIntent frIntent = new FlowRuleIntent(intent.appId(),
224 + createFailoverFlowRules(intent),
225 + asList(ingressPoint.deviceId()),
226 + PathIntent.ProtectionType.FAILOVER);
227 + intentList.add(frIntent);
228 + } else {
229 + updateFailoverGroup(intent, links);
230 + updateFailoverGroup(intent, backupLinks);
231 + }
232 +
233 + return intentList;
234 + }
235 +
236 + private List<Intent> createSinglePathIntent(ConnectPoint ingressPoint,
237 + ConnectPoint egressPoint,
238 + PointToPointIntent intent,
239 + List<Intent> installable) {
240 + List<Link> links = new ArrayList<>();
241 + Path onlyPath = getPath(intent, ingressPoint.deviceId(),
242 + egressPoint.deviceId());
243 +
244 + List<Intent> reusableIntents = null;
245 + if (installable != null) {
246 + reusableIntents = filterInvalidSubIntents(installable, intent);
247 + if (reusableIntents.size() == installable.size()) {
248 + // all old paths are still viable
249 + return installable;
250 + }
251 + }
252 +
253 + // If there exists a full path from old installable intents,
254 + // return the intents that comprise it.
255 + if (reusableIntents != null && reusableIntents.size() > 1) {
256 + return reusableIntents;
257 + } else {
258 + links.add(createEdgeLink(ingressPoint, true));
259 + links.addAll(onlyPath.links());
260 + links.add(createEdgeLink(egressPoint, false));
261 +
262 + return asList(createPathIntent(new DefaultPath(PID, links, onlyPath.cost(),
263 + onlyPath.annotations()),
264 + intent, PathIntent.ProtectionType.PRIMARY));
265 + }
80 } 266 }
81 267
82 /** 268 /**
...@@ -85,9 +271,11 @@ public class PointToPointIntentCompiler ...@@ -85,9 +271,11 @@ public class PointToPointIntentCompiler
85 * 271 *
86 * @param path path to create an intent for 272 * @param path path to create an intent for
87 * @param intent original intent 273 * @param intent original intent
274 + * @param type primary or backup
88 */ 275 */
89 private Intent createPathIntent(Path path, 276 private Intent createPathIntent(Path path,
90 - PointToPointIntent intent) { 277 + PointToPointIntent intent,
278 + PathIntent.ProtectionType type) {
91 return PathIntent.builder() 279 return PathIntent.builder()
92 .appId(intent.appId()) 280 .appId(intent.appId())
93 .selector(intent.selector()) 281 .selector(intent.selector())
...@@ -95,7 +283,262 @@ public class PointToPointIntentCompiler ...@@ -95,7 +283,262 @@ public class PointToPointIntentCompiler
95 .path(path) 283 .path(path)
96 .constraints(intent.constraints()) 284 .constraints(intent.constraints())
97 .priority(intent.priority()) 285 .priority(intent.priority())
286 + .setType(type)
98 .build(); 287 .build();
99 } 288 }
100 289
290 + /**
291 + * Gets primary port number through failover group associated
292 + * with this intent.
293 + */
294 + private PortNumber getPrimaryPort(PointToPointIntent intent) {
295 + Group group = groupService.getGroup(intent.ingressPoint().deviceId(),
296 + makeGroupKey(intent.id()));
297 + PortNumber primaryPort = null;
298 + if (group != null) {
299 + List<GroupBucket> buckets = group.buckets().buckets();
300 + Iterator<GroupBucket> iterator = buckets.iterator();
301 + while (primaryPort == null && iterator.hasNext()) {
302 + GroupBucket bucket = iterator.next();
303 + Instruction individualInstruction = bucket.treatment().allInstructions().get(0);
304 + if (individualInstruction instanceof Instructions.OutputInstruction) {
305 + Instructions.OutputInstruction outInstruction =
306 + (Instructions.OutputInstruction) individualInstruction;
307 + PortNumber tempPortNum = outInstruction.port();
308 + Port port = deviceService.getPort(intent.ingressPoint().deviceId(),
309 + tempPortNum);
310 + if (port != null && port.isEnabled()) {
311 + primaryPort = tempPortNum;
312 + }
313 + }
314 + }
315 + }
316 + return primaryPort;
317 + }
318 +
319 + /**
320 + * Creates group key unique to each intent.
321 + */
322 + public static GroupKey makeGroupKey(IntentId intentId) {
323 + ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
324 + buffer.putLong(intentId.fingerprint());
325 + return new DefaultGroupKey(buffer.array());
326 + }
327 +
328 + /**
329 + * Creates a new failover group with the initial ports of the links
330 + * from the primary and backup path.
331 + *
332 + * @param links links from the primary path
333 + * @param backupLinks links from the backup path
334 + * @param intent intent from which this call originates
335 + */
336 + private void createFailoverTreatmentGroup(List<Link> links,
337 + List<Link> backupLinks,
338 + PointToPointIntent intent) {
339 +
340 + List<GroupBucket> buckets = new ArrayList<>();
341 +
342 + TrafficTreatment.Builder tBuilderIn = DefaultTrafficTreatment.builder();
343 + ConnectPoint src = links.get(0).src();
344 + tBuilderIn.setOutput(src.port());
345 +
346 + TrafficTreatment.Builder tBuilderIn2 = DefaultTrafficTreatment.builder();
347 + ConnectPoint src2 = backupLinks.get(0).src();
348 + tBuilderIn2.setOutput(src2.port());
349 +
350 + buckets.add(DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn.build(), src.port(), null));
351 + buckets.add(DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn2.build(), src2.port(), null));
352 +
353 + GroupBuckets groupBuckets = new GroupBuckets(buckets);
354 +
355 + GroupDescription groupDesc = new DefaultGroupDescription(src.deviceId(), Group.Type.FAILOVER,
356 + groupBuckets, makeGroupKey(intent.id()), null, intent.appId());
357 + groupService.addGroup(groupDesc);
358 + }
359 +
360 + /**
361 + * Manufactures flow rule with treatment that is defined by failover
362 + * group and traffic selector determined by ingress port of the intent.
363 + *
364 + * @param intent intent which is being compiled (for appId)
365 + * @return a list of a singular flow rule with fast failover
366 + * outport traffic treatment
367 + */
368 + private List<FlowRule> createFailoverFlowRules(PointToPointIntent intent) {
369 + List<FlowRule> flowRules = new ArrayList<>();
370 +
371 + ConnectPoint ingress = intent.ingressPoint();
372 + DeviceId deviceId = ingress.deviceId();
373 +
374 + // flow rule with failover traffic treatment
375 + TrafficSelector trafficSelector = DefaultTrafficSelector.builder(intent.selector())
376 + .matchInPort(ingress.port()).build();
377 +
378 + FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
379 + flowRules.add(flowRuleBuilder.withSelector(trafficSelector)
380 + .withTreatment(buildFailoverTreatment(deviceId, makeGroupKey(intent.id())))
381 + .fromApp(intent.appId())
382 + .makePermanent()
383 + .forDevice(deviceId)
384 + .withPriority(PRIORITY)
385 + .build());
386 +
387 + return flowRules;
388 + }
389 +
390 + private TrafficTreatment buildFailoverTreatment(DeviceId srcDevice,
391 + GroupKey groupKey) {
392 + Group group = groupService.getGroup(srcDevice, groupKey);
393 + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
394 + TrafficTreatment trafficTreatment = tBuilder.group(group.id()).build();
395 + return trafficTreatment;
396 + }
397 +
398 + /**
399 + * Deletes intents from the given list if the ports or links the intent
400 + * relies on are no longer viable. The failover flow rule intent is never
401 + * deleted -- only its contents are updated.
402 + *
403 + * @param oldInstallables list of intents to examine
404 + * @return list of reusable installable intents
405 + */
406 + private List<Intent> filterInvalidSubIntents(List<Intent> oldInstallables,
407 + PointToPointIntent pointIntent) {
408 + List<Intent> intentList = new ArrayList<>();
409 + intentList.addAll(oldInstallables);
410 + erasePrimary = false;
411 + eraseBackup = false;
412 + if (intentList != null) {
413 + Iterator<Intent> iterator = intentList.iterator();
414 + while (iterator.hasNext() && !(erasePrimary && eraseBackup)) {
415 + Intent intent = iterator.next();
416 + intent.resources().forEach(resource -> {
417 + if (resource instanceof Link) {
418 + Link link = (Link) resource;
419 + if (link.state() == Link.State.INACTIVE) {
420 + setPathsToRemove(intent);
421 + } else if (link instanceof EdgeLink) {
422 + ConnectPoint connectPoint = (link.src().elementId() instanceof DeviceId)
423 + ? link.src() : link.dst();
424 + Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
425 + if (port == null || !port.isEnabled()) {
426 + setPathsToRemove(intent);
427 + }
428 + } else {
429 + Port port1 = deviceService.getPort(link.src().deviceId(), link.src().port());
430 + Port port2 = deviceService.getPort(link.dst().deviceId(), link.dst().port());
431 + if (port1 == null || !port1.isEnabled() || port2 == null || !port2.isEnabled()) {
432 + setPathsToRemove(intent);
433 + }
434 + }
435 + }
436 + });
437 + }
438 + removeAndUpdateIntents(intentList, pointIntent);
439 + }
440 + return intentList;
441 + }
442 +
443 + /**
444 + * Sets instance variables erasePrimary and eraseBackup. If erasePrimary,
445 + * the primary path is no longer viable and related intents will be deleted.
446 + * If eraseBackup, the backup path is no longer viable and related intents
447 + * will be deleted.
448 + *
449 + * @param intent intent whose resources are found to be disabled/inactive:
450 + * if intent is part of primary path, primary path set for removal;
451 + * if intent is part of backup path, backup path set for removal;
452 + * if bad intent is of type failover, the ingress point is down,
453 + * and both paths are rendered inactive.
454 + * @return true if both primary and backup paths are to be removed
455 + */
456 + private boolean setPathsToRemove(Intent intent) {
457 + if (intent instanceof FlowRuleIntent) {
458 + FlowRuleIntent frIntent = (FlowRuleIntent) intent;
459 + PathIntent.ProtectionType type = frIntent.type();
460 + if (type == PathIntent.ProtectionType.PRIMARY || type == PathIntent.ProtectionType.FAILOVER) {
461 + erasePrimary = true;
462 + }
463 + if (type == PathIntent.ProtectionType.BACKUP || type == PathIntent.ProtectionType.FAILOVER) {
464 + eraseBackup = true;
465 + }
466 + }
467 + return erasePrimary && eraseBackup;
468 + }
469 +
470 + /**
471 + * Removes intents from installables list, depending on the values
472 + * of instance variables erasePrimary and eraseBackup. Flow rule intents
473 + * that contain the manufactured fast failover flow rules are never deleted.
474 + * The contents are simply modified as necessary. If cleanUpIntents size
475 + * is greater than 1 (failover intent), then one whole path from previous
476 + * installables must be still viable.
477 + *
478 + * @param cleanUpIntents list of installable intents
479 + */
480 + private void removeAndUpdateIntents(List<Intent> cleanUpIntents,
481 + PointToPointIntent pointIntent) {
482 + ListIterator<Intent> iterator = cleanUpIntents.listIterator();
483 + while (iterator.hasNext()) {
484 + Intent cIntent = iterator.next();
485 + if (cIntent instanceof FlowRuleIntent) {
486 + FlowRuleIntent fIntent = (FlowRuleIntent) cIntent;
487 + if (fIntent.type() == PathIntent.ProtectionType.PRIMARY && erasePrimary) {
488 + // remove primary path's flow rule intents
489 + iterator.remove();
490 + } else if (fIntent.type() == PathIntent.ProtectionType.BACKUP && eraseBackup) {
491 + //remove backup path's flow rule intents
492 + iterator.remove();
493 + } else if (fIntent.type() == PathIntent.ProtectionType.BACKUP && erasePrimary) {
494 + // promote backup path's flow rule intents to primary
495 + iterator.set(new FlowRuleIntent(fIntent, PathIntent.ProtectionType.PRIMARY));
496 + }
497 + }
498 + }
499 + // remove buckets whose watchports are disabled if the failover group exists
500 + Group group = groupService.getGroup(pointIntent.ingressPoint().deviceId(),
501 + makeGroupKey(pointIntent.id()));
502 + if (group != null) {
503 + updateFailoverGroup(pointIntent);
504 + }
505 + }
506 +
507 + // Removes buckets whose treatments rely on disabled ports from the
508 + // failover group.
509 + private void updateFailoverGroup(PointToPointIntent pointIntent) {
510 + DeviceId deviceId = pointIntent.ingressPoint().deviceId();
511 + GroupKey groupKey = makeGroupKey(pointIntent.id());
512 + Group group = groupService.getGroup(deviceId, groupKey);
513 + Iterator<GroupBucket> groupIterator = group.buckets().buckets().iterator();
514 + while (groupIterator.hasNext()) {
515 + GroupBucket bucket = groupIterator.next();
516 + Instruction individualInstruction = bucket.treatment().allInstructions().get(0);
517 + if (individualInstruction instanceof Instructions.OutputInstruction) {
518 + Instructions.OutputInstruction outInstruction =
519 + (Instructions.OutputInstruction) individualInstruction;
520 + Port port = deviceService.getPort(deviceId, outInstruction.port());
521 + if (port == null || !port.isEnabled()) {
522 + GroupBuckets removeBuckets = new GroupBuckets(Collections.singletonList(bucket));
523 + groupService.removeBucketsFromGroup(deviceId, groupKey,
524 + removeBuckets, groupKey,
525 + pointIntent.appId());
526 + }
527 + }
528 + }
529 + }
530 +
531 + // Adds failover group bucket with treatment outport determined by the
532 + // ingress point of the links.
533 + private void updateFailoverGroup(PointToPointIntent intent, List<Link> links) {
534 + GroupKey groupKey = makeGroupKey(intent.id());
535 +
536 + TrafficTreatment.Builder tBuilderIn = DefaultTrafficTreatment.builder();
537 + ConnectPoint src = links.get(0).src();
538 + tBuilderIn.setOutput(src.port());
539 + GroupBucket bucket = DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn.build(), src.port(), null);
540 + GroupBuckets addBuckets = new GroupBuckets(Collections.singletonList(bucket));
541 +
542 + groupService.addBucketsToGroup(src.deviceId(), groupKey, addBuckets, groupKey, intent.appId());
543 + }
101 } 544 }
......