blob: a7eb6649c98709ba2a00b050bbbf7b1726623da4 [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;
Charles Chan3e56d9f2018-09-21 11:29:12 -0700117 private ConsistentMap<XconnectKey, Integer> xconnectNextObjStore;
Charles Chanc7b3c452018-06-19 20:31:57 -0700118
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
Charles Chan3e56d9f2018-09-21 11:29:12 -0700141 xconnectNextObjStore = storageService.<XconnectKey, Integer>consistentMapBuilder()
Charles Chanc7b3c452018-06-19 20:31:57 -0700142 .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
Charles Chan3e56d9f2018-09-21 11:29:12 -0700197 public ImmutableMap<XconnectKey, Integer> getNext() {
Charles Chand5814aa2018-08-19 19:21:46 -0700198 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 -> {
Charles Chan3e56d9f2018-09-21 11:29:12 -0700208 if (e.getValue().value() == nextId) {
Charles Chand5814aa2018-08-19 19:21:46 -0700209 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
Charles Chan3e56d9f2018-09-21 11:29:12 -0700263 private void init(DeviceId deviceId) {
Charles Chanc7b3c452018-06-19 20:31:57 -0700264 getXconnects().stream()
265 .filter(desc -> desc.key().deviceId().equals(deviceId))
266 .forEach(desc -> populateXConnect(desc.key(), desc.ports()));
267 }
268
Charles Chan3e56d9f2018-09-21 11:29:12 -0700269 private void cleanup(DeviceId deviceId) {
Charles Chanc7b3c452018-06-19 20:31:57 -0700270 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 */
Charles Chan3e56d9f2018-09-21 11:29:12 -0700319 private int populateNext(XconnectKey key, Set<PortNumber> ports) {
Charles Chanc7b3c452018-06-19 20:31:57 -0700320 if (xconnectNextObjStore.containsKey(key)) {
Charles Chan3e56d9f2018-09-21 11:29:12 -0700321 int nextId = xconnectNextObjStore.get(key).value();
322 log.debug("NextObj for {} found, id={}", key, nextId);
323 return nextId;
Charles Chanc7b3c452018-06-19 20:31:57 -0700324 } 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),
Charles Chan55b806f2018-08-23 14:30:33 -0700330 (Serializable & BiConsumer<Objective, ObjectiveError>) (objective, error) -> {
331 log.warn("Failed to add XConnect NextObj for {}: {}", key, error);
332 srService.invalidateNextObj(objective.id());
333 });
Charles Chan3e56d9f2018-09-21 11:29:12 -0700334 NextObjective nextObj = nextObjBuilder.add(nextContext);
Charles Chanc7b3c452018-06-19 20:31:57 -0700335 flowObjectiveService.next(key.deviceId(), nextObj);
Charles Chan3e56d9f2018-09-21 11:29:12 -0700336 xconnectNextObjStore.put(key, nextObj.id());
Charles Chanc7b3c452018-06-19 20:31:57 -0700337 log.debug("NextObj for {} not found. Creating new NextObj with id={}", key, nextObj.id());
Charles Chan3e56d9f2018-09-21 11:29:12 -0700338 return nextObj.id();
Charles Chanc7b3c452018-06-19 20:31:57 -0700339 }
Charles Chanc7b3c452018-06-19 20:31:57 -0700340 }
341
342 /**
343 * Populates bridging forwarding objectives for given XConnect.
344 *
345 * @param key XConnect store key
Charles Chan3e56d9f2018-09-21 11:29:12 -0700346 * @param nextId next objective id
Charles Chanc7b3c452018-06-19 20:31:57 -0700347 */
Charles Chan3e56d9f2018-09-21 11:29:12 -0700348 private void populateFwd(XconnectKey key, int nextId) {
349 ForwardingObjective.Builder fwdObjBuilder = fwdObjBuilder(key, nextId);
Charles Chanc7b3c452018-06-19 20:31:57 -0700350 ObjectiveContext fwdContext = new DefaultObjectiveContext(
351 (objective) -> log.debug("XConnect FwdObj for {} populated", key),
352 (objective, error) ->
353 log.warn("Failed to populate XConnect FwdObj for {}: {}", key, error));
354 flowObjectiveService.forward(key.deviceId(), fwdObjBuilder.add(fwdContext));
355 }
356
357 /**
358 * Populates ACL forwarding objectives for given XConnect.
359 *
360 * @param key XConnect store key
361 */
362 private void populateAcl(XconnectKey key) {
363 ForwardingObjective.Builder aclObjBuilder = aclObjBuilder(key.vlanId());
364 ObjectiveContext aclContext = new DefaultObjectiveContext(
365 (objective) -> log.debug("XConnect AclObj for {} populated", key),
366 (objective, error) ->
367 log.warn("Failed to populate XConnect AclObj for {}: {}", key, error));
368 flowObjectiveService.forward(key.deviceId(), aclObjBuilder.add(aclContext));
369 }
370
371 /**
372 * Revokes XConnect groups and flows for given key.
373 *
374 * @param key XConnect key
375 * @param ports XConnect ports
376 */
377 private void revokeXConnect(XconnectKey key, Set<PortNumber> ports) {
378 if (!mastershipService.isLocalMaster(key.deviceId())) {
379 log.info("Abort populating XConnect {}: {}", key, ERROR_NOT_MASTER);
380 return;
381 }
382
383 ports = addPairPort(key.deviceId(), ports);
384 revokeFilter(key, ports);
385 if (xconnectNextObjStore.containsKey(key)) {
Charles Chan3e56d9f2018-09-21 11:29:12 -0700386 int nextId = xconnectNextObjStore.get(key).value();
387 revokeFwd(key, nextId, null);
388 revokeNext(key, ports, nextId, null);
Charles Chanc7b3c452018-06-19 20:31:57 -0700389 } else {
390 log.warn("NextObj for {} does not exist in the store.", key);
391 }
392 revokeAcl(key);
393 }
394
395 /**
396 * Revokes filtering objectives for given XConnect.
397 *
398 * @param key XConnect store key
399 * @param ports XConnect ports
400 */
401 private void revokeFilter(XconnectKey key, Set<PortNumber> ports) {
402 ports.forEach(port -> {
403 FilteringObjective.Builder filtObjBuilder = filterObjBuilder(key, port);
404 ObjectiveContext context = new DefaultObjectiveContext(
405 (objective) -> log.debug("XConnect FilterObj for {} on port {} revoked",
406 key, port),
407 (objective, error) ->
408 log.warn("Failed to revoke XConnect FilterObj for {} on port {}: {}",
409 key, port, error));
410 flowObjectiveService.filter(key.deviceId(), filtObjBuilder.remove(context));
411 });
412 }
413
414 /**
415 * Revokes next objectives for given XConnect.
416 *
417 * @param key XConnect store key
Charles Chan3e56d9f2018-09-21 11:29:12 -0700418 * @param ports ports in the XConnect
419 * @param nextId next objective id
Charles Chanc7b3c452018-06-19 20:31:57 -0700420 * @param nextFuture completable future for this next objective operation
421 */
Charles Chan3e56d9f2018-09-21 11:29:12 -0700422 private void revokeNext(XconnectKey key, Set<PortNumber> ports, int nextId,
Charles Chanc7b3c452018-06-19 20:31:57 -0700423 CompletableFuture<ObjectiveError> nextFuture) {
424 ObjectiveContext context = new ObjectiveContext() {
425 @Override
426 public void onSuccess(Objective objective) {
427 log.debug("Previous NextObj for {} removed", key);
428 if (nextFuture != null) {
429 nextFuture.complete(null);
430 }
431 }
432
433 @Override
434 public void onError(Objective objective, ObjectiveError error) {
435 log.warn("Failed to remove previous NextObj for {}: {}", key, error);
436 if (nextFuture != null) {
437 nextFuture.complete(error);
438 }
Charles Chan55b806f2018-08-23 14:30:33 -0700439 srService.invalidateNextObj(objective.id());
Charles Chanc7b3c452018-06-19 20:31:57 -0700440 }
441 };
Charles Chan3e56d9f2018-09-21 11:29:12 -0700442 flowObjectiveService.next(key.deviceId(), nextObjBuilder(key, ports, nextId).remove(context));
Charles Chanc7b3c452018-06-19 20:31:57 -0700443 xconnectNextObjStore.remove(key);
444 }
445
446 /**
447 * Revokes bridging forwarding objectives for given XConnect.
448 *
449 * @param key XConnect store key
Charles Chan3e56d9f2018-09-21 11:29:12 -0700450 * @param nextId next objective id
Charles Chanc7b3c452018-06-19 20:31:57 -0700451 * @param fwdFuture completable future for this forwarding objective operation
452 */
Charles Chan3e56d9f2018-09-21 11:29:12 -0700453 private void revokeFwd(XconnectKey key, int nextId, CompletableFuture<ObjectiveError> fwdFuture) {
454 ForwardingObjective.Builder fwdObjBuilder = fwdObjBuilder(key, nextId);
Charles Chanc7b3c452018-06-19 20:31:57 -0700455 ObjectiveContext context = new ObjectiveContext() {
456 @Override
457 public void onSuccess(Objective objective) {
458 log.debug("Previous FwdObj for {} removed", key);
459 if (fwdFuture != null) {
460 fwdFuture.complete(null);
461 }
462 }
463
464 @Override
465 public void onError(Objective objective, ObjectiveError error) {
466 log.warn("Failed to remove previous FwdObj for {}: {}", key, error);
467 if (fwdFuture != null) {
468 fwdFuture.complete(error);
469 }
470 }
471 };
472 flowObjectiveService.forward(key.deviceId(), fwdObjBuilder.remove(context));
473 }
474
475 /**
476 * Revokes ACL forwarding objectives for given XConnect.
477 *
478 * @param key XConnect store key
479 */
480 private void revokeAcl(XconnectKey key) {
481 ForwardingObjective.Builder aclObjBuilder = aclObjBuilder(key.vlanId());
482 ObjectiveContext aclContext = new DefaultObjectiveContext(
483 (objective) -> log.debug("XConnect AclObj for {} populated", key),
484 (objective, error) ->
485 log.warn("Failed to populate XConnect AclObj for {}: {}", key, error));
486 flowObjectiveService.forward(key.deviceId(), aclObjBuilder.remove(aclContext));
487 }
488
489 /**
490 * Updates XConnect groups and flows for given key.
491 *
492 * @param key XConnect key
493 * @param prevPorts previous XConnect ports
494 * @param ports new XConnect ports
495 */
496 private void updateXConnect(XconnectKey key, Set<PortNumber> prevPorts,
497 Set<PortNumber> ports) {
498 // NOTE: ACL flow doesn't include port information. No need to update it.
499 // Pair port is built-in and thus not going to change. No need to update it.
500
501 // remove old filter
502 prevPorts.stream().filter(port -> !ports.contains(port)).forEach(port ->
503 revokeFilter(key, ImmutableSet.of(port)));
504 // install new filter
505 ports.stream().filter(port -> !prevPorts.contains(port)).forEach(port ->
506 populateFilter(key, ImmutableSet.of(port)));
507
508 CompletableFuture<ObjectiveError> fwdFuture = new CompletableFuture<>();
509 CompletableFuture<ObjectiveError> nextFuture = new CompletableFuture<>();
510
511 if (xconnectNextObjStore.containsKey(key)) {
Charles Chan3e56d9f2018-09-21 11:29:12 -0700512 int nextId = xconnectNextObjStore.get(key).value();
513 revokeFwd(key, nextId, fwdFuture);
Charles Chanc7b3c452018-06-19 20:31:57 -0700514
515 fwdFuture.thenAcceptAsync(fwdStatus -> {
516 if (fwdStatus == null) {
517 log.debug("Fwd removed. Now remove group {}", key);
Charles Chan3e56d9f2018-09-21 11:29:12 -0700518 revokeNext(key, prevPorts, nextId, nextFuture);
Charles Chanc7b3c452018-06-19 20:31:57 -0700519 }
520 });
521
522 nextFuture.thenAcceptAsync(nextStatus -> {
523 if (nextStatus == null) {
524 log.debug("Installing new group and flow for {}", key);
525 populateFwd(key, populateNext(key, ports));
526 }
527 });
528 } else {
529 log.warn("NextObj for {} does not exist in the store.", key);
530 }
531 }
532
533 /**
Charles Chan3e56d9f2018-09-21 11:29:12 -0700534 * Creates a next objective builder for XConnect with given nextId.
Charles Chanc7b3c452018-06-19 20:31:57 -0700535 *
536 * @param key XConnect key
537 * @param ports set of XConnect ports
Charles Chan3e56d9f2018-09-21 11:29:12 -0700538 * @param nextId next objective id
Charles Chanc7b3c452018-06-19 20:31:57 -0700539 * @return next objective builder
540 */
Charles Chan3e56d9f2018-09-21 11:29:12 -0700541 private NextObjective.Builder nextObjBuilder(XconnectKey key, Set<PortNumber> ports, int nextId) {
Charles Chanc7b3c452018-06-19 20:31:57 -0700542 TrafficSelector metadata =
543 DefaultTrafficSelector.builder().matchVlanId(key.vlanId()).build();
544 NextObjective.Builder nextObjBuilder = DefaultNextObjective
545 .builder().withId(nextId)
546 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
547 .withMeta(metadata);
548 ports.forEach(port -> {
549 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
550 tBuilder.setOutput(port);
551 nextObjBuilder.addTreatment(tBuilder.build());
552 });
553 return nextObjBuilder;
554 }
555
556 /**
Charles Chan3e56d9f2018-09-21 11:29:12 -0700557 * Creates a next objective builder for XConnect.
558 *
559 * @param key XConnect key
560 * @param ports set of XConnect ports
561 * @return next objective builder
562 */
563 private NextObjective.Builder nextObjBuilder(XconnectKey key, Set<PortNumber> ports) {
564 int nextId = flowObjectiveService.allocateNextId();
565 return nextObjBuilder(key, ports, nextId);
566 }
567
568
569 /**
Charles Chanc7b3c452018-06-19 20:31:57 -0700570 * Creates a bridging forwarding objective builder for XConnect.
571 *
572 * @param key XConnect key
573 * @param nextId next ID of the broadcast group for this XConnect key
574 * @return forwarding objective builder
575 */
576 private ForwardingObjective.Builder fwdObjBuilder(XconnectKey key, int nextId) {
577 /*
578 * Driver should treat objectives with MacAddress.NONE and !VlanId.NONE
579 * as the VLAN cross-connect broadcast rules
580 */
581 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
582 sbuilder.matchVlanId(key.vlanId());
583 sbuilder.matchEthDst(MacAddress.NONE);
584
585 ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
586 fob.withFlag(ForwardingObjective.Flag.SPECIFIC)
587 .withSelector(sbuilder.build())
588 .nextStep(nextId)
589 .withPriority(XCONNECT_PRIORITY)
590 .fromApp(appId)
591 .makePermanent();
592 return fob;
593 }
594
595 /**
596 * Creates an ACL forwarding objective builder for XConnect.
597 *
598 * @param vlanId cross connect VLAN id
599 * @return forwarding objective builder
600 */
601 private ForwardingObjective.Builder aclObjBuilder(VlanId vlanId) {
602 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
603 sbuilder.matchVlanId(vlanId);
604
605 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
606
607 ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
608 fob.withFlag(ForwardingObjective.Flag.VERSATILE)
609 .withSelector(sbuilder.build())
610 .withTreatment(tbuilder.build())
611 .withPriority(XCONNECT_ACL_PRIORITY)
612 .fromApp(appId)
613 .makePermanent();
614 return fob;
615 }
616
617 /**
618 * Creates a filtering objective builder for XConnect.
619 *
620 * @param key XConnect key
621 * @param port XConnect ports
622 * @return next objective builder
623 */
624 private FilteringObjective.Builder filterObjBuilder(XconnectKey key, PortNumber port) {
625 FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
626 fob.withKey(Criteria.matchInPort(port))
627 .addCondition(Criteria.matchVlanId(key.vlanId()))
628 .addCondition(Criteria.matchEthDst(MacAddress.NONE))
629 .withPriority(XCONNECT_PRIORITY);
630 return fob.permit().fromApp(appId);
631 }
632
633 /**
634 * Add pair port to the given set of port.
635 *
636 * @param deviceId device Id
637 * @param ports ports specified in the xconnect config
638 * @return port specified in the xconnect config plus the pair port (if configured)
639 */
640 private Set<PortNumber> addPairPort(DeviceId deviceId, Set<PortNumber> ports) {
641 if (srService == null) {
642 return ports;
643 }
644 Set<PortNumber> newPorts = Sets.newHashSet(ports);
645 srService.getPairLocalPort(deviceId).ifPresent(newPorts::add);
646 return newPorts;
647 }
Charles Chanc7b3c452018-06-19 20:31:57 -0700648}