blob: ab4ec537435d025fb7e2925d0eb1a23e1f1dc015 [file] [log] [blame]
Charles Chanc7b3c452018-06-19 20:31:57 -07001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.segmentrouting.xconnect.impl;
17
Charles Chand5814aa2018-08-19 19:21:46 -070018import com.google.common.collect.ImmutableMap;
Charles Chanc7b3c452018-06-19 20:31:57 -070019import com.google.common.collect.ImmutableSet;
20import com.google.common.collect.Sets;
21import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
24import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
26import org.apache.felix.scr.annotations.Service;
27import org.onlab.packet.MacAddress;
28import org.onlab.packet.VlanId;
29import org.onlab.util.KryoNamespace;
30import org.onosproject.codec.CodecService;
31import org.onosproject.core.ApplicationId;
32import org.onosproject.core.CoreService;
33import org.onosproject.mastership.MastershipService;
34import org.onosproject.net.ConnectPoint;
35import org.onosproject.net.DeviceId;
36import org.onosproject.net.PortNumber;
37import org.onosproject.net.config.NetworkConfigService;
38import org.onosproject.net.device.DeviceEvent;
39import org.onosproject.net.device.DeviceListener;
40import org.onosproject.net.device.DeviceService;
41import org.onosproject.net.flow.DefaultTrafficSelector;
42import org.onosproject.net.flow.DefaultTrafficTreatment;
43import org.onosproject.net.flow.TrafficSelector;
44import org.onosproject.net.flow.TrafficTreatment;
45import org.onosproject.net.flow.criteria.Criteria;
46import org.onosproject.net.flowobjective.DefaultFilteringObjective;
47import org.onosproject.net.flowobjective.DefaultForwardingObjective;
48import org.onosproject.net.flowobjective.DefaultNextObjective;
49import org.onosproject.net.flowobjective.DefaultObjectiveContext;
50import org.onosproject.net.flowobjective.FilteringObjective;
51import org.onosproject.net.flowobjective.FlowObjectiveService;
52import org.onosproject.net.flowobjective.ForwardingObjective;
53import org.onosproject.net.flowobjective.NextObjective;
54import org.onosproject.net.flowobjective.Objective;
55import org.onosproject.net.flowobjective.ObjectiveContext;
56import org.onosproject.net.flowobjective.ObjectiveError;
57import org.onosproject.segmentrouting.SegmentRoutingService;
58import org.onosproject.segmentrouting.xconnect.api.XconnectCodec;
59import org.onosproject.segmentrouting.xconnect.api.XconnectDesc;
60import org.onosproject.segmentrouting.xconnect.api.XconnectKey;
61import org.onosproject.segmentrouting.xconnect.api.XconnectService;
62import org.onosproject.store.serializers.KryoNamespaces;
63import org.onosproject.store.service.ConsistentMap;
64import org.onosproject.store.service.MapEvent;
65import org.onosproject.store.service.MapEventListener;
66import org.onosproject.store.service.Serializer;
67import org.onosproject.store.service.StorageService;
68import org.onosproject.store.service.Versioned;
69import org.slf4j.Logger;
70import org.slf4j.LoggerFactory;
71
72import java.io.Serializable;
73import java.util.Set;
74import java.util.concurrent.CompletableFuture;
Charles Chan168111e2018-08-07 12:48:36 -070075import java.util.concurrent.ExecutorService;
76import java.util.concurrent.Executors;
Charles Chanc7b3c452018-06-19 20:31:57 -070077import java.util.function.BiConsumer;
78import java.util.function.Consumer;
79import java.util.stream.Collectors;
80
Charles Chan168111e2018-08-07 12:48:36 -070081import static org.onlab.util.Tools.groupedThreads;
82
Charles Chanc7b3c452018-06-19 20:31:57 -070083@Service
84@Component(immediate = true)
85public class XconnectManager implements XconnectService {
86 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 private CoreService coreService;
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 private CodecService codecService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 private StorageService storageService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 public NetworkConfigService netCfgService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 public DeviceService deviceService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 public FlowObjectiveService flowObjectiveService;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 public MastershipService mastershipService;
106
107 @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY)
108 public SegmentRoutingService srService;
109
110 private static final String APP_NAME = "org.onosproject.xconnect";
111 private static final String ERROR_NOT_MASTER = "Not master controller";
112
113 private static Logger log = LoggerFactory.getLogger(XconnectManager.class);
114
115 private ApplicationId appId;
116 private ConsistentMap<XconnectKey, Set<PortNumber>> xconnectStore;
117 private ConsistentMap<XconnectKey, NextObjective> xconnectNextObjStore;
118
119 private final MapEventListener<XconnectKey, Set<PortNumber>> xconnectListener = new XconnectMapListener();
120 private final DeviceListener deviceListener = new InternalDeviceListener();
121
Charles Chan168111e2018-08-07 12:48:36 -0700122 private ExecutorService deviceEventExecutor;
123
Charles Chanc7b3c452018-06-19 20:31:57 -0700124 @Activate
125 void activate() {
126 appId = coreService.registerApplication(APP_NAME);
127 codecService.registerCodec(XconnectDesc.class, new XconnectCodec());
128
129 KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
130 .register(KryoNamespaces.API)
Charles Chan871d9182018-07-23 12:53:16 -0700131 .register(XconnectManager.class)
Charles Chanc7b3c452018-06-19 20:31:57 -0700132 .register(XconnectKey.class);
133
134 xconnectStore = storageService.<XconnectKey, Set<PortNumber>>consistentMapBuilder()
135 .withName("onos-sr-xconnect")
136 .withRelaxedReadConsistency()
137 .withSerializer(Serializer.using(serializer.build()))
138 .build();
139 xconnectStore.addListener(xconnectListener);
140
141 xconnectNextObjStore = storageService.<XconnectKey, NextObjective>consistentMapBuilder()
142 .withName("onos-sr-xconnect-next")
143 .withRelaxedReadConsistency()
144 .withSerializer(Serializer.using(serializer.build()))
145 .build();
146
Charles Chan168111e2018-08-07 12:48:36 -0700147 deviceEventExecutor = Executors.newSingleThreadScheduledExecutor(
148 groupedThreads("sr-xconnect-device-event", "%d", log));
149
Charles Chanc7b3c452018-06-19 20:31:57 -0700150 deviceService.addListener(deviceListener);
151
152 log.info("Started");
153 }
154
155 @Deactivate
156 void deactivate() {
157 xconnectStore.removeListener(xconnectListener);
158 deviceService.removeListener(deviceListener);
159 codecService.unregisterCodec(XconnectDesc.class);
160
Charles Chan168111e2018-08-07 12:48:36 -0700161 deviceEventExecutor.shutdown();
162
Charles Chanc7b3c452018-06-19 20:31:57 -0700163 log.info("Stopped");
164 }
165
166 @Override
167 public void addOrUpdateXconnect(DeviceId deviceId, VlanId vlanId, Set<PortNumber> ports) {
168 log.info("Adding or updating xconnect. deviceId={}, vlanId={}, ports={}",
169 deviceId, vlanId, ports);
170 final XconnectKey key = new XconnectKey(deviceId, vlanId);
171 xconnectStore.put(key, ports);
172 }
173
174 @Override
175 public void removeXonnect(DeviceId deviceId, VlanId vlanId) {
176 log.info("Removing xconnect. deviceId={}, vlanId={}",
177 deviceId, vlanId);
178 final XconnectKey key = new XconnectKey(deviceId, vlanId);
179 xconnectStore.remove(key);
180 }
181
182 @Override
183 public Set<XconnectDesc> getXconnects() {
184 return xconnectStore.asJavaMap().entrySet().stream()
185 .map(e -> new XconnectDesc(e.getKey(), e.getValue()))
186 .collect(Collectors.toSet());
187 }
188
189 @Override
190 public boolean hasXconnect(ConnectPoint cp) {
191 return getXconnects().stream().anyMatch(desc ->
192 desc.key().deviceId().equals(cp.deviceId()) && desc.ports().contains(cp.port())
193 );
194 }
195
Charles Chand5814aa2018-08-19 19:21:46 -0700196 @Override
197 public ImmutableMap<XconnectKey, NextObjective> getNext() {
198 if (xconnectNextObjStore != null) {
199 return ImmutableMap.copyOf(xconnectNextObjStore.asJavaMap());
200 } else {
201 return ImmutableMap.of();
202 }
203 }
204
205 @Override
206 public void removeNextId(int nextId) {
207 xconnectNextObjStore.entrySet().forEach(e -> {
208 if (e.getValue().value().id() == nextId) {
209 xconnectNextObjStore.remove(e.getKey());
210 }
211 });
212 }
213
Charles Chanc7b3c452018-06-19 20:31:57 -0700214 private class XconnectMapListener implements MapEventListener<XconnectKey, Set<PortNumber>> {
215 @Override
216 public void event(MapEvent<XconnectKey, Set<PortNumber>> event) {
217 XconnectKey key = event.key();
218 Versioned<Set<PortNumber>> ports = event.newValue();
219 Versioned<Set<PortNumber>> oldPorts = event.oldValue();
220
221 switch (event.type()) {
222 case INSERT:
223 populateXConnect(key, ports.value());
224 break;
225 case UPDATE:
226 updateXConnect(key, oldPorts.value(), ports.value());
227 break;
228 case REMOVE:
229 revokeXConnect(key, oldPorts.value());
230 break;
231 default:
232 break;
233 }
234 }
235 }
236
237 private class InternalDeviceListener implements DeviceListener {
238 @Override
239 public void event(DeviceEvent event) {
Charles Chan168111e2018-08-07 12:48:36 -0700240 deviceEventExecutor.execute(() -> {
241 DeviceId deviceId = event.subject().id();
242 if (!mastershipService.isLocalMaster(deviceId)) {
243 return;
244 }
Charles Chanc7b3c452018-06-19 20:31:57 -0700245
Charles Chan168111e2018-08-07 12:48:36 -0700246 switch (event.type()) {
247 case DEVICE_ADDED:
248 case DEVICE_AVAILABILITY_CHANGED:
249 case DEVICE_UPDATED:
250 if (deviceService.isAvailable(deviceId)) {
251 init(deviceId);
252 } else {
253 cleanup(deviceId);
254 }
255 break;
256 default:
257 break;
258 }
259 });
Charles Chanc7b3c452018-06-19 20:31:57 -0700260 }
261 }
262
263 void init(DeviceId deviceId) {
264 getXconnects().stream()
265 .filter(desc -> desc.key().deviceId().equals(deviceId))
266 .forEach(desc -> populateXConnect(desc.key(), desc.ports()));
267 }
268
269 void cleanup(DeviceId deviceId) {
270 xconnectNextObjStore.entrySet().stream()
271 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
272 .forEach(entry -> xconnectNextObjStore.remove(entry.getKey()));
273 log.debug("{} is removed from xConnectNextObjStore", deviceId);
274 }
275
276 /**
277 * Populates XConnect groups and flows for given key.
278 *
279 * @param key XConnect key
280 * @param ports a set of ports to be cross-connected
281 */
282 private void populateXConnect(XconnectKey key, Set<PortNumber> ports) {
283 if (!mastershipService.isLocalMaster(key.deviceId())) {
284 log.info("Abort populating XConnect {}: {}", key, ERROR_NOT_MASTER);
285 return;
286 }
287
288 ports = addPairPort(key.deviceId(), ports);
289 populateFilter(key, ports);
290 populateFwd(key, populateNext(key, ports));
291 populateAcl(key);
292 }
293
294 /**
295 * Populates filtering objectives for given XConnect.
296 *
297 * @param key XConnect store key
298 * @param ports XConnect ports
299 */
300 private void populateFilter(XconnectKey key, Set<PortNumber> ports) {
301 ports.forEach(port -> {
302 FilteringObjective.Builder filtObjBuilder = filterObjBuilder(key, port);
303 ObjectiveContext context = new DefaultObjectiveContext(
304 (objective) -> log.debug("XConnect FilterObj for {} on port {} populated",
305 key, port),
306 (objective, error) ->
307 log.warn("Failed to populate XConnect FilterObj for {} on port {}: {}",
308 key, port, error));
309 flowObjectiveService.filter(key.deviceId(), filtObjBuilder.add(context));
310 });
311 }
312
313 /**
314 * Populates next objectives for given XConnect.
315 *
316 * @param key XConnect store key
317 * @param ports XConnect ports
318 */
319 private NextObjective populateNext(XconnectKey key, Set<PortNumber> ports) {
320 NextObjective nextObj;
321 if (xconnectNextObjStore.containsKey(key)) {
322 nextObj = xconnectNextObjStore.get(key).value();
323 log.debug("NextObj for {} found, id={}", key, nextObj.id());
324 } else {
325 NextObjective.Builder nextObjBuilder = nextObjBuilder(key, ports);
326 ObjectiveContext nextContext = new DefaultObjectiveContext(
327 // To serialize this with kryo
328 (Serializable & Consumer<Objective>) (objective) ->
329 log.debug("XConnect NextObj for {} added", key),
330 (Serializable & BiConsumer<Objective, ObjectiveError>) (objective, error) ->
331 log.warn("Failed to add XConnect NextObj for {}: {}", key, error)
332 );
333 nextObj = nextObjBuilder.add(nextContext);
334 flowObjectiveService.next(key.deviceId(), nextObj);
335 xconnectNextObjStore.put(key, nextObj);
336 log.debug("NextObj for {} not found. Creating new NextObj with id={}", key, nextObj.id());
337 }
338 return nextObj;
339 }
340
341 /**
342 * Populates bridging forwarding objectives for given XConnect.
343 *
344 * @param key XConnect store key
345 * @param nextObj next objective
346 */
347 private void populateFwd(XconnectKey key, NextObjective nextObj) {
348 ForwardingObjective.Builder fwdObjBuilder = fwdObjBuilder(key, nextObj.id());
349 ObjectiveContext fwdContext = new DefaultObjectiveContext(
350 (objective) -> log.debug("XConnect FwdObj for {} populated", key),
351 (objective, error) ->
352 log.warn("Failed to populate XConnect FwdObj for {}: {}", key, error));
353 flowObjectiveService.forward(key.deviceId(), fwdObjBuilder.add(fwdContext));
354 }
355
356 /**
357 * Populates ACL forwarding objectives for given XConnect.
358 *
359 * @param key XConnect store key
360 */
361 private void populateAcl(XconnectKey key) {
362 ForwardingObjective.Builder aclObjBuilder = aclObjBuilder(key.vlanId());
363 ObjectiveContext aclContext = new DefaultObjectiveContext(
364 (objective) -> log.debug("XConnect AclObj for {} populated", key),
365 (objective, error) ->
366 log.warn("Failed to populate XConnect AclObj for {}: {}", key, error));
367 flowObjectiveService.forward(key.deviceId(), aclObjBuilder.add(aclContext));
368 }
369
370 /**
371 * Revokes XConnect groups and flows for given key.
372 *
373 * @param key XConnect key
374 * @param ports XConnect ports
375 */
376 private void revokeXConnect(XconnectKey key, Set<PortNumber> ports) {
377 if (!mastershipService.isLocalMaster(key.deviceId())) {
378 log.info("Abort populating XConnect {}: {}", key, ERROR_NOT_MASTER);
379 return;
380 }
381
382 ports = addPairPort(key.deviceId(), ports);
383 revokeFilter(key, ports);
384 if (xconnectNextObjStore.containsKey(key)) {
385 NextObjective nextObj = xconnectNextObjStore.get(key).value();
386 revokeFwd(key, nextObj, null);
387 revokeNext(key, nextObj, null);
388 } else {
389 log.warn("NextObj for {} does not exist in the store.", key);
390 }
391 revokeAcl(key);
392 }
393
394 /**
395 * Revokes filtering objectives for given XConnect.
396 *
397 * @param key XConnect store key
398 * @param ports XConnect ports
399 */
400 private void revokeFilter(XconnectKey key, Set<PortNumber> ports) {
401 ports.forEach(port -> {
402 FilteringObjective.Builder filtObjBuilder = filterObjBuilder(key, port);
403 ObjectiveContext context = new DefaultObjectiveContext(
404 (objective) -> log.debug("XConnect FilterObj for {} on port {} revoked",
405 key, port),
406 (objective, error) ->
407 log.warn("Failed to revoke XConnect FilterObj for {} on port {}: {}",
408 key, port, error));
409 flowObjectiveService.filter(key.deviceId(), filtObjBuilder.remove(context));
410 });
411 }
412
413 /**
414 * Revokes next objectives for given XConnect.
415 *
416 * @param key XConnect store key
417 * @param nextObj next objective
418 * @param nextFuture completable future for this next objective operation
419 */
420 private void revokeNext(XconnectKey key, NextObjective nextObj,
421 CompletableFuture<ObjectiveError> nextFuture) {
422 ObjectiveContext context = new ObjectiveContext() {
423 @Override
424 public void onSuccess(Objective objective) {
425 log.debug("Previous NextObj for {} removed", key);
426 if (nextFuture != null) {
427 nextFuture.complete(null);
428 }
429 }
430
431 @Override
432 public void onError(Objective objective, ObjectiveError error) {
433 log.warn("Failed to remove previous NextObj for {}: {}", key, error);
434 if (nextFuture != null) {
435 nextFuture.complete(error);
436 }
437 }
438 };
439 flowObjectiveService.next(key.deviceId(),
440 (NextObjective) nextObj.copy().remove(context));
441 xconnectNextObjStore.remove(key);
442 }
443
444 /**
445 * Revokes bridging forwarding objectives for given XConnect.
446 *
447 * @param key XConnect store key
448 * @param nextObj next objective
449 * @param fwdFuture completable future for this forwarding objective operation
450 */
451 private void revokeFwd(XconnectKey key, NextObjective nextObj,
452 CompletableFuture<ObjectiveError> fwdFuture) {
453 ForwardingObjective.Builder fwdObjBuilder = fwdObjBuilder(key, nextObj.id());
454 ObjectiveContext context = new ObjectiveContext() {
455 @Override
456 public void onSuccess(Objective objective) {
457 log.debug("Previous FwdObj for {} removed", key);
458 if (fwdFuture != null) {
459 fwdFuture.complete(null);
460 }
461 }
462
463 @Override
464 public void onError(Objective objective, ObjectiveError error) {
465 log.warn("Failed to remove previous FwdObj for {}: {}", key, error);
466 if (fwdFuture != null) {
467 fwdFuture.complete(error);
468 }
469 }
470 };
471 flowObjectiveService.forward(key.deviceId(), fwdObjBuilder.remove(context));
472 }
473
474 /**
475 * Revokes ACL forwarding objectives for given XConnect.
476 *
477 * @param key XConnect store key
478 */
479 private void revokeAcl(XconnectKey key) {
480 ForwardingObjective.Builder aclObjBuilder = aclObjBuilder(key.vlanId());
481 ObjectiveContext aclContext = new DefaultObjectiveContext(
482 (objective) -> log.debug("XConnect AclObj for {} populated", key),
483 (objective, error) ->
484 log.warn("Failed to populate XConnect AclObj for {}: {}", key, error));
485 flowObjectiveService.forward(key.deviceId(), aclObjBuilder.remove(aclContext));
486 }
487
488 /**
489 * Updates XConnect groups and flows for given key.
490 *
491 * @param key XConnect key
492 * @param prevPorts previous XConnect ports
493 * @param ports new XConnect ports
494 */
495 private void updateXConnect(XconnectKey key, Set<PortNumber> prevPorts,
496 Set<PortNumber> ports) {
497 // NOTE: ACL flow doesn't include port information. No need to update it.
498 // Pair port is built-in and thus not going to change. No need to update it.
499
500 // remove old filter
501 prevPorts.stream().filter(port -> !ports.contains(port)).forEach(port ->
502 revokeFilter(key, ImmutableSet.of(port)));
503 // install new filter
504 ports.stream().filter(port -> !prevPorts.contains(port)).forEach(port ->
505 populateFilter(key, ImmutableSet.of(port)));
506
507 CompletableFuture<ObjectiveError> fwdFuture = new CompletableFuture<>();
508 CompletableFuture<ObjectiveError> nextFuture = new CompletableFuture<>();
509
510 if (xconnectNextObjStore.containsKey(key)) {
511 NextObjective nextObj = xconnectNextObjStore.get(key).value();
512 revokeFwd(key, nextObj, fwdFuture);
513
514 fwdFuture.thenAcceptAsync(fwdStatus -> {
515 if (fwdStatus == null) {
516 log.debug("Fwd removed. Now remove group {}", key);
517 revokeNext(key, nextObj, nextFuture);
518 }
519 });
520
521 nextFuture.thenAcceptAsync(nextStatus -> {
522 if (nextStatus == null) {
523 log.debug("Installing new group and flow for {}", key);
524 populateFwd(key, populateNext(key, ports));
525 }
526 });
527 } else {
528 log.warn("NextObj for {} does not exist in the store.", key);
529 }
530 }
531
532 /**
533 * Creates a next objective builder for XConnect.
534 *
535 * @param key XConnect key
536 * @param ports set of XConnect ports
537 * @return next objective builder
538 */
539 private NextObjective.Builder nextObjBuilder(XconnectKey key, Set<PortNumber> ports) {
540 int nextId = flowObjectiveService.allocateNextId();
541 TrafficSelector metadata =
542 DefaultTrafficSelector.builder().matchVlanId(key.vlanId()).build();
543 NextObjective.Builder nextObjBuilder = DefaultNextObjective
544 .builder().withId(nextId)
545 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
546 .withMeta(metadata);
547 ports.forEach(port -> {
548 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
549 tBuilder.setOutput(port);
550 nextObjBuilder.addTreatment(tBuilder.build());
551 });
552 return nextObjBuilder;
553 }
554
555 /**
556 * Creates a bridging forwarding objective builder for XConnect.
557 *
558 * @param key XConnect key
559 * @param nextId next ID of the broadcast group for this XConnect key
560 * @return forwarding objective builder
561 */
562 private ForwardingObjective.Builder fwdObjBuilder(XconnectKey key, int nextId) {
563 /*
564 * Driver should treat objectives with MacAddress.NONE and !VlanId.NONE
565 * as the VLAN cross-connect broadcast rules
566 */
567 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
568 sbuilder.matchVlanId(key.vlanId());
569 sbuilder.matchEthDst(MacAddress.NONE);
570
571 ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
572 fob.withFlag(ForwardingObjective.Flag.SPECIFIC)
573 .withSelector(sbuilder.build())
574 .nextStep(nextId)
575 .withPriority(XCONNECT_PRIORITY)
576 .fromApp(appId)
577 .makePermanent();
578 return fob;
579 }
580
581 /**
582 * Creates an ACL forwarding objective builder for XConnect.
583 *
584 * @param vlanId cross connect VLAN id
585 * @return forwarding objective builder
586 */
587 private ForwardingObjective.Builder aclObjBuilder(VlanId vlanId) {
588 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
589 sbuilder.matchVlanId(vlanId);
590
591 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
592
593 ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
594 fob.withFlag(ForwardingObjective.Flag.VERSATILE)
595 .withSelector(sbuilder.build())
596 .withTreatment(tbuilder.build())
597 .withPriority(XCONNECT_ACL_PRIORITY)
598 .fromApp(appId)
599 .makePermanent();
600 return fob;
601 }
602
603 /**
604 * Creates a filtering objective builder for XConnect.
605 *
606 * @param key XConnect key
607 * @param port XConnect ports
608 * @return next objective builder
609 */
610 private FilteringObjective.Builder filterObjBuilder(XconnectKey key, PortNumber port) {
611 FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
612 fob.withKey(Criteria.matchInPort(port))
613 .addCondition(Criteria.matchVlanId(key.vlanId()))
614 .addCondition(Criteria.matchEthDst(MacAddress.NONE))
615 .withPriority(XCONNECT_PRIORITY);
616 return fob.permit().fromApp(appId);
617 }
618
619 /**
620 * Add pair port to the given set of port.
621 *
622 * @param deviceId device Id
623 * @param ports ports specified in the xconnect config
624 * @return port specified in the xconnect config plus the pair port (if configured)
625 */
626 private Set<PortNumber> addPairPort(DeviceId deviceId, Set<PortNumber> ports) {
627 if (srService == null) {
628 return ports;
629 }
630 Set<PortNumber> newPorts = Sets.newHashSet(ports);
631 srService.getPairLocalPort(deviceId).ifPresent(newPorts::add);
632 return newPorts;
633 }
Charles Chanc7b3c452018-06-19 20:31:57 -0700634}