blob: a6fac6fa78869fd9199b4725e3f998d85c0b991b [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<>();
Sho SHIMIZUde8e6b52014-11-13 11:23:17 -0800164 Path path = getPath(intent, ingressPoint.deviceId(),
helenyrwu2a674902016-07-20 09:48:04 -0700165 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) {
177 Path path = getPath(intent, intent.filteredIngressPoint().connectPoint().deviceId(),
178 intent.filteredEgressPoint().connectPoint().deviceId());
179
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()),
262 PathIntent.ProtectionType.FAILOVER);
263 intentList.add(frIntent);
264 } else {
265 updateFailoverGroup(intent, links);
266 updateFailoverGroup(intent, backupLinks);
267 }
268
269 return intentList;
270 }
271
272 private List<Intent> createSinglePathIntent(ConnectPoint ingressPoint,
273 ConnectPoint egressPoint,
274 PointToPointIntent intent,
275 List<Intent> installable) {
276 List<Link> links = new ArrayList<>();
277 Path onlyPath = getPath(intent, ingressPoint.deviceId(),
278 egressPoint.deviceId());
279
280 List<Intent> reusableIntents = null;
281 if (installable != null) {
282 reusableIntents = filterInvalidSubIntents(installable, intent);
283 if (reusableIntents.size() == installable.size()) {
284 // all old paths are still viable
285 return installable;
286 }
287 }
288
289 // If there exists a full path from old installable intents,
290 // return the intents that comprise it.
291 if (reusableIntents != null && reusableIntents.size() > 1) {
292 return reusableIntents;
293 } else {
294 links.add(createEdgeLink(ingressPoint, true));
295 links.addAll(onlyPath.links());
296 links.add(createEdgeLink(egressPoint, false));
helenyrwu2a674902016-07-20 09:48:04 -0700297 return asList(createPathIntent(new DefaultPath(PID, links, onlyPath.cost(),
298 onlyPath.annotations()),
299 intent, PathIntent.ProtectionType.PRIMARY));
300 }
Ray Milkeya058c732014-10-08 13:52:34 -0700301 }
302
303 /**
304 * Creates a path intent from the specified path and original
305 * connectivity intent.
306 *
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700307 * @param path path to create an intent for
Ray Milkeya058c732014-10-08 13:52:34 -0700308 * @param intent original intent
helenyrwu2a674902016-07-20 09:48:04 -0700309 * @param type primary or backup
Ray Milkeya058c732014-10-08 13:52:34 -0700310 */
311 private Intent createPathIntent(Path path,
helenyrwu2a674902016-07-20 09:48:04 -0700312 PointToPointIntent intent,
313 PathIntent.ProtectionType type) {
Ray Milkeyebc5d222015-03-18 15:45:36 -0700314 return PathIntent.builder()
315 .appId(intent.appId())
Yuta HIGUCHI652f27f2016-10-31 16:54:30 -0700316 .key(intent.key())
Ray Milkeyebc5d222015-03-18 15:45:36 -0700317 .selector(intent.selector())
318 .treatment(intent.treatment())
319 .path(path)
320 .constraints(intent.constraints())
321 .priority(intent.priority())
helenyrwu2a674902016-07-20 09:48:04 -0700322 .setType(type)
Ray Milkeyebc5d222015-03-18 15:45:36 -0700323 .build();
Ray Milkeya058c732014-10-08 13:52:34 -0700324 }
325
Pier Ventreffe88d62016-10-13 14:34:40 -0700326
327 /**
328 * Creates a link collection intent from the specified path and original
329 * point to point intent.
330 *
331 * @param links the links of the packets
332 * @param cost the cost associated to the links
333 * @param intent the point to point intent we are compiling
334 * @return the link collection intent
335 */
336 private Intent createLinkCollectionIntent(Set<Link> links,
337 double cost,
338 PointToPointIntent intent) {
339
340 return LinkCollectionIntent.builder()
341 .key(intent.key())
342 .appId(intent.appId())
343 .selector(intent.selector())
344 .treatment(intent.treatment())
345 .links(ImmutableSet.copyOf(links))
346 .filteredIngressPoints(ImmutableSet.of(
347 intent.filteredIngressPoint()
348 ))
349 .filteredEgressPoints(ImmutableSet.of(
350 intent.filteredEgressPoint()
351 ))
352 .applyTreatmentOnEgress(true)
353 .constraints(intent.constraints())
354 .priority(intent.priority())
355 .cost(cost)
356 .build();
357 }
358
helenyrwu2a674902016-07-20 09:48:04 -0700359 /**
360 * Gets primary port number through failover group associated
361 * with this intent.
362 */
363 private PortNumber getPrimaryPort(PointToPointIntent intent) {
Pier Ventreffe88d62016-10-13 14:34:40 -0700364 Group group = groupService.getGroup(intent.filteredIngressPoint().connectPoint().deviceId(),
helenyrwu2a674902016-07-20 09:48:04 -0700365 makeGroupKey(intent.id()));
366 PortNumber primaryPort = null;
367 if (group != null) {
368 List<GroupBucket> buckets = group.buckets().buckets();
369 Iterator<GroupBucket> iterator = buckets.iterator();
370 while (primaryPort == null && iterator.hasNext()) {
371 GroupBucket bucket = iterator.next();
372 Instruction individualInstruction = bucket.treatment().allInstructions().get(0);
373 if (individualInstruction instanceof Instructions.OutputInstruction) {
374 Instructions.OutputInstruction outInstruction =
375 (Instructions.OutputInstruction) individualInstruction;
376 PortNumber tempPortNum = outInstruction.port();
Pier Ventreffe88d62016-10-13 14:34:40 -0700377 Port port = deviceService.getPort(intent.filteredIngressPoint().connectPoint().deviceId(),
helenyrwu2a674902016-07-20 09:48:04 -0700378 tempPortNum);
379 if (port != null && port.isEnabled()) {
380 primaryPort = tempPortNum;
381 }
382 }
383 }
384 }
385 return primaryPort;
386 }
387
388 /**
389 * Creates group key unique to each intent.
Ray Milkeyef794342016-11-09 16:20:29 -0800390 *
391 * @param intentId identifier of intent to get a key for
392 * @return unique group key for the intent identifier
helenyrwu2a674902016-07-20 09:48:04 -0700393 */
394 public static GroupKey makeGroupKey(IntentId intentId) {
395 ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
396 buffer.putLong(intentId.fingerprint());
397 return new DefaultGroupKey(buffer.array());
398 }
399
400 /**
401 * Creates a new failover group with the initial ports of the links
402 * from the primary and backup path.
403 *
404 * @param links links from the primary path
405 * @param backupLinks links from the backup path
406 * @param intent intent from which this call originates
407 */
408 private void createFailoverTreatmentGroup(List<Link> links,
409 List<Link> backupLinks,
410 PointToPointIntent intent) {
411
412 List<GroupBucket> buckets = new ArrayList<>();
413
414 TrafficTreatment.Builder tBuilderIn = DefaultTrafficTreatment.builder();
415 ConnectPoint src = links.get(0).src();
416 tBuilderIn.setOutput(src.port());
417
418 TrafficTreatment.Builder tBuilderIn2 = DefaultTrafficTreatment.builder();
419 ConnectPoint src2 = backupLinks.get(0).src();
420 tBuilderIn2.setOutput(src2.port());
421
422 buckets.add(DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn.build(), src.port(), null));
423 buckets.add(DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn2.build(), src2.port(), null));
424
425 GroupBuckets groupBuckets = new GroupBuckets(buckets);
426
427 GroupDescription groupDesc = new DefaultGroupDescription(src.deviceId(), Group.Type.FAILOVER,
428 groupBuckets, makeGroupKey(intent.id()), null, intent.appId());
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700429 log.trace("adding failover group {}", groupDesc);
helenyrwu2a674902016-07-20 09:48:04 -0700430 groupService.addGroup(groupDesc);
431 }
432
433 /**
434 * Manufactures flow rule with treatment that is defined by failover
435 * group and traffic selector determined by ingress port of the intent.
436 *
437 * @param intent intent which is being compiled (for appId)
438 * @return a list of a singular flow rule with fast failover
439 * outport traffic treatment
440 */
441 private List<FlowRule> createFailoverFlowRules(PointToPointIntent intent) {
442 List<FlowRule> flowRules = new ArrayList<>();
443
444 ConnectPoint ingress = intent.ingressPoint();
445 DeviceId deviceId = ingress.deviceId();
446
447 // flow rule with failover traffic treatment
448 TrafficSelector trafficSelector = DefaultTrafficSelector.builder(intent.selector())
449 .matchInPort(ingress.port()).build();
450
451 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
452 flowRules.add(flowRuleBuilder.withSelector(trafficSelector)
453 .withTreatment(buildFailoverTreatment(deviceId, makeGroupKey(intent.id())))
454 .fromApp(intent.appId())
455 .makePermanent()
456 .forDevice(deviceId)
457 .withPriority(PRIORITY)
458 .build());
459
460 return flowRules;
461 }
462
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700463
464 /**
465 * Waits for specified group to appear maximum of {@value #GROUP_TIMEOUT} seconds.
466 *
467 * @param deviceId {@link DeviceId}
468 * @param groupKey {@link GroupKey} to wait for.
469 * @return {@link Group}
470 * @throws IntentCompilationException on any error.
471 */
472 private Group waitForGroup(DeviceId deviceId, GroupKey groupKey) {
473 return waitForGroup(deviceId, groupKey, GROUP_TIMEOUT, TimeUnit.SECONDS);
474 }
475
476 /**
477 * Waits for specified group to appear until timeout.
478 *
479 * @param deviceId {@link DeviceId}
480 * @param groupKey {@link GroupKey} to wait for.
481 * @param timeout timeout
482 * @param unit unit of timeout
483 * @return {@link Group}
484 * @throws IntentCompilationException on any error.
485 */
486 private Group waitForGroup(DeviceId deviceId, GroupKey groupKey, long timeout, TimeUnit unit) {
487 Group group = groupService.getGroup(deviceId, groupKey);
488 if (group != null) {
489 return group;
490 }
491
492 final CompletableFuture<Group> future = new CompletableFuture<>();
493 final GroupListener listener = event -> {
494 if (event.subject().deviceId() == deviceId &&
495 event.subject().appCookie().equals(groupKey)) {
496 future.complete(event.subject());
497 return;
498 }
499 };
500
501 groupService.addListener(listener);
502 try {
503 group = groupService.getGroup(deviceId, groupKey);
504 if (group != null) {
505 return group;
506 }
507 return future.get(timeout, unit);
508 } catch (InterruptedException e) {
509 log.debug("Interrupted", e);
510 Thread.currentThread().interrupt();
511 throw new IntentCompilationException("Interrupted", e);
512 } catch (ExecutionException e) {
513 log.debug("ExecutionException", e);
514 throw new IntentCompilationException("ExecutionException caught", e);
515 } catch (TimeoutException e) {
516 // one last try
517 group = groupService.getGroup(deviceId, groupKey);
518 if (group != null) {
519 return group;
520 } else {
521 log.debug("Timeout", e);
522 throw new IntentCompilationException("Timeout", e);
523 }
524 } finally {
525 groupService.removeListener(listener);
526 }
527 }
528
helenyrwu2a674902016-07-20 09:48:04 -0700529 private TrafficTreatment buildFailoverTreatment(DeviceId srcDevice,
530 GroupKey groupKey) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700531 Group group = waitForGroup(srcDevice, groupKey);
helenyrwu2a674902016-07-20 09:48:04 -0700532 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
533 TrafficTreatment trafficTreatment = tBuilder.group(group.id()).build();
534 return trafficTreatment;
535 }
536
537 /**
538 * Deletes intents from the given list if the ports or links the intent
539 * relies on are no longer viable. The failover flow rule intent is never
540 * deleted -- only its contents are updated.
541 *
542 * @param oldInstallables list of intents to examine
543 * @return list of reusable installable intents
544 */
545 private List<Intent> filterInvalidSubIntents(List<Intent> oldInstallables,
546 PointToPointIntent pointIntent) {
547 List<Intent> intentList = new ArrayList<>();
548 intentList.addAll(oldInstallables);
549 erasePrimary = false;
550 eraseBackup = false;
551 if (intentList != null) {
552 Iterator<Intent> iterator = intentList.iterator();
553 while (iterator.hasNext() && !(erasePrimary && eraseBackup)) {
554 Intent intent = iterator.next();
555 intent.resources().forEach(resource -> {
556 if (resource instanceof Link) {
557 Link link = (Link) resource;
558 if (link.state() == Link.State.INACTIVE) {
559 setPathsToRemove(intent);
560 } else if (link instanceof EdgeLink) {
561 ConnectPoint connectPoint = (link.src().elementId() instanceof DeviceId)
562 ? link.src() : link.dst();
563 Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
564 if (port == null || !port.isEnabled()) {
565 setPathsToRemove(intent);
566 }
567 } else {
568 Port port1 = deviceService.getPort(link.src().deviceId(), link.src().port());
569 Port port2 = deviceService.getPort(link.dst().deviceId(), link.dst().port());
570 if (port1 == null || !port1.isEnabled() || port2 == null || !port2.isEnabled()) {
571 setPathsToRemove(intent);
572 }
573 }
574 }
575 });
576 }
577 removeAndUpdateIntents(intentList, pointIntent);
578 }
579 return intentList;
580 }
581
582 /**
583 * Sets instance variables erasePrimary and eraseBackup. If erasePrimary,
584 * the primary path is no longer viable and related intents will be deleted.
585 * If eraseBackup, the backup path is no longer viable and related intents
586 * will be deleted.
587 *
588 * @param intent intent whose resources are found to be disabled/inactive:
589 * if intent is part of primary path, primary path set for removal;
590 * if intent is part of backup path, backup path set for removal;
591 * if bad intent is of type failover, the ingress point is down,
592 * and both paths are rendered inactive.
593 * @return true if both primary and backup paths are to be removed
594 */
595 private boolean setPathsToRemove(Intent intent) {
596 if (intent instanceof FlowRuleIntent) {
597 FlowRuleIntent frIntent = (FlowRuleIntent) intent;
598 PathIntent.ProtectionType type = frIntent.type();
599 if (type == PathIntent.ProtectionType.PRIMARY || type == PathIntent.ProtectionType.FAILOVER) {
600 erasePrimary = true;
601 }
602 if (type == PathIntent.ProtectionType.BACKUP || type == PathIntent.ProtectionType.FAILOVER) {
603 eraseBackup = true;
604 }
605 }
606 return erasePrimary && eraseBackup;
607 }
608
609 /**
610 * Removes intents from installables list, depending on the values
611 * of instance variables erasePrimary and eraseBackup. Flow rule intents
612 * that contain the manufactured fast failover flow rules are never deleted.
613 * The contents are simply modified as necessary. If cleanUpIntents size
614 * is greater than 1 (failover intent), then one whole path from previous
615 * installables must be still viable.
616 *
617 * @param cleanUpIntents list of installable intents
618 */
619 private void removeAndUpdateIntents(List<Intent> cleanUpIntents,
620 PointToPointIntent pointIntent) {
621 ListIterator<Intent> iterator = cleanUpIntents.listIterator();
622 while (iterator.hasNext()) {
623 Intent cIntent = iterator.next();
624 if (cIntent instanceof FlowRuleIntent) {
625 FlowRuleIntent fIntent = (FlowRuleIntent) cIntent;
626 if (fIntent.type() == PathIntent.ProtectionType.PRIMARY && erasePrimary) {
627 // remove primary path's flow rule intents
628 iterator.remove();
629 } else if (fIntent.type() == PathIntent.ProtectionType.BACKUP && eraseBackup) {
630 //remove backup path's flow rule intents
631 iterator.remove();
632 } else if (fIntent.type() == PathIntent.ProtectionType.BACKUP && erasePrimary) {
633 // promote backup path's flow rule intents to primary
634 iterator.set(new FlowRuleIntent(fIntent, PathIntent.ProtectionType.PRIMARY));
635 }
636 }
637 }
638 // remove buckets whose watchports are disabled if the failover group exists
Pier Ventreffe88d62016-10-13 14:34:40 -0700639 Group group = groupService.getGroup(pointIntent.filteredIngressPoint().connectPoint().deviceId(),
helenyrwu2a674902016-07-20 09:48:04 -0700640 makeGroupKey(pointIntent.id()));
641 if (group != null) {
642 updateFailoverGroup(pointIntent);
643 }
644 }
645
646 // Removes buckets whose treatments rely on disabled ports from the
647 // failover group.
648 private void updateFailoverGroup(PointToPointIntent pointIntent) {
Pier Ventreffe88d62016-10-13 14:34:40 -0700649 DeviceId deviceId = pointIntent.filteredIngressPoint().connectPoint().deviceId();
helenyrwu2a674902016-07-20 09:48:04 -0700650 GroupKey groupKey = makeGroupKey(pointIntent.id());
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700651 Group group = waitForGroup(deviceId, groupKey);
helenyrwu2a674902016-07-20 09:48:04 -0700652 Iterator<GroupBucket> groupIterator = group.buckets().buckets().iterator();
653 while (groupIterator.hasNext()) {
654 GroupBucket bucket = groupIterator.next();
655 Instruction individualInstruction = bucket.treatment().allInstructions().get(0);
656 if (individualInstruction instanceof Instructions.OutputInstruction) {
657 Instructions.OutputInstruction outInstruction =
658 (Instructions.OutputInstruction) individualInstruction;
659 Port port = deviceService.getPort(deviceId, outInstruction.port());
660 if (port == null || !port.isEnabled()) {
661 GroupBuckets removeBuckets = new GroupBuckets(Collections.singletonList(bucket));
662 groupService.removeBucketsFromGroup(deviceId, groupKey,
663 removeBuckets, groupKey,
664 pointIntent.appId());
665 }
666 }
667 }
668 }
669
670 // Adds failover group bucket with treatment outport determined by the
671 // ingress point of the links.
672 private void updateFailoverGroup(PointToPointIntent intent, List<Link> links) {
673 GroupKey groupKey = makeGroupKey(intent.id());
674
675 TrafficTreatment.Builder tBuilderIn = DefaultTrafficTreatment.builder();
676 ConnectPoint src = links.get(0).src();
677 tBuilderIn.setOutput(src.port());
678 GroupBucket bucket = DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn.build(), src.port(), null);
679 GroupBuckets addBuckets = new GroupBuckets(Collections.singletonList(bucket));
680
681 groupService.addBucketsToGroup(src.deviceId(), groupKey, addBuckets, groupKey, intent.appId());
682 }
Ray Milkeya058c732014-10-08 13:52:34 -0700683}