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