blob: 5e42e570d2133e4e0682cfc69a884eb407d24565 [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;
Charles Chanc7b3c452018-06-19 20:31:57 -070021import org.onlab.packet.MacAddress;
22import org.onlab.packet.VlanId;
23import org.onlab.util.KryoNamespace;
24import org.onosproject.codec.CodecService;
25import org.onosproject.core.ApplicationId;
26import org.onosproject.core.CoreService;
27import org.onosproject.mastership.MastershipService;
28import org.onosproject.net.ConnectPoint;
29import org.onosproject.net.DeviceId;
30import org.onosproject.net.PortNumber;
31import org.onosproject.net.config.NetworkConfigService;
32import org.onosproject.net.device.DeviceEvent;
33import org.onosproject.net.device.DeviceListener;
34import org.onosproject.net.device.DeviceService;
35import org.onosproject.net.flow.DefaultTrafficSelector;
36import org.onosproject.net.flow.DefaultTrafficTreatment;
37import org.onosproject.net.flow.TrafficSelector;
38import org.onosproject.net.flow.TrafficTreatment;
39import org.onosproject.net.flow.criteria.Criteria;
40import org.onosproject.net.flowobjective.DefaultFilteringObjective;
41import org.onosproject.net.flowobjective.DefaultForwardingObjective;
42import org.onosproject.net.flowobjective.DefaultNextObjective;
43import org.onosproject.net.flowobjective.DefaultObjectiveContext;
44import org.onosproject.net.flowobjective.FilteringObjective;
45import org.onosproject.net.flowobjective.FlowObjectiveService;
46import org.onosproject.net.flowobjective.ForwardingObjective;
47import org.onosproject.net.flowobjective.NextObjective;
48import org.onosproject.net.flowobjective.Objective;
49import org.onosproject.net.flowobjective.ObjectiveContext;
50import org.onosproject.net.flowobjective.ObjectiveError;
51import org.onosproject.segmentrouting.SegmentRoutingService;
52import org.onosproject.segmentrouting.xconnect.api.XconnectCodec;
53import org.onosproject.segmentrouting.xconnect.api.XconnectDesc;
54import org.onosproject.segmentrouting.xconnect.api.XconnectKey;
55import org.onosproject.segmentrouting.xconnect.api.XconnectService;
56import org.onosproject.store.serializers.KryoNamespaces;
57import org.onosproject.store.service.ConsistentMap;
58import org.onosproject.store.service.MapEvent;
59import org.onosproject.store.service.MapEventListener;
60import org.onosproject.store.service.Serializer;
61import org.onosproject.store.service.StorageService;
62import org.onosproject.store.service.Versioned;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070063import org.osgi.service.component.annotations.Activate;
64import org.osgi.service.component.annotations.Component;
65import org.osgi.service.component.annotations.Deactivate;
66import org.osgi.service.component.annotations.Reference;
67import org.osgi.service.component.annotations.ReferenceCardinality;
Charles Chanc7b3c452018-06-19 20:31:57 -070068import org.slf4j.Logger;
69import org.slf4j.LoggerFactory;
70
71import java.io.Serializable;
72import java.util.Set;
73import java.util.concurrent.CompletableFuture;
Charles Chan168111e2018-08-07 12:48:36 -070074import java.util.concurrent.ExecutorService;
75import java.util.concurrent.Executors;
Charles Chanc7b3c452018-06-19 20:31:57 -070076import java.util.function.BiConsumer;
77import java.util.function.Consumer;
78import java.util.stream.Collectors;
79
Charles Chan168111e2018-08-07 12:48:36 -070080import static org.onlab.util.Tools.groupedThreads;
81
Ray Milkeyd84f89b2018-08-17 14:54:17 -070082@Component(immediate = true, service = XconnectService.class)
Charles Chanc7b3c452018-06-19 20:31:57 -070083public class XconnectManager implements XconnectService {
Ray Milkeyd84f89b2018-08-17 14:54:17 -070084 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Charles Chanc7b3c452018-06-19 20:31:57 -070085 private CoreService coreService;
86
Ray Milkeyd84f89b2018-08-17 14:54:17 -070087 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Charles Chanc7b3c452018-06-19 20:31:57 -070088 private CodecService codecService;
89
Ray Milkeyd84f89b2018-08-17 14:54:17 -070090 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Charles Chanc7b3c452018-06-19 20:31:57 -070091 private StorageService storageService;
92
Ray Milkeyd84f89b2018-08-17 14:54:17 -070093 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Charles Chanc7b3c452018-06-19 20:31:57 -070094 public NetworkConfigService netCfgService;
95
Ray Milkeyd84f89b2018-08-17 14:54:17 -070096 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Charles Chanc7b3c452018-06-19 20:31:57 -070097 public DeviceService deviceService;
98
Ray Milkeyd84f89b2018-08-17 14:54:17 -070099 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Charles Chanc7b3c452018-06-19 20:31:57 -0700100 public FlowObjectiveService flowObjectiveService;
101
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700102 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Charles Chanc7b3c452018-06-19 20:31:57 -0700103 public MastershipService mastershipService;
104
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700105 @Reference(cardinality = ReferenceCardinality.OPTIONAL)
Charles Chanc7b3c452018-06-19 20:31:57 -0700106 public SegmentRoutingService srService;
107
108 private static final String APP_NAME = "org.onosproject.xconnect";
109 private static final String ERROR_NOT_MASTER = "Not master controller";
110
111 private static Logger log = LoggerFactory.getLogger(XconnectManager.class);
112
113 private ApplicationId appId;
114 private ConsistentMap<XconnectKey, Set<PortNumber>> xconnectStore;
Charles Chan3e56d9f2018-09-21 11:29:12 -0700115 private ConsistentMap<XconnectKey, Integer> xconnectNextObjStore;
Charles Chanc7b3c452018-06-19 20:31:57 -0700116
117 private final MapEventListener<XconnectKey, Set<PortNumber>> xconnectListener = new XconnectMapListener();
118 private final DeviceListener deviceListener = new InternalDeviceListener();
119
Charles Chan168111e2018-08-07 12:48:36 -0700120 private ExecutorService deviceEventExecutor;
121
Charles Chanc7b3c452018-06-19 20:31:57 -0700122 @Activate
123 void activate() {
124 appId = coreService.registerApplication(APP_NAME);
125 codecService.registerCodec(XconnectDesc.class, new XconnectCodec());
126
127 KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
128 .register(KryoNamespaces.API)
Charles Chan871d9182018-07-23 12:53:16 -0700129 .register(XconnectManager.class)
Charles Chanc7b3c452018-06-19 20:31:57 -0700130 .register(XconnectKey.class);
131
132 xconnectStore = storageService.<XconnectKey, Set<PortNumber>>consistentMapBuilder()
133 .withName("onos-sr-xconnect")
134 .withRelaxedReadConsistency()
135 .withSerializer(Serializer.using(serializer.build()))
136 .build();
137 xconnectStore.addListener(xconnectListener);
138
Charles Chan3e56d9f2018-09-21 11:29:12 -0700139 xconnectNextObjStore = storageService.<XconnectKey, Integer>consistentMapBuilder()
Charles Chanc7b3c452018-06-19 20:31:57 -0700140 .withName("onos-sr-xconnect-next")
141 .withRelaxedReadConsistency()
142 .withSerializer(Serializer.using(serializer.build()))
143 .build();
144
Charles Chan168111e2018-08-07 12:48:36 -0700145 deviceEventExecutor = Executors.newSingleThreadScheduledExecutor(
146 groupedThreads("sr-xconnect-device-event", "%d", log));
147
Charles Chanc7b3c452018-06-19 20:31:57 -0700148 deviceService.addListener(deviceListener);
149
150 log.info("Started");
151 }
152
153 @Deactivate
154 void deactivate() {
155 xconnectStore.removeListener(xconnectListener);
156 deviceService.removeListener(deviceListener);
157 codecService.unregisterCodec(XconnectDesc.class);
158
Charles Chan168111e2018-08-07 12:48:36 -0700159 deviceEventExecutor.shutdown();
160
Charles Chanc7b3c452018-06-19 20:31:57 -0700161 log.info("Stopped");
162 }
163
164 @Override
165 public void addOrUpdateXconnect(DeviceId deviceId, VlanId vlanId, Set<PortNumber> ports) {
166 log.info("Adding or updating xconnect. deviceId={}, vlanId={}, ports={}",
167 deviceId, vlanId, ports);
168 final XconnectKey key = new XconnectKey(deviceId, vlanId);
169 xconnectStore.put(key, ports);
170 }
171
172 @Override
173 public void removeXonnect(DeviceId deviceId, VlanId vlanId) {
174 log.info("Removing xconnect. deviceId={}, vlanId={}",
175 deviceId, vlanId);
176 final XconnectKey key = new XconnectKey(deviceId, vlanId);
177 xconnectStore.remove(key);
178 }
179
180 @Override
181 public Set<XconnectDesc> getXconnects() {
182 return xconnectStore.asJavaMap().entrySet().stream()
183 .map(e -> new XconnectDesc(e.getKey(), e.getValue()))
184 .collect(Collectors.toSet());
185 }
186
187 @Override
188 public boolean hasXconnect(ConnectPoint cp) {
189 return getXconnects().stream().anyMatch(desc ->
190 desc.key().deviceId().equals(cp.deviceId()) && desc.ports().contains(cp.port())
191 );
192 }
193
Charles Chand5814aa2018-08-19 19:21:46 -0700194 @Override
Charles Chan3e56d9f2018-09-21 11:29:12 -0700195 public ImmutableMap<XconnectKey, Integer> getNext() {
Charles Chand5814aa2018-08-19 19:21:46 -0700196 if (xconnectNextObjStore != null) {
197 return ImmutableMap.copyOf(xconnectNextObjStore.asJavaMap());
198 } else {
199 return ImmutableMap.of();
200 }
201 }
202
203 @Override
204 public void removeNextId(int nextId) {
205 xconnectNextObjStore.entrySet().forEach(e -> {
Charles Chan3e56d9f2018-09-21 11:29:12 -0700206 if (e.getValue().value() == nextId) {
Charles Chand5814aa2018-08-19 19:21:46 -0700207 xconnectNextObjStore.remove(e.getKey());
208 }
209 });
210 }
211
Charles Chanc7b3c452018-06-19 20:31:57 -0700212 private class XconnectMapListener implements MapEventListener<XconnectKey, Set<PortNumber>> {
213 @Override
214 public void event(MapEvent<XconnectKey, Set<PortNumber>> event) {
215 XconnectKey key = event.key();
216 Versioned<Set<PortNumber>> ports = event.newValue();
217 Versioned<Set<PortNumber>> oldPorts = event.oldValue();
218
219 switch (event.type()) {
220 case INSERT:
221 populateXConnect(key, ports.value());
222 break;
223 case UPDATE:
224 updateXConnect(key, oldPorts.value(), ports.value());
225 break;
226 case REMOVE:
227 revokeXConnect(key, oldPorts.value());
228 break;
229 default:
230 break;
231 }
232 }
233 }
234
235 private class InternalDeviceListener implements DeviceListener {
236 @Override
237 public void event(DeviceEvent event) {
Charles Chan168111e2018-08-07 12:48:36 -0700238 deviceEventExecutor.execute(() -> {
239 DeviceId deviceId = event.subject().id();
240 if (!mastershipService.isLocalMaster(deviceId)) {
241 return;
242 }
Charles Chanc7b3c452018-06-19 20:31:57 -0700243
Charles Chan168111e2018-08-07 12:48:36 -0700244 switch (event.type()) {
245 case DEVICE_ADDED:
246 case DEVICE_AVAILABILITY_CHANGED:
247 case DEVICE_UPDATED:
248 if (deviceService.isAvailable(deviceId)) {
249 init(deviceId);
250 } else {
251 cleanup(deviceId);
252 }
253 break;
254 default:
255 break;
256 }
257 });
Charles Chanc7b3c452018-06-19 20:31:57 -0700258 }
259 }
260
Charles Chan3e56d9f2018-09-21 11:29:12 -0700261 private void init(DeviceId deviceId) {
Charles Chanc7b3c452018-06-19 20:31:57 -0700262 getXconnects().stream()
263 .filter(desc -> desc.key().deviceId().equals(deviceId))
264 .forEach(desc -> populateXConnect(desc.key(), desc.ports()));
265 }
266
Charles Chan3e56d9f2018-09-21 11:29:12 -0700267 private void cleanup(DeviceId deviceId) {
Charles Chanc7b3c452018-06-19 20:31:57 -0700268 xconnectNextObjStore.entrySet().stream()
269 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
270 .forEach(entry -> xconnectNextObjStore.remove(entry.getKey()));
271 log.debug("{} is removed from xConnectNextObjStore", deviceId);
272 }
273
274 /**
275 * Populates XConnect groups and flows for given key.
276 *
277 * @param key XConnect key
278 * @param ports a set of ports to be cross-connected
279 */
280 private void populateXConnect(XconnectKey key, Set<PortNumber> ports) {
281 if (!mastershipService.isLocalMaster(key.deviceId())) {
282 log.info("Abort populating XConnect {}: {}", key, ERROR_NOT_MASTER);
283 return;
284 }
285
286 ports = addPairPort(key.deviceId(), ports);
287 populateFilter(key, ports);
288 populateFwd(key, populateNext(key, ports));
289 populateAcl(key);
290 }
291
292 /**
293 * Populates filtering objectives for given XConnect.
294 *
295 * @param key XConnect store key
296 * @param ports XConnect ports
297 */
298 private void populateFilter(XconnectKey key, Set<PortNumber> ports) {
299 ports.forEach(port -> {
300 FilteringObjective.Builder filtObjBuilder = filterObjBuilder(key, port);
301 ObjectiveContext context = new DefaultObjectiveContext(
302 (objective) -> log.debug("XConnect FilterObj for {} on port {} populated",
303 key, port),
304 (objective, error) ->
305 log.warn("Failed to populate XConnect FilterObj for {} on port {}: {}",
306 key, port, error));
307 flowObjectiveService.filter(key.deviceId(), filtObjBuilder.add(context));
308 });
309 }
310
311 /**
312 * Populates next objectives for given XConnect.
313 *
314 * @param key XConnect store key
315 * @param ports XConnect ports
316 */
Charles Chan3e56d9f2018-09-21 11:29:12 -0700317 private int populateNext(XconnectKey key, Set<PortNumber> ports) {
Charles Chanc7b3c452018-06-19 20:31:57 -0700318 if (xconnectNextObjStore.containsKey(key)) {
Charles Chan3e56d9f2018-09-21 11:29:12 -0700319 int nextId = xconnectNextObjStore.get(key).value();
320 log.debug("NextObj for {} found, id={}", key, nextId);
321 return nextId;
Charles Chanc7b3c452018-06-19 20:31:57 -0700322 } else {
323 NextObjective.Builder nextObjBuilder = nextObjBuilder(key, ports);
324 ObjectiveContext nextContext = new DefaultObjectiveContext(
325 // To serialize this with kryo
326 (Serializable & Consumer<Objective>) (objective) ->
327 log.debug("XConnect NextObj for {} added", key),
Charles Chan55b806f2018-08-23 14:30:33 -0700328 (Serializable & BiConsumer<Objective, ObjectiveError>) (objective, error) -> {
329 log.warn("Failed to add XConnect NextObj for {}: {}", key, error);
330 srService.invalidateNextObj(objective.id());
331 });
Charles Chan3e56d9f2018-09-21 11:29:12 -0700332 NextObjective nextObj = nextObjBuilder.add(nextContext);
Charles Chanc7b3c452018-06-19 20:31:57 -0700333 flowObjectiveService.next(key.deviceId(), nextObj);
Charles Chan3e56d9f2018-09-21 11:29:12 -0700334 xconnectNextObjStore.put(key, nextObj.id());
Charles Chanc7b3c452018-06-19 20:31:57 -0700335 log.debug("NextObj for {} not found. Creating new NextObj with id={}", key, nextObj.id());
Charles Chan3e56d9f2018-09-21 11:29:12 -0700336 return nextObj.id();
Charles Chanc7b3c452018-06-19 20:31:57 -0700337 }
Charles Chanc7b3c452018-06-19 20:31:57 -0700338 }
339
340 /**
341 * Populates bridging forwarding objectives for given XConnect.
342 *
343 * @param key XConnect store key
Charles Chan3e56d9f2018-09-21 11:29:12 -0700344 * @param nextId next objective id
Charles Chanc7b3c452018-06-19 20:31:57 -0700345 */
Charles Chan3e56d9f2018-09-21 11:29:12 -0700346 private void populateFwd(XconnectKey key, int nextId) {
347 ForwardingObjective.Builder fwdObjBuilder = fwdObjBuilder(key, nextId);
Charles Chanc7b3c452018-06-19 20:31:57 -0700348 ObjectiveContext fwdContext = new DefaultObjectiveContext(
349 (objective) -> log.debug("XConnect FwdObj for {} populated", key),
350 (objective, error) ->
351 log.warn("Failed to populate XConnect FwdObj for {}: {}", key, error));
352 flowObjectiveService.forward(key.deviceId(), fwdObjBuilder.add(fwdContext));
353 }
354
355 /**
356 * Populates ACL forwarding objectives for given XConnect.
357 *
358 * @param key XConnect store key
359 */
360 private void populateAcl(XconnectKey key) {
361 ForwardingObjective.Builder aclObjBuilder = aclObjBuilder(key.vlanId());
362 ObjectiveContext aclContext = new DefaultObjectiveContext(
363 (objective) -> log.debug("XConnect AclObj for {} populated", key),
364 (objective, error) ->
365 log.warn("Failed to populate XConnect AclObj for {}: {}", key, error));
366 flowObjectiveService.forward(key.deviceId(), aclObjBuilder.add(aclContext));
367 }
368
369 /**
370 * Revokes XConnect groups and flows for given key.
371 *
372 * @param key XConnect key
373 * @param ports XConnect ports
374 */
375 private void revokeXConnect(XconnectKey key, Set<PortNumber> ports) {
376 if (!mastershipService.isLocalMaster(key.deviceId())) {
377 log.info("Abort populating XConnect {}: {}", key, ERROR_NOT_MASTER);
378 return;
379 }
380
381 ports = addPairPort(key.deviceId(), ports);
382 revokeFilter(key, ports);
383 if (xconnectNextObjStore.containsKey(key)) {
Charles Chan3e56d9f2018-09-21 11:29:12 -0700384 int nextId = xconnectNextObjStore.get(key).value();
385 revokeFwd(key, nextId, null);
386 revokeNext(key, ports, nextId, null);
Charles Chanc7b3c452018-06-19 20:31:57 -0700387 } else {
388 log.warn("NextObj for {} does not exist in the store.", key);
389 }
390 revokeAcl(key);
391 }
392
393 /**
394 * Revokes filtering objectives for given XConnect.
395 *
396 * @param key XConnect store key
397 * @param ports XConnect ports
398 */
399 private void revokeFilter(XconnectKey key, Set<PortNumber> ports) {
400 ports.forEach(port -> {
401 FilteringObjective.Builder filtObjBuilder = filterObjBuilder(key, port);
402 ObjectiveContext context = new DefaultObjectiveContext(
403 (objective) -> log.debug("XConnect FilterObj for {} on port {} revoked",
404 key, port),
405 (objective, error) ->
406 log.warn("Failed to revoke XConnect FilterObj for {} on port {}: {}",
407 key, port, error));
408 flowObjectiveService.filter(key.deviceId(), filtObjBuilder.remove(context));
409 });
410 }
411
412 /**
413 * Revokes next objectives for given XConnect.
414 *
415 * @param key XConnect store key
Charles Chan3e56d9f2018-09-21 11:29:12 -0700416 * @param ports ports in the XConnect
417 * @param nextId next objective id
Charles Chanc7b3c452018-06-19 20:31:57 -0700418 * @param nextFuture completable future for this next objective operation
419 */
Charles Chan3e56d9f2018-09-21 11:29:12 -0700420 private void revokeNext(XconnectKey key, Set<PortNumber> ports, int nextId,
Charles Chanc7b3c452018-06-19 20:31:57 -0700421 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 }
Charles Chan55b806f2018-08-23 14:30:33 -0700437 srService.invalidateNextObj(objective.id());
Charles Chanc7b3c452018-06-19 20:31:57 -0700438 }
439 };
Charles Chan3e56d9f2018-09-21 11:29:12 -0700440 flowObjectiveService.next(key.deviceId(), nextObjBuilder(key, ports, nextId).remove(context));
Charles Chanc7b3c452018-06-19 20:31:57 -0700441 xconnectNextObjStore.remove(key);
442 }
443
444 /**
445 * Revokes bridging forwarding objectives for given XConnect.
446 *
447 * @param key XConnect store key
Charles Chan3e56d9f2018-09-21 11:29:12 -0700448 * @param nextId next objective id
Charles Chanc7b3c452018-06-19 20:31:57 -0700449 * @param fwdFuture completable future for this forwarding objective operation
450 */
Charles Chan3e56d9f2018-09-21 11:29:12 -0700451 private void revokeFwd(XconnectKey key, int nextId, CompletableFuture<ObjectiveError> fwdFuture) {
452 ForwardingObjective.Builder fwdObjBuilder = fwdObjBuilder(key, nextId);
Charles Chanc7b3c452018-06-19 20:31:57 -0700453 ObjectiveContext context = new ObjectiveContext() {
454 @Override
455 public void onSuccess(Objective objective) {
456 log.debug("Previous FwdObj for {} removed", key);
457 if (fwdFuture != null) {
458 fwdFuture.complete(null);
459 }
460 }
461
462 @Override
463 public void onError(Objective objective, ObjectiveError error) {
464 log.warn("Failed to remove previous FwdObj for {}: {}", key, error);
465 if (fwdFuture != null) {
466 fwdFuture.complete(error);
467 }
468 }
469 };
470 flowObjectiveService.forward(key.deviceId(), fwdObjBuilder.remove(context));
471 }
472
473 /**
474 * Revokes ACL forwarding objectives for given XConnect.
475 *
476 * @param key XConnect store key
477 */
478 private void revokeAcl(XconnectKey key) {
479 ForwardingObjective.Builder aclObjBuilder = aclObjBuilder(key.vlanId());
480 ObjectiveContext aclContext = new DefaultObjectiveContext(
481 (objective) -> log.debug("XConnect AclObj for {} populated", key),
482 (objective, error) ->
483 log.warn("Failed to populate XConnect AclObj for {}: {}", key, error));
484 flowObjectiveService.forward(key.deviceId(), aclObjBuilder.remove(aclContext));
485 }
486
487 /**
488 * Updates XConnect groups and flows for given key.
489 *
490 * @param key XConnect key
491 * @param prevPorts previous XConnect ports
492 * @param ports new XConnect ports
493 */
494 private void updateXConnect(XconnectKey key, Set<PortNumber> prevPorts,
495 Set<PortNumber> ports) {
496 // NOTE: ACL flow doesn't include port information. No need to update it.
497 // Pair port is built-in and thus not going to change. No need to update it.
498
499 // remove old filter
500 prevPorts.stream().filter(port -> !ports.contains(port)).forEach(port ->
501 revokeFilter(key, ImmutableSet.of(port)));
502 // install new filter
503 ports.stream().filter(port -> !prevPorts.contains(port)).forEach(port ->
504 populateFilter(key, ImmutableSet.of(port)));
505
506 CompletableFuture<ObjectiveError> fwdFuture = new CompletableFuture<>();
507 CompletableFuture<ObjectiveError> nextFuture = new CompletableFuture<>();
508
509 if (xconnectNextObjStore.containsKey(key)) {
Charles Chan3e56d9f2018-09-21 11:29:12 -0700510 int nextId = xconnectNextObjStore.get(key).value();
511 revokeFwd(key, nextId, fwdFuture);
Charles Chanc7b3c452018-06-19 20:31:57 -0700512
513 fwdFuture.thenAcceptAsync(fwdStatus -> {
514 if (fwdStatus == null) {
515 log.debug("Fwd removed. Now remove group {}", key);
Charles Chan3e56d9f2018-09-21 11:29:12 -0700516 revokeNext(key, prevPorts, nextId, nextFuture);
Charles Chanc7b3c452018-06-19 20:31:57 -0700517 }
518 });
519
520 nextFuture.thenAcceptAsync(nextStatus -> {
521 if (nextStatus == null) {
522 log.debug("Installing new group and flow for {}", key);
523 populateFwd(key, populateNext(key, ports));
524 }
525 });
526 } else {
527 log.warn("NextObj for {} does not exist in the store.", key);
528 }
529 }
530
531 /**
Charles Chan3e56d9f2018-09-21 11:29:12 -0700532 * Creates a next objective builder for XConnect with given nextId.
Charles Chanc7b3c452018-06-19 20:31:57 -0700533 *
534 * @param key XConnect key
535 * @param ports set of XConnect ports
Charles Chan3e56d9f2018-09-21 11:29:12 -0700536 * @param nextId next objective id
Charles Chanc7b3c452018-06-19 20:31:57 -0700537 * @return next objective builder
538 */
Charles Chan3e56d9f2018-09-21 11:29:12 -0700539 private NextObjective.Builder nextObjBuilder(XconnectKey key, Set<PortNumber> ports, int nextId) {
Charles Chanc7b3c452018-06-19 20:31:57 -0700540 TrafficSelector metadata =
541 DefaultTrafficSelector.builder().matchVlanId(key.vlanId()).build();
542 NextObjective.Builder nextObjBuilder = DefaultNextObjective
543 .builder().withId(nextId)
544 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
545 .withMeta(metadata);
546 ports.forEach(port -> {
547 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
548 tBuilder.setOutput(port);
549 nextObjBuilder.addTreatment(tBuilder.build());
550 });
551 return nextObjBuilder;
552 }
553
554 /**
Charles Chan3e56d9f2018-09-21 11:29:12 -0700555 * Creates a next objective builder for XConnect.
556 *
557 * @param key XConnect key
558 * @param ports set of XConnect ports
559 * @return next objective builder
560 */
561 private NextObjective.Builder nextObjBuilder(XconnectKey key, Set<PortNumber> ports) {
562 int nextId = flowObjectiveService.allocateNextId();
563 return nextObjBuilder(key, ports, nextId);
564 }
565
566
567 /**
Charles Chanc7b3c452018-06-19 20:31:57 -0700568 * Creates a bridging forwarding objective builder for XConnect.
569 *
570 * @param key XConnect key
571 * @param nextId next ID of the broadcast group for this XConnect key
572 * @return forwarding objective builder
573 */
574 private ForwardingObjective.Builder fwdObjBuilder(XconnectKey key, int nextId) {
575 /*
576 * Driver should treat objectives with MacAddress.NONE and !VlanId.NONE
577 * as the VLAN cross-connect broadcast rules
578 */
579 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
580 sbuilder.matchVlanId(key.vlanId());
581 sbuilder.matchEthDst(MacAddress.NONE);
582
583 ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
584 fob.withFlag(ForwardingObjective.Flag.SPECIFIC)
585 .withSelector(sbuilder.build())
586 .nextStep(nextId)
587 .withPriority(XCONNECT_PRIORITY)
588 .fromApp(appId)
589 .makePermanent();
590 return fob;
591 }
592
593 /**
594 * Creates an ACL forwarding objective builder for XConnect.
595 *
596 * @param vlanId cross connect VLAN id
597 * @return forwarding objective builder
598 */
599 private ForwardingObjective.Builder aclObjBuilder(VlanId vlanId) {
600 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
601 sbuilder.matchVlanId(vlanId);
602
603 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
604
605 ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
606 fob.withFlag(ForwardingObjective.Flag.VERSATILE)
607 .withSelector(sbuilder.build())
608 .withTreatment(tbuilder.build())
609 .withPriority(XCONNECT_ACL_PRIORITY)
610 .fromApp(appId)
611 .makePermanent();
612 return fob;
613 }
614
615 /**
616 * Creates a filtering objective builder for XConnect.
617 *
618 * @param key XConnect key
619 * @param port XConnect ports
620 * @return next objective builder
621 */
622 private FilteringObjective.Builder filterObjBuilder(XconnectKey key, PortNumber port) {
623 FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
624 fob.withKey(Criteria.matchInPort(port))
625 .addCondition(Criteria.matchVlanId(key.vlanId()))
626 .addCondition(Criteria.matchEthDst(MacAddress.NONE))
627 .withPriority(XCONNECT_PRIORITY);
628 return fob.permit().fromApp(appId);
629 }
630
631 /**
632 * Add pair port to the given set of port.
633 *
634 * @param deviceId device Id
635 * @param ports ports specified in the xconnect config
636 * @return port specified in the xconnect config plus the pair port (if configured)
637 */
638 private Set<PortNumber> addPairPort(DeviceId deviceId, Set<PortNumber> ports) {
639 if (srService == null) {
640 return ports;
641 }
642 Set<PortNumber> newPorts = Sets.newHashSet(ports);
643 srService.getPairLocalPort(deviceId).ifPresent(newPorts::add);
644 return newPorts;
645 }
Charles Chanc7b3c452018-06-19 20:31:57 -0700646}