Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 1 | /* |
Brian O'Connor | a09fe5b | 2017-08-03 21:12:30 -0700 | [diff] [blame] | 2 | * Copyright 2016-present Open Networking Foundation |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 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 | */ |
| 16 | |
| 17 | package org.onosproject.segmentrouting; |
| 18 | |
| 19 | import com.google.common.collect.ImmutableSet; |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 20 | import com.google.common.collect.Sets; |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 21 | import org.onlab.packet.MacAddress; |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 22 | import org.onlab.packet.VlanId; |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 23 | import org.onlab.util.KryoNamespace; |
| 24 | import org.onosproject.net.ConnectPoint; |
| 25 | import org.onosproject.net.DeviceId; |
| 26 | import org.onosproject.net.PortNumber; |
| 27 | import org.onosproject.net.config.NetworkConfigEvent; |
| 28 | import org.onosproject.net.flow.DefaultTrafficSelector; |
| 29 | import org.onosproject.net.flow.DefaultTrafficTreatment; |
| 30 | import org.onosproject.net.flow.TrafficSelector; |
| 31 | import org.onosproject.net.flow.TrafficTreatment; |
| 32 | import org.onosproject.net.flow.criteria.Criteria; |
| 33 | import org.onosproject.net.flowobjective.DefaultFilteringObjective; |
| 34 | import org.onosproject.net.flowobjective.DefaultForwardingObjective; |
| 35 | import org.onosproject.net.flowobjective.DefaultNextObjective; |
| 36 | import org.onosproject.net.flowobjective.DefaultObjectiveContext; |
| 37 | import org.onosproject.net.flowobjective.FilteringObjective; |
| 38 | import org.onosproject.net.flowobjective.ForwardingObjective; |
| 39 | import org.onosproject.net.flowobjective.NextObjective; |
| 40 | import org.onosproject.net.flowobjective.Objective; |
| 41 | import org.onosproject.net.flowobjective.ObjectiveContext; |
| 42 | import org.onosproject.net.flowobjective.ObjectiveError; |
| 43 | import org.onosproject.segmentrouting.config.XConnectConfig; |
| 44 | import org.onosproject.segmentrouting.storekey.XConnectStoreKey; |
| 45 | import org.onosproject.store.serializers.KryoNamespaces; |
| 46 | import org.onosproject.store.service.ConsistentMap; |
| 47 | import org.onosproject.store.service.Serializer; |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 48 | import org.slf4j.Logger; |
| 49 | import org.slf4j.LoggerFactory; |
| 50 | |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 51 | import java.util.Set; |
| 52 | import java.util.concurrent.CompletableFuture; |
| 53 | import java.util.stream.Collectors; |
| 54 | |
| 55 | /** |
| 56 | * Handles cross connect related events. |
Charles Chan | c7b3c45 | 2018-06-19 20:31:57 -0700 | [diff] [blame] | 57 | * |
| 58 | * @deprecated in ONOS 1.12. Replaced by {@link org.onosproject.segmentrouting.xconnect.impl.XconnectManager} |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 59 | */ |
Charles Chan | c7b3c45 | 2018-06-19 20:31:57 -0700 | [diff] [blame] | 60 | @Deprecated |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 61 | public class XConnectHandler { |
| 62 | private static final Logger log = LoggerFactory.getLogger(XConnectHandler.class); |
Charles Chan | c4d797f | 2016-07-07 12:00:08 -0700 | [diff] [blame] | 63 | private static final String CONFIG_NOT_FOUND = "XConnect config not found"; |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 64 | private static final String NOT_MASTER = "Not master controller"; |
| 65 | private final SegmentRoutingManager srManager; |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 66 | private final ConsistentMap<XConnectStoreKey, NextObjective> xConnectNextObjStore; |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 67 | |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 68 | XConnectHandler(SegmentRoutingManager srManager) { |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 69 | this.srManager = srManager; |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 70 | KryoNamespace.Builder xConnectKryo = new KryoNamespace.Builder() |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 71 | .register(KryoNamespaces.API) |
| 72 | .register(XConnectStoreKey.class) |
| 73 | .register(NextObjContext.class); |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 74 | xConnectNextObjStore = srManager.storageService |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 75 | .<XConnectStoreKey, NextObjective>consistentMapBuilder() |
| 76 | .withName("onos-xconnect-nextobj-store") |
| 77 | .withSerializer(Serializer.using(xConnectKryo.build())) |
| 78 | .build(); |
| 79 | } |
| 80 | |
| 81 | /** |
| 82 | * Read initial XConnect for given device. |
| 83 | * |
| 84 | * @param deviceId ID of the device to be initialized |
| 85 | */ |
| 86 | public void init(DeviceId deviceId) { |
| 87 | // Try to read XConnect config |
| 88 | XConnectConfig config = |
| 89 | srManager.cfgService.getConfig(srManager.appId, XConnectConfig.class); |
| 90 | if (config == null) { |
Charles Chan | c4d797f | 2016-07-07 12:00:08 -0700 | [diff] [blame] | 91 | log.info("Skip XConnect initialization: {}", CONFIG_NOT_FOUND); |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 92 | return; |
| 93 | } |
| 94 | |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 95 | config.getXconnects(deviceId).forEach(key -> populateXConnect(key, config.getPorts(key))); |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 96 | } |
| 97 | |
| 98 | /** |
| 99 | * Processes Segment Routing App Config added event. |
| 100 | * |
| 101 | * @param event network config added event |
| 102 | */ |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 103 | void processXConnectConfigAdded(NetworkConfigEvent event) { |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 104 | log.info("Processing XConnect CONFIG_ADDED"); |
| 105 | XConnectConfig config = (XConnectConfig) event.config().get(); |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 106 | config.getXconnects().forEach(key -> populateXConnect(key, config.getPorts(key))); |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 107 | } |
| 108 | |
| 109 | /** |
| 110 | * Processes Segment Routing App Config updated event. |
| 111 | * |
| 112 | * @param event network config updated event |
| 113 | */ |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 114 | void processXConnectConfigUpdated(NetworkConfigEvent event) { |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 115 | log.info("Processing XConnect CONFIG_UPDATED"); |
| 116 | XConnectConfig prevConfig = (XConnectConfig) event.prevConfig().get(); |
| 117 | XConnectConfig config = (XConnectConfig) event.config().get(); |
| 118 | Set<XConnectStoreKey> prevKeys = prevConfig.getXconnects(); |
| 119 | Set<XConnectStoreKey> keys = config.getXconnects(); |
| 120 | |
| 121 | Set<XConnectStoreKey> pendingRemove = prevKeys.stream() |
| 122 | .filter(key -> !keys.contains(key)).collect(Collectors.toSet()); |
| 123 | Set<XConnectStoreKey> pendingAdd = keys.stream() |
| 124 | .filter(key -> !prevKeys.contains(key)).collect(Collectors.toSet()); |
| 125 | Set<XConnectStoreKey> pendingUpdate = keys.stream() |
| 126 | .filter(key -> prevKeys.contains(key) && |
| 127 | !config.getPorts(key).equals(prevConfig.getPorts(key))) |
| 128 | .collect(Collectors.toSet()); |
| 129 | |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 130 | pendingRemove.forEach(key -> revokeXConnect(key, prevConfig.getPorts(key))); |
| 131 | pendingAdd.forEach(key -> populateXConnect(key, config.getPorts(key))); |
| 132 | pendingUpdate.forEach(key -> |
| 133 | updateXConnect(key, prevConfig.getPorts(key), config.getPorts(key))); |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 134 | } |
| 135 | |
| 136 | /** |
| 137 | * Processes Segment Routing App Config removed event. |
| 138 | * |
| 139 | * @param event network config removed event |
| 140 | */ |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 141 | void processXConnectConfigRemoved(NetworkConfigEvent event) { |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 142 | log.info("Processing XConnect CONFIG_REMOVED"); |
| 143 | XConnectConfig prevConfig = (XConnectConfig) event.prevConfig().get(); |
| 144 | prevConfig.getXconnects().forEach(key -> { |
| 145 | revokeXConnect(key, prevConfig.getPorts(key)); |
| 146 | }); |
| 147 | } |
| 148 | |
| 149 | /** |
| 150 | * Checks if there is any XConnect configured on given connect point. |
| 151 | * |
| 152 | * @param cp connect point |
| 153 | * @return true if there is XConnect configured on given connect point. |
| 154 | */ |
| 155 | public boolean hasXConnect(ConnectPoint cp) { |
| 156 | // Try to read XConnect config |
| 157 | XConnectConfig config = |
| 158 | srManager.cfgService.getConfig(srManager.appId, XConnectConfig.class); |
| 159 | if (config == null) { |
| 160 | log.warn("Failed to read XConnect config: {}", CONFIG_NOT_FOUND); |
| 161 | return false; |
| 162 | } |
| 163 | return config.getXconnects(cp.deviceId()).stream() |
| 164 | .anyMatch(key -> config.getPorts(key).contains(cp.port())); |
| 165 | } |
| 166 | |
| 167 | /** |
| 168 | * Populates XConnect groups and flows for given key. |
| 169 | * |
| 170 | * @param key XConnect key |
| 171 | * @param ports a set of ports to be cross-connected |
| 172 | */ |
| 173 | private void populateXConnect(XConnectStoreKey key, Set<PortNumber> ports) { |
| 174 | if (!srManager.mastershipService.isLocalMaster(key.deviceId())) { |
| 175 | log.info("Abort populating XConnect {}: {}", key, NOT_MASTER); |
| 176 | return; |
| 177 | } |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 178 | |
| 179 | ports = addPairPort(key.deviceId(), ports); |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 180 | populateFilter(key, ports); |
| 181 | populateFwd(key, populateNext(key, ports)); |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 182 | populateAcl(key); |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 183 | } |
| 184 | |
| 185 | /** |
| 186 | * Populates filtering objectives for given XConnect. |
| 187 | * |
| 188 | * @param key XConnect store key |
| 189 | * @param ports XConnect ports |
| 190 | */ |
| 191 | private void populateFilter(XConnectStoreKey key, Set<PortNumber> ports) { |
| 192 | ports.forEach(port -> { |
| 193 | FilteringObjective.Builder filtObjBuilder = filterObjBuilder(key, port); |
| 194 | ObjectiveContext context = new DefaultObjectiveContext( |
| 195 | (objective) -> log.debug("XConnect FilterObj for {} on port {} populated", |
| 196 | key, port), |
| 197 | (objective, error) -> |
| 198 | log.warn("Failed to populate XConnect FilterObj for {} on port {}: {}", |
| 199 | key, port, error)); |
| 200 | srManager.flowObjectiveService.filter(key.deviceId(), filtObjBuilder.add(context)); |
| 201 | }); |
| 202 | } |
| 203 | |
| 204 | /** |
| 205 | * Populates next objectives for given XConnect. |
| 206 | * |
| 207 | * @param key XConnect store key |
| 208 | * @param ports XConnect ports |
| 209 | */ |
| 210 | private NextObjective populateNext(XConnectStoreKey key, Set<PortNumber> ports) { |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 211 | NextObjective nextObj; |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 212 | if (xConnectNextObjStore.containsKey(key)) { |
| 213 | nextObj = xConnectNextObjStore.get(key).value(); |
| 214 | log.debug("NextObj for {} found, id={}", key, nextObj.id()); |
| 215 | } else { |
| 216 | NextObjective.Builder nextObjBuilder = nextObjBuilder(key, ports); |
| 217 | ObjectiveContext nextContext = new NextObjContext(Objective.Operation.ADD, key); |
| 218 | nextObj = nextObjBuilder.add(nextContext); |
| 219 | srManager.flowObjectiveService.next(key.deviceId(), nextObj); |
| 220 | xConnectNextObjStore.put(key, nextObj); |
| 221 | log.debug("NextObj for {} not found. Creating new NextObj with id={}", key, nextObj.id()); |
| 222 | } |
| 223 | return nextObj; |
| 224 | } |
| 225 | |
| 226 | /** |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 227 | * Populates bridging forwarding objectives for given XConnect. |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 228 | * |
| 229 | * @param key XConnect store key |
| 230 | * @param nextObj next objective |
| 231 | */ |
| 232 | private void populateFwd(XConnectStoreKey key, NextObjective nextObj) { |
| 233 | ForwardingObjective.Builder fwdObjBuilder = fwdObjBuilder(key, nextObj.id()); |
| 234 | ObjectiveContext fwdContext = new DefaultObjectiveContext( |
| 235 | (objective) -> log.debug("XConnect FwdObj for {} populated", key), |
| 236 | (objective, error) -> |
| 237 | log.warn("Failed to populate XConnect FwdObj for {}: {}", key, error)); |
| 238 | srManager.flowObjectiveService.forward(key.deviceId(), fwdObjBuilder.add(fwdContext)); |
| 239 | } |
| 240 | |
| 241 | /** |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 242 | * Populates ACL forwarding objectives for given XConnect. |
| 243 | * |
| 244 | * @param key XConnect store key |
| 245 | */ |
| 246 | private void populateAcl(XConnectStoreKey key) { |
| 247 | ForwardingObjective.Builder aclObjBuilder = aclObjBuilder(key.vlanId()); |
| 248 | ObjectiveContext aclContext = new DefaultObjectiveContext( |
| 249 | (objective) -> log.debug("XConnect AclObj for {} populated", key), |
| 250 | (objective, error) -> |
| 251 | log.warn("Failed to populate XConnect AclObj for {}: {}", key, error)); |
| 252 | srManager.flowObjectiveService.forward(key.deviceId(), aclObjBuilder.add(aclContext)); |
| 253 | } |
| 254 | |
| 255 | /** |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 256 | * Revokes XConnect groups and flows for given key. |
| 257 | * |
| 258 | * @param key XConnect key |
| 259 | * @param ports XConnect ports |
| 260 | */ |
| 261 | private void revokeXConnect(XConnectStoreKey key, Set<PortNumber> ports) { |
| 262 | if (!srManager.mastershipService.isLocalMaster(key.deviceId())) { |
| 263 | log.info("Abort populating XConnect {}: {}", key, NOT_MASTER); |
| 264 | return; |
| 265 | } |
| 266 | |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 267 | ports = addPairPort(key.deviceId(), ports); |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 268 | revokeFilter(key, ports); |
| 269 | if (xConnectNextObjStore.containsKey(key)) { |
| 270 | NextObjective nextObj = xConnectNextObjStore.get(key).value(); |
| 271 | revokeFwd(key, nextObj, null); |
| 272 | revokeNext(key, nextObj, null); |
| 273 | } else { |
| 274 | log.warn("NextObj for {} does not exist in the store.", key); |
| 275 | } |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 276 | revokeAcl(key); |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 277 | } |
| 278 | |
| 279 | /** |
| 280 | * Revokes filtering objectives for given XConnect. |
| 281 | * |
| 282 | * @param key XConnect store key |
| 283 | * @param ports XConnect ports |
| 284 | */ |
| 285 | private void revokeFilter(XConnectStoreKey key, Set<PortNumber> ports) { |
| 286 | ports.forEach(port -> { |
| 287 | FilteringObjective.Builder filtObjBuilder = filterObjBuilder(key, port); |
| 288 | ObjectiveContext context = new DefaultObjectiveContext( |
| 289 | (objective) -> log.debug("XConnect FilterObj for {} on port {} revoked", |
| 290 | key, port), |
| 291 | (objective, error) -> |
| 292 | log.warn("Failed to revoke XConnect FilterObj for {} on port {}: {}", |
| 293 | key, port, error)); |
| 294 | srManager.flowObjectiveService.filter(key.deviceId(), filtObjBuilder.remove(context)); |
| 295 | }); |
| 296 | } |
| 297 | |
| 298 | /** |
| 299 | * Revokes next objectives for given XConnect. |
| 300 | * |
| 301 | * @param key XConnect store key |
| 302 | * @param nextObj next objective |
| 303 | * @param nextFuture completable future for this next objective operation |
| 304 | */ |
| 305 | private void revokeNext(XConnectStoreKey key, NextObjective nextObj, |
| 306 | CompletableFuture<ObjectiveError> nextFuture) { |
| 307 | ObjectiveContext context = new ObjectiveContext() { |
| 308 | @Override |
| 309 | public void onSuccess(Objective objective) { |
| 310 | log.debug("Previous NextObj for {} removed", key); |
| 311 | if (nextFuture != null) { |
| 312 | nextFuture.complete(null); |
| 313 | } |
| 314 | } |
| 315 | |
| 316 | @Override |
| 317 | public void onError(Objective objective, ObjectiveError error) { |
| 318 | log.warn("Failed to remove previous NextObj for {}: {}", key, error); |
| 319 | if (nextFuture != null) { |
| 320 | nextFuture.complete(error); |
| 321 | } |
| 322 | } |
| 323 | }; |
| 324 | srManager.flowObjectiveService.next(key.deviceId(), |
| 325 | (NextObjective) nextObj.copy().remove(context)); |
| 326 | xConnectNextObjStore.remove(key); |
| 327 | } |
| 328 | |
| 329 | /** |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 330 | * Revokes bridging forwarding objectives for given XConnect. |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 331 | * |
| 332 | * @param key XConnect store key |
| 333 | * @param nextObj next objective |
| 334 | * @param fwdFuture completable future for this forwarding objective operation |
| 335 | */ |
| 336 | private void revokeFwd(XConnectStoreKey key, NextObjective nextObj, |
| 337 | CompletableFuture<ObjectiveError> fwdFuture) { |
| 338 | ForwardingObjective.Builder fwdObjBuilder = fwdObjBuilder(key, nextObj.id()); |
| 339 | ObjectiveContext context = new ObjectiveContext() { |
| 340 | @Override |
| 341 | public void onSuccess(Objective objective) { |
| 342 | log.debug("Previous FwdObj for {} removed", key); |
| 343 | if (fwdFuture != null) { |
| 344 | fwdFuture.complete(null); |
| 345 | } |
| 346 | } |
| 347 | |
| 348 | @Override |
| 349 | public void onError(Objective objective, ObjectiveError error) { |
| 350 | log.warn("Failed to remove previous FwdObj for {}: {}", key, error); |
| 351 | if (fwdFuture != null) { |
| 352 | fwdFuture.complete(error); |
| 353 | } |
| 354 | } |
| 355 | }; |
| 356 | srManager.flowObjectiveService |
| 357 | .forward(key.deviceId(), fwdObjBuilder.remove(context)); |
| 358 | } |
| 359 | |
| 360 | /** |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 361 | * Revokes ACL forwarding objectives for given XConnect. |
| 362 | * |
| 363 | * @param key XConnect store key |
| 364 | */ |
| 365 | private void revokeAcl(XConnectStoreKey key) { |
| 366 | ForwardingObjective.Builder aclObjBuilder = aclObjBuilder(key.vlanId()); |
| 367 | ObjectiveContext aclContext = new DefaultObjectiveContext( |
| 368 | (objective) -> log.debug("XConnect AclObj for {} populated", key), |
| 369 | (objective, error) -> |
| 370 | log.warn("Failed to populate XConnect AclObj for {}: {}", key, error)); |
| 371 | srManager.flowObjectiveService |
| 372 | .forward(key.deviceId(), aclObjBuilder.remove(aclContext)); |
| 373 | } |
| 374 | |
| 375 | /** |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 376 | * Updates XConnect groups and flows for given key. |
| 377 | * |
| 378 | * @param key XConnect key |
| 379 | * @param prevPorts previous XConnect ports |
| 380 | * @param ports new XConnect ports |
| 381 | */ |
| 382 | private void updateXConnect(XConnectStoreKey key, Set<PortNumber> prevPorts, |
| 383 | Set<PortNumber> ports) { |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 384 | // NOTE: ACL flow doesn't include port information. No need to update it. |
| 385 | // Pair port is built-in and thus not going to change. No need to update it. |
| 386 | |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 387 | // remove old filter |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 388 | prevPorts.stream().filter(port -> !ports.contains(port)).forEach(port -> |
| 389 | revokeFilter(key, ImmutableSet.of(port))); |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 390 | // install new filter |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 391 | ports.stream().filter(port -> !prevPorts.contains(port)).forEach(port -> |
| 392 | populateFilter(key, ImmutableSet.of(port))); |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 393 | |
| 394 | CompletableFuture<ObjectiveError> fwdFuture = new CompletableFuture<>(); |
| 395 | CompletableFuture<ObjectiveError> nextFuture = new CompletableFuture<>(); |
| 396 | |
| 397 | if (xConnectNextObjStore.containsKey(key)) { |
| 398 | NextObjective nextObj = xConnectNextObjStore.get(key).value(); |
| 399 | revokeFwd(key, nextObj, fwdFuture); |
| 400 | |
| 401 | fwdFuture.thenAcceptAsync(fwdStatus -> { |
| 402 | if (fwdStatus == null) { |
| 403 | log.debug("Fwd removed. Now remove group {}", key); |
| 404 | revokeNext(key, nextObj, nextFuture); |
| 405 | } |
| 406 | }); |
| 407 | |
| 408 | nextFuture.thenAcceptAsync(nextStatus -> { |
| 409 | if (nextStatus == null) { |
| 410 | log.debug("Installing new group and flow for {}", key); |
| 411 | populateFwd(key, populateNext(key, ports)); |
| 412 | } |
| 413 | }); |
| 414 | } else { |
| 415 | log.warn("NextObj for {} does not exist in the store.", key); |
| 416 | } |
| 417 | } |
| 418 | |
| 419 | /** |
| 420 | * Remove all groups on given device. |
| 421 | * |
| 422 | * @param deviceId device ID |
| 423 | */ |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 424 | void removeDevice(DeviceId deviceId) { |
Charles Chan | a061fb3f | 2016-06-17 14:28:07 -0700 | [diff] [blame] | 425 | xConnectNextObjStore.entrySet().stream() |
| 426 | .filter(entry -> entry.getKey().deviceId().equals(deviceId)) |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 427 | .forEach(entry -> xConnectNextObjStore.remove(entry.getKey())); |
Charles Chan | a061fb3f | 2016-06-17 14:28:07 -0700 | [diff] [blame] | 428 | log.debug("{} is removed from xConnectNextObjStore", deviceId); |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 429 | } |
| 430 | |
| 431 | /** |
| 432 | * Creates a next objective builder for XConnect. |
| 433 | * |
| 434 | * @param key XConnect key |
| 435 | * @param ports set of XConnect ports |
| 436 | * @return next objective builder |
| 437 | */ |
| 438 | private NextObjective.Builder nextObjBuilder(XConnectStoreKey key, Set<PortNumber> ports) { |
| 439 | int nextId = srManager.flowObjectiveService.allocateNextId(); |
| 440 | TrafficSelector metadata = |
| 441 | DefaultTrafficSelector.builder().matchVlanId(key.vlanId()).build(); |
| 442 | NextObjective.Builder nextObjBuilder = DefaultNextObjective |
| 443 | .builder().withId(nextId) |
| 444 | .withType(NextObjective.Type.BROADCAST).fromApp(srManager.appId) |
| 445 | .withMeta(metadata); |
| 446 | ports.forEach(port -> { |
| 447 | TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); |
| 448 | tBuilder.setOutput(port); |
| 449 | nextObjBuilder.addTreatment(tBuilder.build()); |
| 450 | }); |
| 451 | return nextObjBuilder; |
| 452 | } |
| 453 | |
| 454 | /** |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 455 | * Creates a bridging forwarding objective builder for XConnect. |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 456 | * |
| 457 | * @param key XConnect key |
| 458 | * @param nextId next ID of the broadcast group for this XConnect key |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 459 | * @return forwarding objective builder |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 460 | */ |
| 461 | private ForwardingObjective.Builder fwdObjBuilder(XConnectStoreKey key, int nextId) { |
| 462 | /* |
| 463 | * Driver should treat objectives with MacAddress.NONE and !VlanId.NONE |
| 464 | * as the VLAN cross-connect broadcast rules |
| 465 | */ |
| 466 | TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder(); |
| 467 | sbuilder.matchVlanId(key.vlanId()); |
| 468 | sbuilder.matchEthDst(MacAddress.NONE); |
| 469 | |
| 470 | ForwardingObjective.Builder fob = DefaultForwardingObjective.builder(); |
| 471 | fob.withFlag(ForwardingObjective.Flag.SPECIFIC) |
| 472 | .withSelector(sbuilder.build()) |
| 473 | .nextStep(nextId) |
| 474 | .withPriority(SegmentRoutingService.XCONNECT_PRIORITY) |
| 475 | .fromApp(srManager.appId) |
| 476 | .makePermanent(); |
| 477 | return fob; |
| 478 | } |
| 479 | |
| 480 | /** |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 481 | * Creates an ACL forwarding objective builder for XConnect. |
| 482 | * |
| 483 | * @param vlanId cross connect VLAN id |
| 484 | * @return forwarding objective builder |
| 485 | */ |
| 486 | private ForwardingObjective.Builder aclObjBuilder(VlanId vlanId) { |
| 487 | TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder(); |
| 488 | sbuilder.matchVlanId(vlanId); |
| 489 | |
| 490 | TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder(); |
| 491 | |
| 492 | ForwardingObjective.Builder fob = DefaultForwardingObjective.builder(); |
| 493 | fob.withFlag(ForwardingObjective.Flag.VERSATILE) |
| 494 | .withSelector(sbuilder.build()) |
| 495 | .withTreatment(tbuilder.build()) |
| 496 | .withPriority(SegmentRoutingService.XCONNECT_ACL_PRIORITY) |
| 497 | .fromApp(srManager.appId) |
| 498 | .makePermanent(); |
| 499 | return fob; |
| 500 | } |
| 501 | |
| 502 | /** |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 503 | * Creates a filtering objective builder for XConnect. |
| 504 | * |
| 505 | * @param key XConnect key |
| 506 | * @param port XConnect ports |
| 507 | * @return next objective builder |
| 508 | */ |
| 509 | private FilteringObjective.Builder filterObjBuilder(XConnectStoreKey key, PortNumber port) { |
| 510 | FilteringObjective.Builder fob = DefaultFilteringObjective.builder(); |
| 511 | fob.withKey(Criteria.matchInPort(port)) |
| 512 | .addCondition(Criteria.matchVlanId(key.vlanId())) |
| 513 | .addCondition(Criteria.matchEthDst(MacAddress.NONE)) |
| 514 | .withPriority(SegmentRoutingService.XCONNECT_PRIORITY); |
| 515 | return fob.permit().fromApp(srManager.appId); |
| 516 | } |
| 517 | |
Charles Chan | f76de30 | 2018-06-15 18:54:18 -0700 | [diff] [blame] | 518 | /** |
| 519 | * Add pair port to the given set of port. |
| 520 | * |
| 521 | * @param deviceId device Id |
| 522 | * @param ports ports specified in the xconnect config |
| 523 | * @return port specified in the xconnect config plus the pair port (if configured) |
| 524 | */ |
| 525 | private Set<PortNumber> addPairPort(DeviceId deviceId, Set<PortNumber> ports) { |
| 526 | Set<PortNumber> newPorts = Sets.newHashSet(ports); |
| 527 | srManager.getPairLocalPort(deviceId).ifPresent(newPorts::add); |
| 528 | return newPorts; |
| 529 | } |
| 530 | |
Charles Chan | fc5c780 | 2016-05-17 13:13:55 -0700 | [diff] [blame] | 531 | // TODO: Lambda closure in DefaultObjectiveContext cannot be serialized properly |
| 532 | // with Kryo 3.0.3. It will be fixed in 3.0.4. By then we can use |
| 533 | // DefaultObjectiveContext again. |
| 534 | private final class NextObjContext implements ObjectiveContext { |
| 535 | Objective.Operation op; |
| 536 | XConnectStoreKey key; |
| 537 | |
| 538 | private NextObjContext(Objective.Operation op, XConnectStoreKey key) { |
| 539 | this.op = op; |
| 540 | this.key = key; |
| 541 | } |
| 542 | |
| 543 | @Override |
| 544 | public void onSuccess(Objective objective) { |
| 545 | log.debug("XConnect NextObj for {} {}ED", key, op); |
| 546 | } |
| 547 | |
| 548 | @Override |
| 549 | public void onError(Objective objective, ObjectiveError error) { |
| 550 | log.warn("Failed to {} XConnect NextObj for {}: {}", op, key, error); |
| 551 | } |
| 552 | } |
| 553 | } |