blob: b53143b313bf982ea142676c4ae79a2ba359863e [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
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;
Luca Pretede10c782017-01-05 17:23:08 -080076import java.util.stream.Collectors;
77import java.util.stream.Stream;
Ray Milkeya058c732014-10-08 13:52:34 -070078
Thomas Vachuska425a2d72014-10-29 11:28:28 -070079import static java.util.Arrays.asList;
Brian O'Connorabafb502014-12-02 22:26:20 -080080import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070081import static org.slf4j.LoggerFactory.getLogger;
Thomas Vachuska425a2d72014-10-29 11:28:28 -070082
Ray Milkeya058c732014-10-08 13:52:34 -070083/**
Brian O'Connorabafb502014-12-02 22:26:20 -080084 * An intent compiler for {@link org.onosproject.net.intent.PointToPointIntent}.
Ray Milkeya058c732014-10-08 13:52:34 -070085 */
86@Component(immediate = true)
87public class PointToPointIntentCompiler
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080088 extends ConnectivityIntentCompiler<PointToPointIntent> {
Ray Milkeya058c732014-10-08 13:52:34 -070089
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080090 // TODO: use off-the-shell core provider ID
91 private static final ProviderId PID =
Brian O'Connorabafb502014-12-02 22:26:20 -080092 new ProviderId("core", "org.onosproject.core", true);
Sho SHIMIZU3908fde2014-11-19 16:30:22 -080093 // TODO: consider whether the default cost is appropriate or not
94 public static final int DEFAULT_COST = 1;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070095
helenyrwu2a674902016-07-20 09:48:04 -070096 protected static final int PRIORITY = Intent.DEFAULT_INTENT_PRIORITY;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070097
98 private static final int GROUP_TIMEOUT = 5;
99
100 private final Logger log = getLogger(getClass());
101
helenyrwu2a674902016-07-20 09:48:04 -0700102 protected boolean erasePrimary = false;
103 protected boolean eraseBackup = false;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected GroupService groupService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected LinkService linkService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected DeviceService deviceService;
weibit50eb95b2014-10-25 21:47:54 -0700113
Ray Milkeya058c732014-10-08 13:52:34 -0700114 @Activate
115 public void activate() {
Ray Milkeya058c732014-10-08 13:52:34 -0700116 intentManager.registerCompiler(PointToPointIntent.class, this);
117 }
118
119 @Deactivate
120 public void deactivate() {
121 intentManager.unregisterCompiler(PointToPointIntent.class);
122 }
123
124 @Override
Sho SHIMIZUec07ffd2016-02-22 20:45:21 -0800125 public List<Intent> compile(PointToPointIntent intent, List<Intent> installable) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700126 log.trace("compiling {} {}", intent, installable);
Luca Pretede10c782017-01-05 17:23:08 -0800127 ConnectPoint ingressPoint = intent.filteredIngressPoint().connectPoint();
128 ConnectPoint egressPoint = intent.filteredEgressPoint().connectPoint();
Sho SHIMIZUde8e6b52014-11-13 11:23:17 -0800129
130 if (ingressPoint.deviceId().equals(egressPoint.deviceId())) {
Pier Ventreffe88d62016-10-13 14:34:40 -0700131 return createZeroHopLinkCollectionIntent(intent);
Sho SHIMIZUde8e6b52014-11-13 11:23:17 -0800132 }
Ray Milkeya058c732014-10-08 13:52:34 -0700133
helenyrwu2a674902016-07-20 09:48:04 -0700134 // proceed with no protected paths
135 if (!ProtectionConstraint.requireProtectedPath(intent)) {
Pier Ventreffe88d62016-10-13 14:34:40 -0700136 return createUnprotectedLinkCollectionIntent(intent);
helenyrwu2a674902016-07-20 09:48:04 -0700137 }
138
139 try {
140 // attempt to compute and implement backup path
141 return createProtectedIntent(ingressPoint, egressPoint, intent, installable);
142 } catch (PathNotFoundException e) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700143 log.warn("Could not find disjoint Path for {}", intent);
helenyrwu2a674902016-07-20 09:48:04 -0700144 // no disjoint path extant -- maximum one path exists between devices
145 return createSinglePathIntent(ingressPoint, egressPoint, intent, installable);
146 }
147 }
148
149 private List<Intent> createZeroHopIntent(ConnectPoint ingressPoint,
150 ConnectPoint egressPoint,
151 PointToPointIntent intent) {
152 List<Link> links = asList(createEdgeLink(ingressPoint, true), createEdgeLink(egressPoint, false));
153 return asList(createPathIntent(new DefaultPath(PID, links, DEFAULT_COST),
154 intent, PathIntent.ProtectionType.PRIMARY));
155 }
156
Pier Ventreffe88d62016-10-13 14:34:40 -0700157 private List<Intent> createZeroHopLinkCollectionIntent(PointToPointIntent intent) {
158 return asList(createLinkCollectionIntent(ImmutableSet.of(), DEFAULT_COST,
159 intent));
160 }
161
Luca Pretede10c782017-01-05 17:23:08 -0800162 /**
163 * Creates an unprotected intent.
164 * @param ingressPoint the ingress connect point
165 * @param egressPoint the egress connect point
166 * @param intent the original intent
167 * @return the compilation result
168 * @deprecated 1.10.0
169 */
170 @Deprecated
helenyrwu2a674902016-07-20 09:48:04 -0700171 private List<Intent> createUnprotectedIntent(ConnectPoint ingressPoint,
172 ConnectPoint egressPoint,
173 PointToPointIntent intent) {
Ray Milkeya058c732014-10-08 13:52:34 -0700174 List<Link> links = new ArrayList<>();
Luca Preted26ea652017-01-03 15:59:30 -0800175 Path path = getPathOrException(intent, ingressPoint.deviceId(),
176 egressPoint.deviceId());
Sho SHIMIZUde8e6b52014-11-13 11:23:17 -0800177
Sho SHIMIZU3908fde2014-11-19 16:30:22 -0800178 links.add(createEdgeLink(ingressPoint, true));
Ray Milkeya058c732014-10-08 13:52:34 -0700179 links.addAll(path.links());
Sho SHIMIZU3908fde2014-11-19 16:30:22 -0800180 links.add(createEdgeLink(egressPoint, false));
Ray Milkeya058c732014-10-08 13:52:34 -0700181
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800182 return asList(createPathIntent(new DefaultPath(PID, links, path.cost(),
helenyrwu2a674902016-07-20 09:48:04 -0700183 path.annotations()), intent,
184 PathIntent.ProtectionType.PRIMARY));
185 }
186
Pier Ventreffe88d62016-10-13 14:34:40 -0700187 private List<Intent> createUnprotectedLinkCollectionIntent(PointToPointIntent intent) {
Luca Preted26ea652017-01-03 15:59:30 -0800188 Path path = getPathOrException(intent, intent.filteredIngressPoint().connectPoint().deviceId(),
189 intent.filteredEgressPoint().connectPoint().deviceId());
Pier Ventreffe88d62016-10-13 14:34:40 -0700190
Luca Pretede10c782017-01-05 17:23:08 -0800191 // Allocate bandwidth if a bandwidth constraint is set
192 ConnectPoint ingressCP = intent.filteredIngressPoint().connectPoint();
193 ConnectPoint egressCP = intent.filteredEgressPoint().connectPoint();
194
195 List<ConnectPoint> pathCPs =
196 path.links().stream()
197 .flatMap(l -> Stream.of(l.src(), l.dst()))
198 .collect(Collectors.toList());
199
200 pathCPs.add(ingressCP);
201 pathCPs.add(egressCP);
202
203 allocateBandwidth(intent, pathCPs);
204
Pier Ventreffe88d62016-10-13 14:34:40 -0700205 return asList(createLinkCollectionIntent(ImmutableSet.copyOf(path.links()),
206 path.cost(),
207 intent));
208 }
209
helenyrwu2a674902016-07-20 09:48:04 -0700210 //FIXME: Compatibility with EncapsulationConstraint
211 private List<Intent> createProtectedIntent(ConnectPoint ingressPoint,
212 ConnectPoint egressPoint,
213 PointToPointIntent intent,
214 List<Intent> installable) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700215 log.trace("createProtectedIntent");
helenyrwu2a674902016-07-20 09:48:04 -0700216 DisjointPath path = getDisjointPath(intent, ingressPoint.deviceId(),
217 egressPoint.deviceId());
218
219 List<Intent> reusableIntents = null;
220 if (installable != null) {
221 reusableIntents = filterInvalidSubIntents(installable, intent);
222 if (reusableIntents.size() == installable.size()) {
223 // all old paths are still viable
224 return installable;
225 }
226 }
227
228 List<Intent> intentList = new ArrayList<>();
229
230 // primary path intent
231 List<Link> links = new ArrayList<>();
232 links.addAll(path.links());
233 links.add(createEdgeLink(egressPoint, false));
234
235 // backup path intent
236 List<Link> backupLinks = new ArrayList<>();
237 backupLinks.addAll(path.backup().links());
238 backupLinks.add(createEdgeLink(egressPoint, false));
239
240 /*
241 * One of the old paths is still entirely intact. This old path has
242 * already been made primary, so we must add a backup path intent
243 * and modify the failover group treatment accordingly.
244 */
245 if (reusableIntents != null && reusableIntents.size() > 1) {
246 /*
247 * Ensures that the egress port on source device is different than
248 * that of existing path so that failover group will be useful
249 * (would not be useful if both output ports in group bucket were
250 * the same). Does not necessarily ensure that the new backup path
251 * is entirely disjoint from the old path.
252 */
253 PortNumber primaryPort = getPrimaryPort(intent);
254 if (primaryPort != null && !links.get(0).src().port().equals(primaryPort)) {
255 reusableIntents.add(createPathIntent(new DefaultPath(PID, links,
256 path.cost(), path.annotations()),
257 intent, PathIntent.ProtectionType.BACKUP));
258 updateFailoverGroup(intent, links);
259 return reusableIntents;
260
261 } else {
262 reusableIntents.add(createPathIntent(new DefaultPath(PID, backupLinks, path.backup().cost(),
263 path.backup().annotations()), intent, PathIntent.ProtectionType.BACKUP));
264 updateFailoverGroup(intent, backupLinks);
265 return reusableIntents;
266 }
267 }
268
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700269 intentList.add(createPathIntent(new DefaultPath(PID, links, path.cost(),
270 path.annotations()),
helenyrwu2a674902016-07-20 09:48:04 -0700271 intent, PathIntent.ProtectionType.PRIMARY));
272 intentList.add(createPathIntent(new DefaultPath(PID, backupLinks, path.backup().cost(),
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700273 path.backup().annotations()),
274 intent, PathIntent.ProtectionType.BACKUP));
helenyrwu2a674902016-07-20 09:48:04 -0700275
276 // Create fast failover flow rule intent or, if it already exists,
277 // add contents appropriately.
278 if (groupService.getGroup(ingressPoint.deviceId(),
279 makeGroupKey(intent.id())) == null) {
280 // manufactured fast failover flow rule intent
281 createFailoverTreatmentGroup(path.links(), path.backup().links(), intent);
282
283 FlowRuleIntent frIntent = new FlowRuleIntent(intent.appId(),
Yuta HIGUCHI652f27f2016-10-31 16:54:30 -0700284 intent.key(),
helenyrwu2a674902016-07-20 09:48:04 -0700285 createFailoverFlowRules(intent),
286 asList(ingressPoint.deviceId()),
Luca Prete670ac5d2017-02-03 15:55:43 -0800287 PathIntent.ProtectionType.FAILOVER,
288 intent.resourceGroup());
helenyrwu2a674902016-07-20 09:48:04 -0700289 intentList.add(frIntent);
290 } else {
291 updateFailoverGroup(intent, links);
292 updateFailoverGroup(intent, backupLinks);
293 }
294
295 return intentList;
296 }
297
298 private List<Intent> createSinglePathIntent(ConnectPoint ingressPoint,
299 ConnectPoint egressPoint,
300 PointToPointIntent intent,
301 List<Intent> installable) {
302 List<Link> links = new ArrayList<>();
Luca Preted26ea652017-01-03 15:59:30 -0800303 Path onlyPath = getPathOrException(intent, ingressPoint.deviceId(),
304 egressPoint.deviceId());
helenyrwu2a674902016-07-20 09:48:04 -0700305
306 List<Intent> reusableIntents = null;
307 if (installable != null) {
308 reusableIntents = filterInvalidSubIntents(installable, intent);
309 if (reusableIntents.size() == installable.size()) {
310 // all old paths are still viable
311 return installable;
312 }
313 }
314
315 // If there exists a full path from old installable intents,
316 // return the intents that comprise it.
317 if (reusableIntents != null && reusableIntents.size() > 1) {
318 return reusableIntents;
319 } else {
Luca Pretede10c782017-01-05 17:23:08 -0800320 // Allocate bandwidth if a bandwidth constraint is set
321 ConnectPoint ingressCP = intent.filteredIngressPoint().connectPoint();
322 ConnectPoint egressCP = intent.filteredEgressPoint().connectPoint();
323
324 List<ConnectPoint> pathCPs =
325 onlyPath.links().stream()
326 .flatMap(l -> Stream.of(l.src(), l.dst()))
327 .collect(Collectors.toList());
328
329 pathCPs.add(ingressCP);
330 pathCPs.add(egressCP);
331
332 // Allocate bandwidth if a bandwidth constraint is set
333 allocateBandwidth(intent, pathCPs);
334
helenyrwu2a674902016-07-20 09:48:04 -0700335 links.add(createEdgeLink(ingressPoint, true));
336 links.addAll(onlyPath.links());
337 links.add(createEdgeLink(egressPoint, false));
helenyrwu2a674902016-07-20 09:48:04 -0700338 return asList(createPathIntent(new DefaultPath(PID, links, onlyPath.cost(),
339 onlyPath.annotations()),
340 intent, PathIntent.ProtectionType.PRIMARY));
341 }
Ray Milkeya058c732014-10-08 13:52:34 -0700342 }
343
344 /**
345 * Creates a path intent from the specified path and original
346 * connectivity intent.
347 *
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700348 * @param path path to create an intent for
Ray Milkeya058c732014-10-08 13:52:34 -0700349 * @param intent original intent
helenyrwu2a674902016-07-20 09:48:04 -0700350 * @param type primary or backup
Ray Milkeya058c732014-10-08 13:52:34 -0700351 */
352 private Intent createPathIntent(Path path,
helenyrwu2a674902016-07-20 09:48:04 -0700353 PointToPointIntent intent,
354 PathIntent.ProtectionType type) {
Ray Milkeyebc5d222015-03-18 15:45:36 -0700355 return PathIntent.builder()
356 .appId(intent.appId())
Yuta HIGUCHI652f27f2016-10-31 16:54:30 -0700357 .key(intent.key())
Ray Milkeyebc5d222015-03-18 15:45:36 -0700358 .selector(intent.selector())
359 .treatment(intent.treatment())
360 .path(path)
361 .constraints(intent.constraints())
362 .priority(intent.priority())
helenyrwu2a674902016-07-20 09:48:04 -0700363 .setType(type)
Luca Prete670ac5d2017-02-03 15:55:43 -0800364 .resourceGroup(intent.resourceGroup())
Ray Milkeyebc5d222015-03-18 15:45:36 -0700365 .build();
Ray Milkeya058c732014-10-08 13:52:34 -0700366 }
367
Pier Ventreffe88d62016-10-13 14:34:40 -0700368
369 /**
370 * Creates a link collection intent from the specified path and original
371 * point to point intent.
372 *
373 * @param links the links of the packets
374 * @param cost the cost associated to the links
375 * @param intent the point to point intent we are compiling
376 * @return the link collection intent
377 */
378 private Intent createLinkCollectionIntent(Set<Link> links,
379 double cost,
380 PointToPointIntent intent) {
381
382 return LinkCollectionIntent.builder()
383 .key(intent.key())
384 .appId(intent.appId())
385 .selector(intent.selector())
386 .treatment(intent.treatment())
387 .links(ImmutableSet.copyOf(links))
388 .filteredIngressPoints(ImmutableSet.of(
389 intent.filteredIngressPoint()
390 ))
391 .filteredEgressPoints(ImmutableSet.of(
392 intent.filteredEgressPoint()
393 ))
394 .applyTreatmentOnEgress(true)
395 .constraints(intent.constraints())
396 .priority(intent.priority())
397 .cost(cost)
Luca Prete670ac5d2017-02-03 15:55:43 -0800398 .resourceGroup(intent.resourceGroup())
Pier Ventreffe88d62016-10-13 14:34:40 -0700399 .build();
400 }
401
helenyrwu2a674902016-07-20 09:48:04 -0700402 /**
403 * Gets primary port number through failover group associated
404 * with this intent.
405 */
406 private PortNumber getPrimaryPort(PointToPointIntent intent) {
Pier Ventreffe88d62016-10-13 14:34:40 -0700407 Group group = groupService.getGroup(intent.filteredIngressPoint().connectPoint().deviceId(),
helenyrwu2a674902016-07-20 09:48:04 -0700408 makeGroupKey(intent.id()));
409 PortNumber primaryPort = null;
410 if (group != null) {
411 List<GroupBucket> buckets = group.buckets().buckets();
412 Iterator<GroupBucket> iterator = buckets.iterator();
413 while (primaryPort == null && iterator.hasNext()) {
414 GroupBucket bucket = iterator.next();
415 Instruction individualInstruction = bucket.treatment().allInstructions().get(0);
416 if (individualInstruction instanceof Instructions.OutputInstruction) {
417 Instructions.OutputInstruction outInstruction =
418 (Instructions.OutputInstruction) individualInstruction;
419 PortNumber tempPortNum = outInstruction.port();
Pier Ventreffe88d62016-10-13 14:34:40 -0700420 Port port = deviceService.getPort(intent.filteredIngressPoint().connectPoint().deviceId(),
helenyrwu2a674902016-07-20 09:48:04 -0700421 tempPortNum);
422 if (port != null && port.isEnabled()) {
423 primaryPort = tempPortNum;
424 }
425 }
426 }
427 }
428 return primaryPort;
429 }
430
431 /**
432 * Creates group key unique to each intent.
Ray Milkeyef794342016-11-09 16:20:29 -0800433 *
434 * @param intentId identifier of intent to get a key for
435 * @return unique group key for the intent identifier
helenyrwu2a674902016-07-20 09:48:04 -0700436 */
437 public static GroupKey makeGroupKey(IntentId intentId) {
438 ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
439 buffer.putLong(intentId.fingerprint());
440 return new DefaultGroupKey(buffer.array());
441 }
442
443 /**
444 * Creates a new failover group with the initial ports of the links
445 * from the primary and backup path.
446 *
447 * @param links links from the primary path
448 * @param backupLinks links from the backup path
449 * @param intent intent from which this call originates
450 */
451 private void createFailoverTreatmentGroup(List<Link> links,
452 List<Link> backupLinks,
453 PointToPointIntent intent) {
454
455 List<GroupBucket> buckets = new ArrayList<>();
456
457 TrafficTreatment.Builder tBuilderIn = DefaultTrafficTreatment.builder();
458 ConnectPoint src = links.get(0).src();
459 tBuilderIn.setOutput(src.port());
460
461 TrafficTreatment.Builder tBuilderIn2 = DefaultTrafficTreatment.builder();
462 ConnectPoint src2 = backupLinks.get(0).src();
463 tBuilderIn2.setOutput(src2.port());
464
465 buckets.add(DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn.build(), src.port(), null));
466 buckets.add(DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn2.build(), src2.port(), null));
467
468 GroupBuckets groupBuckets = new GroupBuckets(buckets);
469
470 GroupDescription groupDesc = new DefaultGroupDescription(src.deviceId(), Group.Type.FAILOVER,
471 groupBuckets, makeGroupKey(intent.id()), null, intent.appId());
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700472 log.trace("adding failover group {}", groupDesc);
helenyrwu2a674902016-07-20 09:48:04 -0700473 groupService.addGroup(groupDesc);
474 }
475
476 /**
477 * Manufactures flow rule with treatment that is defined by failover
478 * group and traffic selector determined by ingress port of the intent.
479 *
480 * @param intent intent which is being compiled (for appId)
481 * @return a list of a singular flow rule with fast failover
482 * outport traffic treatment
483 */
484 private List<FlowRule> createFailoverFlowRules(PointToPointIntent intent) {
485 List<FlowRule> flowRules = new ArrayList<>();
486
487 ConnectPoint ingress = intent.ingressPoint();
488 DeviceId deviceId = ingress.deviceId();
489
490 // flow rule with failover traffic treatment
491 TrafficSelector trafficSelector = DefaultTrafficSelector.builder(intent.selector())
492 .matchInPort(ingress.port()).build();
493
494 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
495 flowRules.add(flowRuleBuilder.withSelector(trafficSelector)
496 .withTreatment(buildFailoverTreatment(deviceId, makeGroupKey(intent.id())))
497 .fromApp(intent.appId())
498 .makePermanent()
499 .forDevice(deviceId)
500 .withPriority(PRIORITY)
501 .build());
502
503 return flowRules;
504 }
505
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700506
507 /**
508 * Waits for specified group to appear maximum of {@value #GROUP_TIMEOUT} seconds.
509 *
510 * @param deviceId {@link DeviceId}
511 * @param groupKey {@link GroupKey} to wait for.
512 * @return {@link Group}
513 * @throws IntentCompilationException on any error.
514 */
515 private Group waitForGroup(DeviceId deviceId, GroupKey groupKey) {
516 return waitForGroup(deviceId, groupKey, GROUP_TIMEOUT, TimeUnit.SECONDS);
517 }
518
519 /**
520 * Waits for specified group to appear until timeout.
521 *
522 * @param deviceId {@link DeviceId}
523 * @param groupKey {@link GroupKey} to wait for.
524 * @param timeout timeout
525 * @param unit unit of timeout
526 * @return {@link Group}
527 * @throws IntentCompilationException on any error.
528 */
529 private Group waitForGroup(DeviceId deviceId, GroupKey groupKey, long timeout, TimeUnit unit) {
530 Group group = groupService.getGroup(deviceId, groupKey);
531 if (group != null) {
532 return group;
533 }
534
535 final CompletableFuture<Group> future = new CompletableFuture<>();
536 final GroupListener listener = event -> {
537 if (event.subject().deviceId() == deviceId &&
538 event.subject().appCookie().equals(groupKey)) {
539 future.complete(event.subject());
540 return;
541 }
542 };
543
544 groupService.addListener(listener);
545 try {
546 group = groupService.getGroup(deviceId, groupKey);
547 if (group != null) {
548 return group;
549 }
550 return future.get(timeout, unit);
551 } catch (InterruptedException e) {
552 log.debug("Interrupted", e);
553 Thread.currentThread().interrupt();
554 throw new IntentCompilationException("Interrupted", e);
555 } catch (ExecutionException e) {
556 log.debug("ExecutionException", e);
557 throw new IntentCompilationException("ExecutionException caught", e);
558 } catch (TimeoutException e) {
559 // one last try
560 group = groupService.getGroup(deviceId, groupKey);
561 if (group != null) {
562 return group;
563 } else {
564 log.debug("Timeout", e);
565 throw new IntentCompilationException("Timeout", e);
566 }
567 } finally {
568 groupService.removeListener(listener);
569 }
570 }
571
helenyrwu2a674902016-07-20 09:48:04 -0700572 private TrafficTreatment buildFailoverTreatment(DeviceId srcDevice,
573 GroupKey groupKey) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700574 Group group = waitForGroup(srcDevice, groupKey);
helenyrwu2a674902016-07-20 09:48:04 -0700575 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
576 TrafficTreatment trafficTreatment = tBuilder.group(group.id()).build();
577 return trafficTreatment;
578 }
579
580 /**
581 * Deletes intents from the given list if the ports or links the intent
582 * relies on are no longer viable. The failover flow rule intent is never
583 * deleted -- only its contents are updated.
584 *
585 * @param oldInstallables list of intents to examine
586 * @return list of reusable installable intents
587 */
588 private List<Intent> filterInvalidSubIntents(List<Intent> oldInstallables,
589 PointToPointIntent pointIntent) {
590 List<Intent> intentList = new ArrayList<>();
591 intentList.addAll(oldInstallables);
592 erasePrimary = false;
593 eraseBackup = false;
594 if (intentList != null) {
595 Iterator<Intent> iterator = intentList.iterator();
596 while (iterator.hasNext() && !(erasePrimary && eraseBackup)) {
597 Intent intent = iterator.next();
598 intent.resources().forEach(resource -> {
599 if (resource instanceof Link) {
600 Link link = (Link) resource;
601 if (link.state() == Link.State.INACTIVE) {
602 setPathsToRemove(intent);
603 } else if (link instanceof EdgeLink) {
604 ConnectPoint connectPoint = (link.src().elementId() instanceof DeviceId)
605 ? link.src() : link.dst();
606 Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
607 if (port == null || !port.isEnabled()) {
608 setPathsToRemove(intent);
609 }
610 } else {
611 Port port1 = deviceService.getPort(link.src().deviceId(), link.src().port());
612 Port port2 = deviceService.getPort(link.dst().deviceId(), link.dst().port());
613 if (port1 == null || !port1.isEnabled() || port2 == null || !port2.isEnabled()) {
614 setPathsToRemove(intent);
615 }
616 }
617 }
618 });
619 }
620 removeAndUpdateIntents(intentList, pointIntent);
621 }
622 return intentList;
623 }
624
625 /**
626 * Sets instance variables erasePrimary and eraseBackup. If erasePrimary,
627 * the primary path is no longer viable and related intents will be deleted.
628 * If eraseBackup, the backup path is no longer viable and related intents
629 * will be deleted.
630 *
631 * @param intent intent whose resources are found to be disabled/inactive:
632 * if intent is part of primary path, primary path set for removal;
633 * if intent is part of backup path, backup path set for removal;
634 * if bad intent is of type failover, the ingress point is down,
635 * and both paths are rendered inactive.
636 * @return true if both primary and backup paths are to be removed
637 */
638 private boolean setPathsToRemove(Intent intent) {
639 if (intent instanceof FlowRuleIntent) {
640 FlowRuleIntent frIntent = (FlowRuleIntent) intent;
641 PathIntent.ProtectionType type = frIntent.type();
642 if (type == PathIntent.ProtectionType.PRIMARY || type == PathIntent.ProtectionType.FAILOVER) {
643 erasePrimary = true;
644 }
645 if (type == PathIntent.ProtectionType.BACKUP || type == PathIntent.ProtectionType.FAILOVER) {
646 eraseBackup = true;
647 }
648 }
649 return erasePrimary && eraseBackup;
650 }
651
652 /**
653 * Removes intents from installables list, depending on the values
654 * of instance variables erasePrimary and eraseBackup. Flow rule intents
655 * that contain the manufactured fast failover flow rules are never deleted.
656 * The contents are simply modified as necessary. If cleanUpIntents size
657 * is greater than 1 (failover intent), then one whole path from previous
658 * installables must be still viable.
659 *
660 * @param cleanUpIntents list of installable intents
661 */
662 private void removeAndUpdateIntents(List<Intent> cleanUpIntents,
663 PointToPointIntent pointIntent) {
664 ListIterator<Intent> iterator = cleanUpIntents.listIterator();
665 while (iterator.hasNext()) {
666 Intent cIntent = iterator.next();
667 if (cIntent instanceof FlowRuleIntent) {
668 FlowRuleIntent fIntent = (FlowRuleIntent) cIntent;
669 if (fIntent.type() == PathIntent.ProtectionType.PRIMARY && erasePrimary) {
670 // remove primary path's flow rule intents
671 iterator.remove();
672 } else if (fIntent.type() == PathIntent.ProtectionType.BACKUP && eraseBackup) {
673 //remove backup path's flow rule intents
674 iterator.remove();
675 } else if (fIntent.type() == PathIntent.ProtectionType.BACKUP && erasePrimary) {
676 // promote backup path's flow rule intents to primary
677 iterator.set(new FlowRuleIntent(fIntent, PathIntent.ProtectionType.PRIMARY));
678 }
679 }
680 }
681 // remove buckets whose watchports are disabled if the failover group exists
Pier Ventreffe88d62016-10-13 14:34:40 -0700682 Group group = groupService.getGroup(pointIntent.filteredIngressPoint().connectPoint().deviceId(),
helenyrwu2a674902016-07-20 09:48:04 -0700683 makeGroupKey(pointIntent.id()));
684 if (group != null) {
685 updateFailoverGroup(pointIntent);
686 }
687 }
688
689 // Removes buckets whose treatments rely on disabled ports from the
690 // failover group.
691 private void updateFailoverGroup(PointToPointIntent pointIntent) {
Pier Ventreffe88d62016-10-13 14:34:40 -0700692 DeviceId deviceId = pointIntent.filteredIngressPoint().connectPoint().deviceId();
helenyrwu2a674902016-07-20 09:48:04 -0700693 GroupKey groupKey = makeGroupKey(pointIntent.id());
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700694 Group group = waitForGroup(deviceId, groupKey);
helenyrwu2a674902016-07-20 09:48:04 -0700695 Iterator<GroupBucket> groupIterator = group.buckets().buckets().iterator();
696 while (groupIterator.hasNext()) {
697 GroupBucket bucket = groupIterator.next();
698 Instruction individualInstruction = bucket.treatment().allInstructions().get(0);
699 if (individualInstruction instanceof Instructions.OutputInstruction) {
700 Instructions.OutputInstruction outInstruction =
701 (Instructions.OutputInstruction) individualInstruction;
702 Port port = deviceService.getPort(deviceId, outInstruction.port());
703 if (port == null || !port.isEnabled()) {
704 GroupBuckets removeBuckets = new GroupBuckets(Collections.singletonList(bucket));
705 groupService.removeBucketsFromGroup(deviceId, groupKey,
706 removeBuckets, groupKey,
707 pointIntent.appId());
708 }
709 }
710 }
711 }
712
713 // Adds failover group bucket with treatment outport determined by the
714 // ingress point of the links.
715 private void updateFailoverGroup(PointToPointIntent intent, List<Link> links) {
716 GroupKey groupKey = makeGroupKey(intent.id());
717
718 TrafficTreatment.Builder tBuilderIn = DefaultTrafficTreatment.builder();
719 ConnectPoint src = links.get(0).src();
720 tBuilderIn.setOutput(src.port());
721 GroupBucket bucket = DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn.build(), src.port(), null);
722 GroupBuckets addBuckets = new GroupBuckets(Collections.singletonList(bucket));
723
724 groupService.addBucketsToGroup(src.deviceId(), groupKey, addBuckets, groupKey, intent.appId());
725 }
Ray Milkeya058c732014-10-08 13:52:34 -0700726}