blob: 94fe6edb667bbda199fa506845b7cdb85c2c201a [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07003 *
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 */
Sho SHIMIZU6c28f832015-02-20 16:12:19 -080016package org.onosproject.net.intent.impl.compiler;
Ray Milkeya058c732014-10-08 13:52:34 -070017
Pier Ventreffe88d62016-10-13 14:34:40 -070018import com.google.common.collect.ImmutableSet;
Ray Milkeya058c732014-10-08 13:52:34 -070019import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
helenyrwu2a674902016-07-20 09:48:04 -070022import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
Brian O'Connorabafb502014-12-02 22:26:20 -080024import org.onosproject.net.ConnectPoint;
25import org.onosproject.net.DefaultPath;
helenyrwu2a674902016-07-20 09:48:04 -070026import org.onosproject.net.DeviceId;
27import org.onosproject.net.DisjointPath;
28import org.onosproject.net.EdgeLink;
Brian O'Connorabafb502014-12-02 22:26:20 -080029import org.onosproject.net.Link;
30import org.onosproject.net.Path;
helenyrwu2a674902016-07-20 09:48:04 -070031import org.onosproject.net.Port;
32import org.onosproject.net.PortNumber;
33import org.onosproject.net.device.DeviceService;
34import org.onosproject.net.flow.DefaultFlowRule;
35import org.onosproject.net.flow.DefaultTrafficSelector;
36import org.onosproject.net.flow.DefaultTrafficTreatment;
37import org.onosproject.net.flow.FlowRule;
38import org.onosproject.net.flow.TrafficSelector;
39import org.onosproject.net.flow.TrafficTreatment;
40import org.onosproject.net.flow.instructions.Instruction;
41import org.onosproject.net.flow.instructions.Instructions;
42import org.onosproject.net.group.DefaultGroupBucket;
43import org.onosproject.net.group.DefaultGroupDescription;
44import org.onosproject.net.group.DefaultGroupKey;
45import org.onosproject.net.group.Group;
46import org.onosproject.net.group.GroupBucket;
47import org.onosproject.net.group.GroupBuckets;
48import org.onosproject.net.group.GroupDescription;
49import org.onosproject.net.group.GroupKey;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070050import org.onosproject.net.group.GroupListener;
helenyrwu2a674902016-07-20 09:48:04 -070051import org.onosproject.net.group.GroupService;
52import org.onosproject.net.intent.FlowRuleIntent;
Brian O'Connorabafb502014-12-02 22:26:20 -080053import org.onosproject.net.intent.Intent;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070054import org.onosproject.net.intent.IntentCompilationException;
helenyrwu2a674902016-07-20 09:48:04 -070055import org.onosproject.net.intent.IntentId;
Pier Ventreffe88d62016-10-13 14:34:40 -070056import org.onosproject.net.intent.LinkCollectionIntent;
Brian O'Connorabafb502014-12-02 22:26:20 -080057import org.onosproject.net.intent.PathIntent;
58import org.onosproject.net.intent.PointToPointIntent;
helenyrwu2a674902016-07-20 09:48:04 -070059import org.onosproject.net.intent.constraint.ProtectionConstraint;
60import org.onosproject.net.intent.impl.PathNotFoundException;
61import org.onosproject.net.link.LinkService;
Brian O'Connorabafb502014-12-02 22:26:20 -080062import org.onosproject.net.provider.ProviderId;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070063import org.slf4j.Logger;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080064
helenyrwu2a674902016-07-20 09:48:04 -070065import java.nio.ByteBuffer;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080066import java.util.ArrayList;
helenyrwu2a674902016-07-20 09:48:04 -070067import java.util.Collections;
68import java.util.Iterator;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080069import java.util.List;
helenyrwu2a674902016-07-20 09:48:04 -070070import java.util.ListIterator;
Pier Ventreffe88d62016-10-13 14:34:40 -070071import java.util.Set;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070072import java.util.concurrent.CompletableFuture;
73import java.util.concurrent.ExecutionException;
74import java.util.concurrent.TimeUnit;
75import java.util.concurrent.TimeoutException;
Ray Milkeya058c732014-10-08 13:52:34 -070076
Thomas Vachuska425a2d72014-10-29 11:28:28 -070077import static java.util.Arrays.asList;
Brian O'Connorabafb502014-12-02 22:26:20 -080078import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070079import static org.slf4j.LoggerFactory.getLogger;
Thomas Vachuska425a2d72014-10-29 11:28:28 -070080
Ray Milkeya058c732014-10-08 13:52:34 -070081/**
Brian O'Connorabafb502014-12-02 22:26:20 -080082 * An intent compiler for {@link org.onosproject.net.intent.PointToPointIntent}.
Ray Milkeya058c732014-10-08 13:52:34 -070083 */
84@Component(immediate = true)
85public class PointToPointIntentCompiler
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080086 extends ConnectivityIntentCompiler<PointToPointIntent> {
Ray Milkeya058c732014-10-08 13:52:34 -070087
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080088 // TODO: use off-the-shell core provider ID
89 private static final ProviderId PID =
Brian O'Connorabafb502014-12-02 22:26:20 -080090 new ProviderId("core", "org.onosproject.core", true);
Sho SHIMIZU3908fde2014-11-19 16:30:22 -080091 // TODO: consider whether the default cost is appropriate or not
92 public static final int DEFAULT_COST = 1;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070093
helenyrwu2a674902016-07-20 09:48:04 -070094 protected static final int PRIORITY = Intent.DEFAULT_INTENT_PRIORITY;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070095
96 private static final int GROUP_TIMEOUT = 5;
97
98 private final Logger log = getLogger(getClass());
99
helenyrwu2a674902016-07-20 09:48:04 -0700100 protected boolean erasePrimary = false;
101 protected boolean eraseBackup = false;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 protected GroupService groupService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected LinkService linkService;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected DeviceService deviceService;
weibit50eb95b2014-10-25 21:47:54 -0700111
Ray Milkeya058c732014-10-08 13:52:34 -0700112 @Activate
113 public void activate() {
Ray Milkeya058c732014-10-08 13:52:34 -0700114 intentManager.registerCompiler(PointToPointIntent.class, this);
115 }
116
117 @Deactivate
118 public void deactivate() {
119 intentManager.unregisterCompiler(PointToPointIntent.class);
120 }
121
122 @Override
Sho SHIMIZUec07ffd2016-02-22 20:45:21 -0800123 public List<Intent> compile(PointToPointIntent intent, List<Intent> installable) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700124 log.trace("compiling {} {}", intent, installable);
Sho SHIMIZUde8e6b52014-11-13 11:23:17 -0800125 ConnectPoint ingressPoint = intent.ingressPoint();
126 ConnectPoint egressPoint = intent.egressPoint();
127
128 if (ingressPoint.deviceId().equals(egressPoint.deviceId())) {
Pier Ventreffe88d62016-10-13 14:34:40 -0700129 return createZeroHopLinkCollectionIntent(intent);
Sho SHIMIZUde8e6b52014-11-13 11:23:17 -0800130 }
Ray Milkeya058c732014-10-08 13:52:34 -0700131
helenyrwu2a674902016-07-20 09:48:04 -0700132 // proceed with no protected paths
133 if (!ProtectionConstraint.requireProtectedPath(intent)) {
Pier Ventreffe88d62016-10-13 14:34:40 -0700134 return createUnprotectedLinkCollectionIntent(intent);
helenyrwu2a674902016-07-20 09:48:04 -0700135 }
136
137 try {
138 // attempt to compute and implement backup path
139 return createProtectedIntent(ingressPoint, egressPoint, intent, installable);
140 } catch (PathNotFoundException e) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700141 log.warn("Could not find disjoint Path for {}", intent);
helenyrwu2a674902016-07-20 09:48:04 -0700142 // no disjoint path extant -- maximum one path exists between devices
143 return createSinglePathIntent(ingressPoint, egressPoint, intent, installable);
144 }
145 }
146
147 private List<Intent> createZeroHopIntent(ConnectPoint ingressPoint,
148 ConnectPoint egressPoint,
149 PointToPointIntent intent) {
150 List<Link> links = asList(createEdgeLink(ingressPoint, true), createEdgeLink(egressPoint, false));
151 return asList(createPathIntent(new DefaultPath(PID, links, DEFAULT_COST),
152 intent, PathIntent.ProtectionType.PRIMARY));
153 }
154
Pier Ventreffe88d62016-10-13 14:34:40 -0700155 private List<Intent> createZeroHopLinkCollectionIntent(PointToPointIntent intent) {
156 return asList(createLinkCollectionIntent(ImmutableSet.of(), DEFAULT_COST,
157 intent));
158 }
159
helenyrwu2a674902016-07-20 09:48:04 -0700160 private List<Intent> createUnprotectedIntent(ConnectPoint ingressPoint,
161 ConnectPoint egressPoint,
162 PointToPointIntent intent) {
Ray Milkeya058c732014-10-08 13:52:34 -0700163 List<Link> links = new ArrayList<>();
Luca Preted26ea652017-01-03 15:59:30 -0800164 Path path = getPathOrException(intent, ingressPoint.deviceId(),
165 egressPoint.deviceId());
Sho SHIMIZUde8e6b52014-11-13 11:23:17 -0800166
Sho SHIMIZU3908fde2014-11-19 16:30:22 -0800167 links.add(createEdgeLink(ingressPoint, true));
Ray Milkeya058c732014-10-08 13:52:34 -0700168 links.addAll(path.links());
Sho SHIMIZU3908fde2014-11-19 16:30:22 -0800169 links.add(createEdgeLink(egressPoint, false));
Ray Milkeya058c732014-10-08 13:52:34 -0700170
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800171 return asList(createPathIntent(new DefaultPath(PID, links, path.cost(),
helenyrwu2a674902016-07-20 09:48:04 -0700172 path.annotations()), intent,
173 PathIntent.ProtectionType.PRIMARY));
174 }
175
Pier Ventreffe88d62016-10-13 14:34:40 -0700176 private List<Intent> createUnprotectedLinkCollectionIntent(PointToPointIntent intent) {
Luca Preted26ea652017-01-03 15:59:30 -0800177 Path path = getPathOrException(intent, intent.filteredIngressPoint().connectPoint().deviceId(),
178 intent.filteredEgressPoint().connectPoint().deviceId());
Pier Ventreffe88d62016-10-13 14:34:40 -0700179
180 return asList(createLinkCollectionIntent(ImmutableSet.copyOf(path.links()),
181 path.cost(),
182 intent));
183 }
184
helenyrwu2a674902016-07-20 09:48:04 -0700185 //FIXME: Compatibility with EncapsulationConstraint
186 private List<Intent> createProtectedIntent(ConnectPoint ingressPoint,
187 ConnectPoint egressPoint,
188 PointToPointIntent intent,
189 List<Intent> installable) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700190 log.trace("createProtectedIntent");
helenyrwu2a674902016-07-20 09:48:04 -0700191 DisjointPath path = getDisjointPath(intent, ingressPoint.deviceId(),
192 egressPoint.deviceId());
193
194 List<Intent> reusableIntents = null;
195 if (installable != null) {
196 reusableIntents = filterInvalidSubIntents(installable, intent);
197 if (reusableIntents.size() == installable.size()) {
198 // all old paths are still viable
199 return installable;
200 }
201 }
202
203 List<Intent> intentList = new ArrayList<>();
204
205 // primary path intent
206 List<Link> links = new ArrayList<>();
207 links.addAll(path.links());
208 links.add(createEdgeLink(egressPoint, false));
209
210 // backup path intent
211 List<Link> backupLinks = new ArrayList<>();
212 backupLinks.addAll(path.backup().links());
213 backupLinks.add(createEdgeLink(egressPoint, false));
214
215 /*
216 * One of the old paths is still entirely intact. This old path has
217 * already been made primary, so we must add a backup path intent
218 * and modify the failover group treatment accordingly.
219 */
220 if (reusableIntents != null && reusableIntents.size() > 1) {
221 /*
222 * Ensures that the egress port on source device is different than
223 * that of existing path so that failover group will be useful
224 * (would not be useful if both output ports in group bucket were
225 * the same). Does not necessarily ensure that the new backup path
226 * is entirely disjoint from the old path.
227 */
228 PortNumber primaryPort = getPrimaryPort(intent);
229 if (primaryPort != null && !links.get(0).src().port().equals(primaryPort)) {
230 reusableIntents.add(createPathIntent(new DefaultPath(PID, links,
231 path.cost(), path.annotations()),
232 intent, PathIntent.ProtectionType.BACKUP));
233 updateFailoverGroup(intent, links);
234 return reusableIntents;
235
236 } else {
237 reusableIntents.add(createPathIntent(new DefaultPath(PID, backupLinks, path.backup().cost(),
238 path.backup().annotations()), intent, PathIntent.ProtectionType.BACKUP));
239 updateFailoverGroup(intent, backupLinks);
240 return reusableIntents;
241 }
242 }
243
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700244 intentList.add(createPathIntent(new DefaultPath(PID, links, path.cost(),
245 path.annotations()),
helenyrwu2a674902016-07-20 09:48:04 -0700246 intent, PathIntent.ProtectionType.PRIMARY));
247 intentList.add(createPathIntent(new DefaultPath(PID, backupLinks, path.backup().cost(),
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700248 path.backup().annotations()),
249 intent, PathIntent.ProtectionType.BACKUP));
helenyrwu2a674902016-07-20 09:48:04 -0700250
251 // Create fast failover flow rule intent or, if it already exists,
252 // add contents appropriately.
253 if (groupService.getGroup(ingressPoint.deviceId(),
254 makeGroupKey(intent.id())) == null) {
255 // manufactured fast failover flow rule intent
256 createFailoverTreatmentGroup(path.links(), path.backup().links(), intent);
257
258 FlowRuleIntent frIntent = new FlowRuleIntent(intent.appId(),
Yuta HIGUCHI652f27f2016-10-31 16:54:30 -0700259 intent.key(),
helenyrwu2a674902016-07-20 09:48:04 -0700260 createFailoverFlowRules(intent),
261 asList(ingressPoint.deviceId()),
Luca Prete670ac5d2017-02-03 15:55:43 -0800262 PathIntent.ProtectionType.FAILOVER,
263 intent.resourceGroup());
helenyrwu2a674902016-07-20 09:48:04 -0700264 intentList.add(frIntent);
265 } else {
266 updateFailoverGroup(intent, links);
267 updateFailoverGroup(intent, backupLinks);
268 }
269
270 return intentList;
271 }
272
273 private List<Intent> createSinglePathIntent(ConnectPoint ingressPoint,
274 ConnectPoint egressPoint,
275 PointToPointIntent intent,
276 List<Intent> installable) {
277 List<Link> links = new ArrayList<>();
Luca Preted26ea652017-01-03 15:59:30 -0800278 Path onlyPath = getPathOrException(intent, ingressPoint.deviceId(),
279 egressPoint.deviceId());
helenyrwu2a674902016-07-20 09:48:04 -0700280
281 List<Intent> reusableIntents = null;
282 if (installable != null) {
283 reusableIntents = filterInvalidSubIntents(installable, intent);
284 if (reusableIntents.size() == installable.size()) {
285 // all old paths are still viable
286 return installable;
287 }
288 }
289
290 // If there exists a full path from old installable intents,
291 // return the intents that comprise it.
292 if (reusableIntents != null && reusableIntents.size() > 1) {
293 return reusableIntents;
294 } else {
295 links.add(createEdgeLink(ingressPoint, true));
296 links.addAll(onlyPath.links());
297 links.add(createEdgeLink(egressPoint, false));
helenyrwu2a674902016-07-20 09:48:04 -0700298 return asList(createPathIntent(new DefaultPath(PID, links, onlyPath.cost(),
299 onlyPath.annotations()),
300 intent, PathIntent.ProtectionType.PRIMARY));
301 }
Ray Milkeya058c732014-10-08 13:52:34 -0700302 }
303
304 /**
305 * Creates a path intent from the specified path and original
306 * connectivity intent.
307 *
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700308 * @param path path to create an intent for
Ray Milkeya058c732014-10-08 13:52:34 -0700309 * @param intent original intent
helenyrwu2a674902016-07-20 09:48:04 -0700310 * @param type primary or backup
Ray Milkeya058c732014-10-08 13:52:34 -0700311 */
312 private Intent createPathIntent(Path path,
helenyrwu2a674902016-07-20 09:48:04 -0700313 PointToPointIntent intent,
314 PathIntent.ProtectionType type) {
Ray Milkeyebc5d222015-03-18 15:45:36 -0700315 return PathIntent.builder()
316 .appId(intent.appId())
Yuta HIGUCHI652f27f2016-10-31 16:54:30 -0700317 .key(intent.key())
Ray Milkeyebc5d222015-03-18 15:45:36 -0700318 .selector(intent.selector())
319 .treatment(intent.treatment())
320 .path(path)
321 .constraints(intent.constraints())
322 .priority(intent.priority())
helenyrwu2a674902016-07-20 09:48:04 -0700323 .setType(type)
Luca Prete670ac5d2017-02-03 15:55:43 -0800324 .resourceGroup(intent.resourceGroup())
Ray Milkeyebc5d222015-03-18 15:45:36 -0700325 .build();
Ray Milkeya058c732014-10-08 13:52:34 -0700326 }
327
Pier Ventreffe88d62016-10-13 14:34:40 -0700328
329 /**
330 * Creates a link collection intent from the specified path and original
331 * point to point intent.
332 *
333 * @param links the links of the packets
334 * @param cost the cost associated to the links
335 * @param intent the point to point intent we are compiling
336 * @return the link collection intent
337 */
338 private Intent createLinkCollectionIntent(Set<Link> links,
339 double cost,
340 PointToPointIntent intent) {
341
342 return LinkCollectionIntent.builder()
343 .key(intent.key())
344 .appId(intent.appId())
345 .selector(intent.selector())
346 .treatment(intent.treatment())
347 .links(ImmutableSet.copyOf(links))
348 .filteredIngressPoints(ImmutableSet.of(
349 intent.filteredIngressPoint()
350 ))
351 .filteredEgressPoints(ImmutableSet.of(
352 intent.filteredEgressPoint()
353 ))
354 .applyTreatmentOnEgress(true)
355 .constraints(intent.constraints())
356 .priority(intent.priority())
357 .cost(cost)
Luca Prete670ac5d2017-02-03 15:55:43 -0800358 .resourceGroup(intent.resourceGroup())
Pier Ventreffe88d62016-10-13 14:34:40 -0700359 .build();
360 }
361
helenyrwu2a674902016-07-20 09:48:04 -0700362 /**
363 * Gets primary port number through failover group associated
364 * with this intent.
365 */
366 private PortNumber getPrimaryPort(PointToPointIntent intent) {
Pier Ventreffe88d62016-10-13 14:34:40 -0700367 Group group = groupService.getGroup(intent.filteredIngressPoint().connectPoint().deviceId(),
helenyrwu2a674902016-07-20 09:48:04 -0700368 makeGroupKey(intent.id()));
369 PortNumber primaryPort = null;
370 if (group != null) {
371 List<GroupBucket> buckets = group.buckets().buckets();
372 Iterator<GroupBucket> iterator = buckets.iterator();
373 while (primaryPort == null && iterator.hasNext()) {
374 GroupBucket bucket = iterator.next();
375 Instruction individualInstruction = bucket.treatment().allInstructions().get(0);
376 if (individualInstruction instanceof Instructions.OutputInstruction) {
377 Instructions.OutputInstruction outInstruction =
378 (Instructions.OutputInstruction) individualInstruction;
379 PortNumber tempPortNum = outInstruction.port();
Pier Ventreffe88d62016-10-13 14:34:40 -0700380 Port port = deviceService.getPort(intent.filteredIngressPoint().connectPoint().deviceId(),
helenyrwu2a674902016-07-20 09:48:04 -0700381 tempPortNum);
382 if (port != null && port.isEnabled()) {
383 primaryPort = tempPortNum;
384 }
385 }
386 }
387 }
388 return primaryPort;
389 }
390
391 /**
392 * Creates group key unique to each intent.
Ray Milkeyef794342016-11-09 16:20:29 -0800393 *
394 * @param intentId identifier of intent to get a key for
395 * @return unique group key for the intent identifier
helenyrwu2a674902016-07-20 09:48:04 -0700396 */
397 public static GroupKey makeGroupKey(IntentId intentId) {
398 ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
399 buffer.putLong(intentId.fingerprint());
400 return new DefaultGroupKey(buffer.array());
401 }
402
403 /**
404 * Creates a new failover group with the initial ports of the links
405 * from the primary and backup path.
406 *
407 * @param links links from the primary path
408 * @param backupLinks links from the backup path
409 * @param intent intent from which this call originates
410 */
411 private void createFailoverTreatmentGroup(List<Link> links,
412 List<Link> backupLinks,
413 PointToPointIntent intent) {
414
415 List<GroupBucket> buckets = new ArrayList<>();
416
417 TrafficTreatment.Builder tBuilderIn = DefaultTrafficTreatment.builder();
418 ConnectPoint src = links.get(0).src();
419 tBuilderIn.setOutput(src.port());
420
421 TrafficTreatment.Builder tBuilderIn2 = DefaultTrafficTreatment.builder();
422 ConnectPoint src2 = backupLinks.get(0).src();
423 tBuilderIn2.setOutput(src2.port());
424
425 buckets.add(DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn.build(), src.port(), null));
426 buckets.add(DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn2.build(), src2.port(), null));
427
428 GroupBuckets groupBuckets = new GroupBuckets(buckets);
429
430 GroupDescription groupDesc = new DefaultGroupDescription(src.deviceId(), Group.Type.FAILOVER,
431 groupBuckets, makeGroupKey(intent.id()), null, intent.appId());
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700432 log.trace("adding failover group {}", groupDesc);
helenyrwu2a674902016-07-20 09:48:04 -0700433 groupService.addGroup(groupDesc);
434 }
435
436 /**
437 * Manufactures flow rule with treatment that is defined by failover
438 * group and traffic selector determined by ingress port of the intent.
439 *
440 * @param intent intent which is being compiled (for appId)
441 * @return a list of a singular flow rule with fast failover
442 * outport traffic treatment
443 */
444 private List<FlowRule> createFailoverFlowRules(PointToPointIntent intent) {
445 List<FlowRule> flowRules = new ArrayList<>();
446
447 ConnectPoint ingress = intent.ingressPoint();
448 DeviceId deviceId = ingress.deviceId();
449
450 // flow rule with failover traffic treatment
451 TrafficSelector trafficSelector = DefaultTrafficSelector.builder(intent.selector())
452 .matchInPort(ingress.port()).build();
453
454 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
455 flowRules.add(flowRuleBuilder.withSelector(trafficSelector)
456 .withTreatment(buildFailoverTreatment(deviceId, makeGroupKey(intent.id())))
457 .fromApp(intent.appId())
458 .makePermanent()
459 .forDevice(deviceId)
460 .withPriority(PRIORITY)
461 .build());
462
463 return flowRules;
464 }
465
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700466
467 /**
468 * Waits for specified group to appear maximum of {@value #GROUP_TIMEOUT} seconds.
469 *
470 * @param deviceId {@link DeviceId}
471 * @param groupKey {@link GroupKey} to wait for.
472 * @return {@link Group}
473 * @throws IntentCompilationException on any error.
474 */
475 private Group waitForGroup(DeviceId deviceId, GroupKey groupKey) {
476 return waitForGroup(deviceId, groupKey, GROUP_TIMEOUT, TimeUnit.SECONDS);
477 }
478
479 /**
480 * Waits for specified group to appear until timeout.
481 *
482 * @param deviceId {@link DeviceId}
483 * @param groupKey {@link GroupKey} to wait for.
484 * @param timeout timeout
485 * @param unit unit of timeout
486 * @return {@link Group}
487 * @throws IntentCompilationException on any error.
488 */
489 private Group waitForGroup(DeviceId deviceId, GroupKey groupKey, long timeout, TimeUnit unit) {
490 Group group = groupService.getGroup(deviceId, groupKey);
491 if (group != null) {
492 return group;
493 }
494
495 final CompletableFuture<Group> future = new CompletableFuture<>();
496 final GroupListener listener = event -> {
497 if (event.subject().deviceId() == deviceId &&
498 event.subject().appCookie().equals(groupKey)) {
499 future.complete(event.subject());
500 return;
501 }
502 };
503
504 groupService.addListener(listener);
505 try {
506 group = groupService.getGroup(deviceId, groupKey);
507 if (group != null) {
508 return group;
509 }
510 return future.get(timeout, unit);
511 } catch (InterruptedException e) {
512 log.debug("Interrupted", e);
513 Thread.currentThread().interrupt();
514 throw new IntentCompilationException("Interrupted", e);
515 } catch (ExecutionException e) {
516 log.debug("ExecutionException", e);
517 throw new IntentCompilationException("ExecutionException caught", e);
518 } catch (TimeoutException e) {
519 // one last try
520 group = groupService.getGroup(deviceId, groupKey);
521 if (group != null) {
522 return group;
523 } else {
524 log.debug("Timeout", e);
525 throw new IntentCompilationException("Timeout", e);
526 }
527 } finally {
528 groupService.removeListener(listener);
529 }
530 }
531
helenyrwu2a674902016-07-20 09:48:04 -0700532 private TrafficTreatment buildFailoverTreatment(DeviceId srcDevice,
533 GroupKey groupKey) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700534 Group group = waitForGroup(srcDevice, groupKey);
helenyrwu2a674902016-07-20 09:48:04 -0700535 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
536 TrafficTreatment trafficTreatment = tBuilder.group(group.id()).build();
537 return trafficTreatment;
538 }
539
540 /**
541 * Deletes intents from the given list if the ports or links the intent
542 * relies on are no longer viable. The failover flow rule intent is never
543 * deleted -- only its contents are updated.
544 *
545 * @param oldInstallables list of intents to examine
546 * @return list of reusable installable intents
547 */
548 private List<Intent> filterInvalidSubIntents(List<Intent> oldInstallables,
549 PointToPointIntent pointIntent) {
550 List<Intent> intentList = new ArrayList<>();
551 intentList.addAll(oldInstallables);
552 erasePrimary = false;
553 eraseBackup = false;
554 if (intentList != null) {
555 Iterator<Intent> iterator = intentList.iterator();
556 while (iterator.hasNext() && !(erasePrimary && eraseBackup)) {
557 Intent intent = iterator.next();
558 intent.resources().forEach(resource -> {
559 if (resource instanceof Link) {
560 Link link = (Link) resource;
561 if (link.state() == Link.State.INACTIVE) {
562 setPathsToRemove(intent);
563 } else if (link instanceof EdgeLink) {
564 ConnectPoint connectPoint = (link.src().elementId() instanceof DeviceId)
565 ? link.src() : link.dst();
566 Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
567 if (port == null || !port.isEnabled()) {
568 setPathsToRemove(intent);
569 }
570 } else {
571 Port port1 = deviceService.getPort(link.src().deviceId(), link.src().port());
572 Port port2 = deviceService.getPort(link.dst().deviceId(), link.dst().port());
573 if (port1 == null || !port1.isEnabled() || port2 == null || !port2.isEnabled()) {
574 setPathsToRemove(intent);
575 }
576 }
577 }
578 });
579 }
580 removeAndUpdateIntents(intentList, pointIntent);
581 }
582 return intentList;
583 }
584
585 /**
586 * Sets instance variables erasePrimary and eraseBackup. If erasePrimary,
587 * the primary path is no longer viable and related intents will be deleted.
588 * If eraseBackup, the backup path is no longer viable and related intents
589 * will be deleted.
590 *
591 * @param intent intent whose resources are found to be disabled/inactive:
592 * if intent is part of primary path, primary path set for removal;
593 * if intent is part of backup path, backup path set for removal;
594 * if bad intent is of type failover, the ingress point is down,
595 * and both paths are rendered inactive.
596 * @return true if both primary and backup paths are to be removed
597 */
598 private boolean setPathsToRemove(Intent intent) {
599 if (intent instanceof FlowRuleIntent) {
600 FlowRuleIntent frIntent = (FlowRuleIntent) intent;
601 PathIntent.ProtectionType type = frIntent.type();
602 if (type == PathIntent.ProtectionType.PRIMARY || type == PathIntent.ProtectionType.FAILOVER) {
603 erasePrimary = true;
604 }
605 if (type == PathIntent.ProtectionType.BACKUP || type == PathIntent.ProtectionType.FAILOVER) {
606 eraseBackup = true;
607 }
608 }
609 return erasePrimary && eraseBackup;
610 }
611
612 /**
613 * Removes intents from installables list, depending on the values
614 * of instance variables erasePrimary and eraseBackup. Flow rule intents
615 * that contain the manufactured fast failover flow rules are never deleted.
616 * The contents are simply modified as necessary. If cleanUpIntents size
617 * is greater than 1 (failover intent), then one whole path from previous
618 * installables must be still viable.
619 *
620 * @param cleanUpIntents list of installable intents
621 */
622 private void removeAndUpdateIntents(List<Intent> cleanUpIntents,
623 PointToPointIntent pointIntent) {
624 ListIterator<Intent> iterator = cleanUpIntents.listIterator();
625 while (iterator.hasNext()) {
626 Intent cIntent = iterator.next();
627 if (cIntent instanceof FlowRuleIntent) {
628 FlowRuleIntent fIntent = (FlowRuleIntent) cIntent;
629 if (fIntent.type() == PathIntent.ProtectionType.PRIMARY && erasePrimary) {
630 // remove primary path's flow rule intents
631 iterator.remove();
632 } else if (fIntent.type() == PathIntent.ProtectionType.BACKUP && eraseBackup) {
633 //remove backup path's flow rule intents
634 iterator.remove();
635 } else if (fIntent.type() == PathIntent.ProtectionType.BACKUP && erasePrimary) {
636 // promote backup path's flow rule intents to primary
637 iterator.set(new FlowRuleIntent(fIntent, PathIntent.ProtectionType.PRIMARY));
638 }
639 }
640 }
641 // remove buckets whose watchports are disabled if the failover group exists
Pier Ventreffe88d62016-10-13 14:34:40 -0700642 Group group = groupService.getGroup(pointIntent.filteredIngressPoint().connectPoint().deviceId(),
helenyrwu2a674902016-07-20 09:48:04 -0700643 makeGroupKey(pointIntent.id()));
644 if (group != null) {
645 updateFailoverGroup(pointIntent);
646 }
647 }
648
649 // Removes buckets whose treatments rely on disabled ports from the
650 // failover group.
651 private void updateFailoverGroup(PointToPointIntent pointIntent) {
Pier Ventreffe88d62016-10-13 14:34:40 -0700652 DeviceId deviceId = pointIntent.filteredIngressPoint().connectPoint().deviceId();
helenyrwu2a674902016-07-20 09:48:04 -0700653 GroupKey groupKey = makeGroupKey(pointIntent.id());
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700654 Group group = waitForGroup(deviceId, groupKey);
helenyrwu2a674902016-07-20 09:48:04 -0700655 Iterator<GroupBucket> groupIterator = group.buckets().buckets().iterator();
656 while (groupIterator.hasNext()) {
657 GroupBucket bucket = groupIterator.next();
658 Instruction individualInstruction = bucket.treatment().allInstructions().get(0);
659 if (individualInstruction instanceof Instructions.OutputInstruction) {
660 Instructions.OutputInstruction outInstruction =
661 (Instructions.OutputInstruction) individualInstruction;
662 Port port = deviceService.getPort(deviceId, outInstruction.port());
663 if (port == null || !port.isEnabled()) {
664 GroupBuckets removeBuckets = new GroupBuckets(Collections.singletonList(bucket));
665 groupService.removeBucketsFromGroup(deviceId, groupKey,
666 removeBuckets, groupKey,
667 pointIntent.appId());
668 }
669 }
670 }
671 }
672
673 // Adds failover group bucket with treatment outport determined by the
674 // ingress point of the links.
675 private void updateFailoverGroup(PointToPointIntent intent, List<Link> links) {
676 GroupKey groupKey = makeGroupKey(intent.id());
677
678 TrafficTreatment.Builder tBuilderIn = DefaultTrafficTreatment.builder();
679 ConnectPoint src = links.get(0).src();
680 tBuilderIn.setOutput(src.port());
681 GroupBucket bucket = DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn.build(), src.port(), null);
682 GroupBuckets addBuckets = new GroupBuckets(Collections.singletonList(bucket));
683
684 groupService.addBucketsToGroup(src.deviceId(), groupKey, addBuckets, groupKey, intent.appId());
685 }
Ray Milkeya058c732014-10-08 13:52:34 -0700686}