blob: 02f2b1974fa9395b8bb3682851dd7ca11d99d62c [file] [log] [blame]
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -08003 *
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 */
Srikanth Vavilapalli4db76e32015-04-07 15:12:32 -070016package org.onosproject.segmentrouting.grouphandler;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080017
Saurav Das9df5b7c2017-08-14 16:44:43 -070018import com.google.common.collect.ImmutableMap;
Pier Ventre917127a2016-10-31 16:49:19 -070019import com.google.common.collect.Iterables;
Saurav Das9df5b7c2017-08-14 16:44:43 -070020import com.google.common.collect.Lists;
Saurav Dasc88d4662017-05-15 15:34:25 -070021import com.google.common.collect.Sets;
22
Pier Ventre917127a2016-10-31 16:49:19 -070023import org.apache.commons.lang3.RandomUtils;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080024import org.onlab.packet.MacAddress;
sangho32a59322015-02-17 12:07:41 -080025import org.onlab.packet.MplsLabel;
Saurav Das423fe2b2015-12-04 10:52:59 -080026import org.onlab.packet.VlanId;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070027import org.onlab.util.KryoNamespace;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080028import org.onosproject.core.ApplicationId;
Charles Chan59cc16d2017-02-02 16:20:42 -080029import org.onosproject.net.ConnectPoint;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080030import org.onosproject.net.DeviceId;
31import org.onosproject.net.Link;
32import org.onosproject.net.PortNumber;
Saurav Das423fe2b2015-12-04 10:52:59 -080033import org.onosproject.net.flow.DefaultTrafficSelector;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080034import org.onosproject.net.flow.DefaultTrafficTreatment;
Saurav Das8a0732e2015-11-20 15:27:53 -080035import org.onosproject.net.flow.TrafficSelector;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080036import org.onosproject.net.flow.TrafficTreatment;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070037import org.onosproject.net.flowobjective.DefaultNextObjective;
Charles Chan216e3c82016-04-23 14:48:16 -070038import org.onosproject.net.flowobjective.DefaultObjectiveContext;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070039import org.onosproject.net.flowobjective.FlowObjectiveService;
40import org.onosproject.net.flowobjective.NextObjective;
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -070041import org.onosproject.net.flowobjective.ObjectiveContext;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080042import org.onosproject.net.link.LinkService;
Saurav Dasceccf242017-08-03 18:30:35 -070043import org.onosproject.segmentrouting.DefaultRoutingHandler;
Saurav Das423fe2b2015-12-04 10:52:59 -080044import org.onosproject.segmentrouting.SegmentRoutingManager;
Charles Chan0b4e6182015-11-03 10:42:14 -080045import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
46import org.onosproject.segmentrouting.config.DeviceProperties;
Saurav Das7bcbe702017-06-13 15:35:54 -070047import org.onosproject.segmentrouting.storekey.DestinationSetNextObjectiveStoreKey;
Charles Chand2990362016-04-18 13:44:03 -070048import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
Charles Chan59cc16d2017-02-02 16:20:42 -080049import org.onosproject.segmentrouting.storekey.VlanNextObjectiveStoreKey;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070050import org.onosproject.store.service.EventuallyConsistentMap;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080051import org.slf4j.Logger;
52
Pier Ventre917127a2016-10-31 16:49:19 -070053import java.net.URI;
Charles Chan7ffd81f2017-02-08 15:52:08 -080054import java.util.Collection;
Pier Ventre917127a2016-10-31 16:49:19 -070055import java.util.Collections;
Saurav Dasc88d4662017-05-15 15:34:25 -070056import java.util.HashMap;
Pier Ventre917127a2016-10-31 16:49:19 -070057import java.util.HashSet;
58import java.util.List;
59import java.util.Map;
60import java.util.Set;
61import java.util.concurrent.ConcurrentHashMap;
Saurav Das1547b3f2017-05-05 17:01:08 -070062import java.util.concurrent.ScheduledExecutorService;
63import java.util.concurrent.TimeUnit;
Pier Ventre917127a2016-10-31 16:49:19 -070064import java.util.stream.Collectors;
65
66import static com.google.common.base.Preconditions.checkNotNull;
Saurav Das1547b3f2017-05-05 17:01:08 -070067import static java.util.concurrent.Executors.newScheduledThreadPool;
68import static org.onlab.util.Tools.groupedThreads;
Pier Ventre917127a2016-10-31 16:49:19 -070069import static org.slf4j.LoggerFactory.getLogger;
70
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080071/**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070072 * Default ECMP group handler creation module. This component creates a set of
73 * ECMP groups for every neighbor that this device is connected to based on
74 * whether the current device is an edge device or a transit device.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080075 */
76public class DefaultGroupHandler {
Ray Milkey9c9cde42018-01-12 14:22:06 -080077 private static final Logger log = getLogger(DefaultGroupHandler.class);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080078
Saurav Dasceccf242017-08-03 18:30:35 -070079 private static final long VERIFY_INTERVAL = 30; // secs
80
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080081 protected final DeviceId deviceId;
82 protected final ApplicationId appId;
83 protected final DeviceProperties deviceConfig;
84 protected final List<Integer> allSegmentIds;
Pier Ventree0ae7a32016-11-23 09:57:42 -080085 protected int ipv4NodeSegmentId = -1;
86 protected int ipv6NodeSegmentId = -1;
Charles Chan0b4e6182015-11-03 10:42:14 -080087 protected boolean isEdgeRouter = false;
88 protected MacAddress nodeMacAddr = null;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080089 protected LinkService linkService;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070090 protected FlowObjectiveService flowObjectiveService;
Saurav Dasc88d4662017-05-15 15:34:25 -070091 /**
92 * local store for neighbor-device-ids and the set of ports on this device
93 * that connect to the same neighbor.
94 */
Saurav Das8a0732e2015-11-20 15:27:53 -080095 protected ConcurrentHashMap<DeviceId, Set<PortNumber>> devicePortMap =
96 new ConcurrentHashMap<>();
Saurav Dasc88d4662017-05-15 15:34:25 -070097 /**
98 * local store for ports on this device connected to neighbor-device-id.
99 */
Saurav Das8a0732e2015-11-20 15:27:53 -0800100 protected ConcurrentHashMap<PortNumber, DeviceId> portDeviceMap =
101 new ConcurrentHashMap<>();
Saurav Dasc88d4662017-05-15 15:34:25 -0700102
Saurav Das7bcbe702017-06-13 15:35:54 -0700103 // distributed store for (device+destination-set) mapped to next-id and neighbors
104 protected EventuallyConsistentMap<DestinationSetNextObjectiveStoreKey, NextNeighbors>
105 dsNextObjStore = null;
Saurav Das1a129a02016-11-18 15:21:57 -0800106 // distributed store for (device+subnet-ip-prefix) mapped to next-id
Charles Chan59cc16d2017-02-02 16:20:42 -0800107 protected EventuallyConsistentMap<VlanNextObjectiveStoreKey, Integer>
108 vlanNextObjStore = null;
Saurav Das1a129a02016-11-18 15:21:57 -0800109 // distributed store for (device+port+treatment) mapped to next-id
Charles Chane849c192016-01-11 18:28:54 -0800110 protected EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
111 portNextObjStore = null;
Charles Chan188ebf52015-12-23 00:15:11 -0800112 private SegmentRoutingManager srManager;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800113
Saurav Das1547b3f2017-05-05 17:01:08 -0700114 private ScheduledExecutorService executorService
Saurav Dasceccf242017-08-03 18:30:35 -0700115 = newScheduledThreadPool(1, groupedThreads("bktCorrector", "bktC-%d", log));
Saurav Das1547b3f2017-05-05 17:01:08 -0700116
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700117 protected KryoNamespace.Builder kryo = new KryoNamespace.Builder()
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700118 .register(URI.class).register(HashSet.class)
Saurav Das7bcbe702017-06-13 15:35:54 -0700119 .register(DeviceId.class).register(PortNumber.class)
120 .register(DestinationSet.class).register(PolicyGroupIdentifier.class)
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700121 .register(PolicyGroupParams.class)
122 .register(GroupBucketIdentifier.class)
123 .register(GroupBucketIdentifier.BucketOutputType.class);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800124
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700125 protected DefaultGroupHandler(DeviceId deviceId, ApplicationId appId,
126 DeviceProperties config,
127 LinkService linkService,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700128 FlowObjectiveService flowObjService,
Charles Chan188ebf52015-12-23 00:15:11 -0800129 SegmentRoutingManager srManager) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800130 this.deviceId = checkNotNull(deviceId);
131 this.appId = checkNotNull(appId);
132 this.deviceConfig = checkNotNull(config);
133 this.linkService = checkNotNull(linkService);
Charles Chan0b4e6182015-11-03 10:42:14 -0800134 this.allSegmentIds = checkNotNull(config.getAllDeviceSegmentIds());
135 try {
Pier Ventree0ae7a32016-11-23 09:57:42 -0800136 this.ipv4NodeSegmentId = config.getIPv4SegmentId(deviceId);
137 this.ipv6NodeSegmentId = config.getIPv6SegmentId(deviceId);
Charles Chan0b4e6182015-11-03 10:42:14 -0800138 this.isEdgeRouter = config.isEdgeDevice(deviceId);
139 this.nodeMacAddr = checkNotNull(config.getDeviceMac(deviceId));
140 } catch (DeviceConfigNotFoundException e) {
141 log.warn(e.getMessage()
142 + " Skipping value assignment in DefaultGroupHandler");
143 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700144 this.flowObjectiveService = flowObjService;
Saurav Das7bcbe702017-06-13 15:35:54 -0700145 this.dsNextObjStore = srManager.dsNextObjStore();
Ray Milkeye4afdb52017-04-05 09:42:04 -0700146 this.vlanNextObjStore = srManager.vlanNextObjStore();
147 this.portNextObjStore = srManager.portNextObjStore();
Charles Chan188ebf52015-12-23 00:15:11 -0800148 this.srManager = srManager;
Saurav Dasceccf242017-08-03 18:30:35 -0700149 executorService.scheduleWithFixedDelay(new BucketCorrector(), 10,
150 VERIFY_INTERVAL,
151 TimeUnit.SECONDS);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800152 populateNeighborMaps();
153 }
154
155 /**
Saurav Dasceccf242017-08-03 18:30:35 -0700156 * Gracefully shuts down a groupHandler. Typically called when the handler is
157 * no longer needed.
158 */
159 public void shutdown() {
160 executorService.shutdown();
161 }
162
163 /**
Saurav Dasc88d4662017-05-15 15:34:25 -0700164 * Creates a group handler object.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800165 *
166 * @param deviceId device identifier
167 * @param appId application identifier
168 * @param config interface to retrieve the device properties
169 * @param linkService link service object
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700170 * @param flowObjService flow objective service object
Charles Chane849c192016-01-11 18:28:54 -0800171 * @param srManager segment routing manager
Charles Chan0b4e6182015-11-03 10:42:14 -0800172 * @throws DeviceConfigNotFoundException if the device configuration is not found
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800173 * @return default group handler type
174 */
Saurav Das4ce45962015-11-24 23:21:05 -0800175 public static DefaultGroupHandler createGroupHandler(
Saurav Dasceccf242017-08-03 18:30:35 -0700176 DeviceId deviceId,
177 ApplicationId appId,
178 DeviceProperties config,
179 LinkService linkService,
180 FlowObjectiveService flowObjService,
181 SegmentRoutingManager srManager)
182 throws DeviceConfigNotFoundException {
Saurav Dasc88d4662017-05-15 15:34:25 -0700183 return new DefaultGroupHandler(deviceId, appId, config,
184 linkService,
185 flowObjService,
186 srManager);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800187 }
188
189 /**
Saurav Dasa4020382018-02-14 14:14:54 -0800190 * Updates local stores for link-src-device/port to neighbor (link-dst) for
191 * link that has come up.
Saurav Dasc88d4662017-05-15 15:34:25 -0700192 *
193 * @param link the infrastructure link
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800194 */
Saurav Dasc88d4662017-05-15 15:34:25 -0700195 public void portUpForLink(Link link) {
Saurav Dasceccf242017-08-03 18:30:35 -0700196 if (!link.src().deviceId().equals(deviceId)) {
197 log.warn("linkUp: deviceId{} doesn't match with link src {}",
198 deviceId, link.src().deviceId());
199 return;
200 }
Saurav Dasc88d4662017-05-15 15:34:25 -0700201
Saurav Dasceccf242017-08-03 18:30:35 -0700202 log.info("* portUpForLink: Device {} linkUp at local port {} to "
203 + "neighbor {}", deviceId, link.src().port(), link.dst().deviceId());
204 // ensure local state is updated even if linkup is aborted later on
205 addNeighborAtPort(link.dst().deviceId(),
206 link.src().port());
207 }
Saurav Dasc88d4662017-05-15 15:34:25 -0700208
Saurav Dasceccf242017-08-03 18:30:35 -0700209 /**
Saurav Dasa4020382018-02-14 14:14:54 -0800210 * Updates local stores for link-src-device/port to neighbor (link-dst) for
211 * link that has gone down.
Saurav Dasceccf242017-08-03 18:30:35 -0700212 *
Saurav Dasa4020382018-02-14 14:14:54 -0800213 * @param link the infrastructure link
Saurav Dasceccf242017-08-03 18:30:35 -0700214 */
Saurav Dasa4020382018-02-14 14:14:54 -0800215 public void portDownForLink(Link link) {
216 PortNumber port = link.src().port();
Saurav Dasceccf242017-08-03 18:30:35 -0700217 if (portDeviceMap.get(port) == null) {
218 log.warn("portDown: unknown port");
219 return;
220 }
Saurav Dasc88d4662017-05-15 15:34:25 -0700221
Saurav Dasceccf242017-08-03 18:30:35 -0700222 log.debug("Device {} portDown {} to neighbor {}", deviceId, port,
223 portDeviceMap.get(port));
224 devicePortMap.get(portDeviceMap.get(port)).remove(port);
225 portDeviceMap.remove(port);
226 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800227
228 /**
Saurav Dasa4020382018-02-14 14:14:54 -0800229 * Cleans up local stores for removed neighbor device.
230 *
231 * @param neighborId the device identifier for the neighbor device
232 */
233 public void cleanUpForNeighborDown(DeviceId neighborId) {
234 Set<PortNumber> ports = devicePortMap.remove(neighborId);
235 if (ports != null) {
236 ports.forEach(p -> portDeviceMap.remove(p));
237 }
238 }
239
240 /**
Saurav Dasc88d4662017-05-15 15:34:25 -0700241 * Checks all groups in the src-device of link for neighbor sets that include
242 * the dst-device of link, and edits the hash groups according to link up
243 * or down. Should only be called by the master instance of the src-switch
244 * of link. Typically used when there are no route-path changes due to the
245 * link up or down, as the ECMPspg does not change.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800246 *
Saurav Dasc88d4662017-05-15 15:34:25 -0700247 * @param link the infrastructure link that has gone down or come up
248 * @param linkDown true if link has gone down
249 * @param firstTime true if link has come up for the first time i.e a link
250 * not seen-before
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800251 */
Saurav Dasc88d4662017-05-15 15:34:25 -0700252 public void retryHash(Link link, boolean linkDown, boolean firstTime) {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700253 MacAddress neighborMac;
Charles Chan0b4e6182015-11-03 10:42:14 -0800254 try {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700255 neighborMac = deviceConfig.getDeviceMac(link.dst().deviceId());
Charles Chan0b4e6182015-11-03 10:42:14 -0800256 } catch (DeviceConfigNotFoundException e) {
Saurav Dasc88d4662017-05-15 15:34:25 -0700257 log.warn(e.getMessage() + " Aborting retryHash.");
Charles Chan0b4e6182015-11-03 10:42:14 -0800258 return;
259 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700260 // find all the destinationSets related to link
261 Set<DestinationSetNextObjectiveStoreKey> dsKeySet = dsNextObjStore.entrySet()
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700262 .stream()
Saurav Das7bcbe702017-06-13 15:35:54 -0700263 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
Pier Luigi63edd932018-01-14 21:56:11 +0100264 // Filter out PW transit groups or include them if MPLS ECMP is supported
Saurav Dasa4020382018-02-14 14:14:54 -0800265 .filter(entry -> !entry.getKey().destinationSet().notBos() ||
266 (entry.getKey().destinationSet().notBos() && srManager.getMplsEcmp()))
267 // Filter out simple SWAP groups or include them if MPLS ECMP is supported
268 .filter(entry -> !entry.getKey().destinationSet().swap() ||
269 (entry.getKey().destinationSet().swap() && srManager.getMplsEcmp()))
Saurav Das7bcbe702017-06-13 15:35:54 -0700270 .filter(entry -> entry.getValue().containsNextHop(link.dst().deviceId()))
271 .map(entry -> entry.getKey())
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700272 .collect(Collectors.toSet());
Saurav Dasc88d4662017-05-15 15:34:25 -0700273
Saurav Das7bcbe702017-06-13 15:35:54 -0700274 log.debug("retryHash: dsNextObjStore contents for linkSrc {} -> linkDst {}: {}",
275 deviceId, link.dst().deviceId(), dsKeySet);
276
277 for (DestinationSetNextObjectiveStoreKey dsKey : dsKeySet) {
278 NextNeighbors nextHops = dsNextObjStore.get(dsKey);
279 if (nextHops == null) {
Saurav Dasc88d4662017-05-15 15:34:25 -0700280 log.warn("retryHash in device {}, but global store has no record "
Saurav Das7bcbe702017-06-13 15:35:54 -0700281 + "for dsKey:{}", deviceId, dsKey);
Saurav Dasc88d4662017-05-15 15:34:25 -0700282 continue;
283 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700284 int nextId = nextHops.nextId();
285 Set<DeviceId> dstSet = nextHops.getDstForNextHop(link.dst().deviceId());
Saurav Dasc88d4662017-05-15 15:34:25 -0700286 if (!linkDown) {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700287 List<PortLabel> pl = Lists.newArrayList();
Saurav Dasc88d4662017-05-15 15:34:25 -0700288 if (firstTime) {
289 // some links may have come up before the next-objective was created
290 // we take this opportunity to ensure other ports to same next-hop-dst
291 // are part of the hash group (see CORD-1180). Duplicate additions
292 // to the same hash group are avoided by the driver.
293 for (PortNumber p : devicePortMap.get(link.dst().deviceId())) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700294 dstSet.forEach(dst -> {
295 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
Charles Chane0260e12018-08-22 17:56:47 -0700296 pl.add(new PortLabel(p, edgeLabel, popVlanInHashGroup(dsKey.destinationSet())));
Saurav Das7bcbe702017-06-13 15:35:54 -0700297 });
Saurav Dasc88d4662017-05-15 15:34:25 -0700298 }
Saurav Das9df5b7c2017-08-14 16:44:43 -0700299 addToHashedNextObjective(pl, neighborMac, nextId);
300 } else {
301 // handle only the port that came up
302 dstSet.forEach(dst -> {
303 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
Charles Chane0260e12018-08-22 17:56:47 -0700304 pl.add(new PortLabel(link.src().port(), edgeLabel, popVlanInHashGroup(dsKey.destinationSet())));
Saurav Das9df5b7c2017-08-14 16:44:43 -0700305 });
306 addToHashedNextObjective(pl, neighborMac, nextId);
Saurav Dasc88d4662017-05-15 15:34:25 -0700307 }
308 } else {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700309 // linkdown
310 List<PortLabel> pl = Lists.newArrayList();
Saurav Das7bcbe702017-06-13 15:35:54 -0700311 dstSet.forEach(dst -> {
312 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
Charles Chane0260e12018-08-22 17:56:47 -0700313 pl.add(new PortLabel(link.src().port(), edgeLabel, popVlanInHashGroup(dsKey.destinationSet())));
Saurav Das7bcbe702017-06-13 15:35:54 -0700314 });
Saurav Das9df5b7c2017-08-14 16:44:43 -0700315 removeFromHashedNextObjective(pl, neighborMac, nextId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700316 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800317 }
Saurav Das1547b3f2017-05-05 17:01:08 -0700318 }
319
Saurav Dasc88d4662017-05-15 15:34:25 -0700320 /**
Saurav Das9df5b7c2017-08-14 16:44:43 -0700321 * Utility class for associating output ports and the corresponding MPLS
322 * labels to push. In dual-homing, there are different labels to push
323 * corresponding to the destination switches in an edge-pair. If both
324 * destinations are reachable via the same spine, then the output-port to
325 * the spine will be associated with two labels i.e. there will be two
326 * PortLabel objects for the same port but with different labels.
327 */
328 private class PortLabel {
329 PortNumber port;
330 int edgeLabel;
Charles Chane0260e12018-08-22 17:56:47 -0700331 boolean popVlan;
Saurav Das9df5b7c2017-08-14 16:44:43 -0700332
Charles Chane0260e12018-08-22 17:56:47 -0700333 PortLabel(PortNumber port, int edgeLabel, boolean popVlan) {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700334 this.port = port;
335 this.edgeLabel = edgeLabel;
Charles Chane0260e12018-08-22 17:56:47 -0700336 this.popVlan = popVlan;
Saurav Das9df5b7c2017-08-14 16:44:43 -0700337 }
338
339 @Override
340 public String toString() {
Charles Chane0260e12018-08-22 17:56:47 -0700341 return port.toString() + "/" + String.valueOf(edgeLabel) + (popVlan ? "/popVlan" : "");
Saurav Das9df5b7c2017-08-14 16:44:43 -0700342 }
343 }
344
345 /**
346 * Makes a call to the FlowObjective service to add buckets to
347 * a hashed group. User must ensure that all the ports & labels are meant
348 * same neighbor (ie. dstMac).
Saurav Dasc88d4662017-05-15 15:34:25 -0700349 *
Pier Luigi63edd932018-01-14 21:56:11 +0100350 * @param portLabels a collection of port & label combinations to add
Saurav Das9df5b7c2017-08-14 16:44:43 -0700351 * to the hash group identified by the nextId
Saurav Dasc88d4662017-05-15 15:34:25 -0700352 * @param dstMac destination mac address of next-hop
Saurav Das9df5b7c2017-08-14 16:44:43 -0700353 * @param nextId id for next-objective to which buckets will be added
Saurav Dasceccf242017-08-03 18:30:35 -0700354 *
Saurav Dasc88d4662017-05-15 15:34:25 -0700355 */
Saurav Das9df5b7c2017-08-14 16:44:43 -0700356 private void addToHashedNextObjective(Collection<PortLabel> portLabels,
357 MacAddress dstMac, Integer nextId) {
Saurav Das1547b3f2017-05-05 17:01:08 -0700358 // setup metadata to pass to nextObjective - indicate the vlan on egress
359 // if needed by the switch pipeline. Since hashed next-hops are always to
360 // other neighboring routers, there is no subnet assigned on those ports.
361 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
Saurav Das09c2c4d2018-08-13 15:34:26 -0700362 metabuilder.matchVlanId(srManager.getDefaultInternalVlan());
Saurav Das1547b3f2017-05-05 17:01:08 -0700363 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
364 .withId(nextId)
365 .withType(NextObjective.Type.HASHED)
Saurav Das1547b3f2017-05-05 17:01:08 -0700366 .withMeta(metabuilder.build())
367 .fromApp(appId);
Saurav Das9df5b7c2017-08-14 16:44:43 -0700368 // Create the new buckets to be updated
369 portLabels.forEach(pl -> {
370 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
371 tBuilder.setOutput(pl.port)
372 .setEthDst(dstMac)
373 .setEthSrc(nodeMacAddr);
Charles Chane0260e12018-08-22 17:56:47 -0700374 if (pl.popVlan) {
375 tBuilder.popVlan();
376 }
Saurav Das9df5b7c2017-08-14 16:44:43 -0700377 if (pl.edgeLabel != DestinationSet.NO_EDGE_LABEL) {
378 tBuilder.pushMpls()
379 .copyTtlOut()
380 .setMpls(MplsLabel.mplsLabel(pl.edgeLabel));
381 }
382 nextObjBuilder.addTreatment(tBuilder.build());
383 });
384
385 log.debug("addToHash in device {}: Adding Bucket with port/label {} "
386 + "to nextId {}", deviceId, portLabels, nextId);
Saurav Das1547b3f2017-05-05 17:01:08 -0700387
388 ObjectiveContext context = new DefaultObjectiveContext(
Saurav Das9df5b7c2017-08-14 16:44:43 -0700389 (objective) -> log.debug("addToHash port/label {} addedTo "
390 + "NextObj {} on {}", portLabels, nextId, deviceId),
Charles Chan55b806f2018-08-23 14:30:33 -0700391 (objective, error) -> {
392 log.warn("addToHash failed to add port/label {} to NextObj {} on {}: {}",
393 portLabels, nextId, deviceId, error);
394 srManager.invalidateNextObj(objective.id());
395 });
Saurav Das1547b3f2017-05-05 17:01:08 -0700396 NextObjective nextObjective = nextObjBuilder.addToExisting(context);
397 flowObjectiveService.next(deviceId, nextObjective);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800398 }
399
400 /**
Saurav Das9df5b7c2017-08-14 16:44:43 -0700401 * Makes a call to the FlowObjective service to remove buckets from
402 * a hash group. User must ensure that all the ports & labels are meant
403 * same neighbor (ie. dstMac).
Saurav Dasceccf242017-08-03 18:30:35 -0700404 *
Pier Luigi63edd932018-01-14 21:56:11 +0100405 * @param portLabels a collection of port & label combinations to remove
Saurav Das9df5b7c2017-08-14 16:44:43 -0700406 * from the hash group identified by the nextId
Saurav Dasceccf242017-08-03 18:30:35 -0700407 * @param dstMac destination mac address of next-hop
Saurav Das9df5b7c2017-08-14 16:44:43 -0700408 * @param nextId id for next-objective from which buckets will be removed
Saurav Dasceccf242017-08-03 18:30:35 -0700409 */
Saurav Das9df5b7c2017-08-14 16:44:43 -0700410 private void removeFromHashedNextObjective(Collection<PortLabel> portLabels,
411 MacAddress dstMac, Integer nextId) {
Charles Chan67ef2552018-08-30 19:49:23 -0700412 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
413 metabuilder.matchVlanId(srManager.getDefaultInternalVlan());
Saurav Dasceccf242017-08-03 18:30:35 -0700414 NextObjective.Builder nextObjBuilder = DefaultNextObjective
415 .builder()
416 .withType(NextObjective.Type.HASHED) //same as original
Charles Chan67ef2552018-08-30 19:49:23 -0700417 .withMeta(metabuilder.build())
Saurav Dasceccf242017-08-03 18:30:35 -0700418 .withId(nextId)
Saurav Das9df5b7c2017-08-14 16:44:43 -0700419 .fromApp(appId);
420 // Create the buckets to be removed
421 portLabels.forEach(pl -> {
422 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
423 tBuilder.setOutput(pl.port)
424 .setEthDst(dstMac)
425 .setEthSrc(nodeMacAddr);
Charles Chane0260e12018-08-22 17:56:47 -0700426 if (pl.popVlan) {
427 tBuilder.popVlan();
428 }
Saurav Das9df5b7c2017-08-14 16:44:43 -0700429 if (pl.edgeLabel != DestinationSet.NO_EDGE_LABEL) {
430 tBuilder.pushMpls()
431 .copyTtlOut()
432 .setMpls(MplsLabel.mplsLabel(pl.edgeLabel));
433 }
434 nextObjBuilder.addTreatment(tBuilder.build());
435 });
436 log.debug("removeFromHash in device {}: Removing Bucket with port/label"
437 + " {} from nextId {}", deviceId, portLabels, nextId);
Saurav Dasc88d4662017-05-15 15:34:25 -0700438
Saurav Das9df5b7c2017-08-14 16:44:43 -0700439 ObjectiveContext context = new DefaultObjectiveContext(
440 (objective) -> log.debug("port/label {} removedFrom NextObj"
441 + " {} on {}", portLabels, nextId, deviceId),
Charles Chan55b806f2018-08-23 14:30:33 -0700442 (objective, error) -> {
443 log.warn("port/label {} failed to removeFrom NextObj {} on {}: {}",
444 portLabels, nextId, deviceId, error);
445 srManager.invalidateNextObj(objective.id());
446 });
Saurav Das9df5b7c2017-08-14 16:44:43 -0700447 NextObjective nextObjective = nextObjBuilder.removeFromExisting(context);
Saurav Dasceccf242017-08-03 18:30:35 -0700448 flowObjectiveService.next(deviceId, nextObjective);
449 }
Saurav Dasc88d4662017-05-15 15:34:25 -0700450
451 /**
452 * Checks all the hash-groups in the target-switch meant for the destination
453 * switch, and either adds or removes buckets to make the neighbor-set
454 * match the given next-hops. Typically called by the master instance of the
455 * destination switch, which may be different from the master instance of the
456 * target switch where hash-group changes are made.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800457 *
Saurav Dasc88d4662017-05-15 15:34:25 -0700458 * @param targetSw the switch in which the hash groups will be edited
459 * @param nextHops the current next hops for the target switch to reach
460 * the dest sw
461 * @param destSw the destination switch
462 * @param revoke true if hash groups need to remove buckets from the
463 * the groups to match the current next hops
464 * @return true if calls are made to edit buckets, or if no edits are required
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800465 */
Saurav Dasc88d4662017-05-15 15:34:25 -0700466 public boolean fixHashGroups(DeviceId targetSw, Set<DeviceId> nextHops,
467 DeviceId destSw, boolean revoke) {
468 // temporary storage of keys to be updated
Saurav Das7bcbe702017-06-13 15:35:54 -0700469 Map<DestinationSetNextObjectiveStoreKey, Set<DeviceId>> tempStore =
Saurav Dasc88d4662017-05-15 15:34:25 -0700470 new HashMap<>();
Saurav Das9df5b7c2017-08-14 16:44:43 -0700471 boolean foundNextObjective = false, success = true;
Charles Chan0b4e6182015-11-03 10:42:14 -0800472
Saurav Das7bcbe702017-06-13 15:35:54 -0700473 // retrieve hash-groups meant for destSw, which have destinationSets
Saurav Dasc88d4662017-05-15 15:34:25 -0700474 // with different neighbors than the given next-hops
Saurav Das7bcbe702017-06-13 15:35:54 -0700475 for (DestinationSetNextObjectiveStoreKey dskey : dsNextObjStore.keySet()) {
476 if (!dskey.deviceId().equals(targetSw) ||
477 !dskey.destinationSet().getDestinationSwitches().contains(destSw)) {
Saurav Dasc88d4662017-05-15 15:34:25 -0700478 continue;
479 }
480 foundNextObjective = true;
Saurav Das7bcbe702017-06-13 15:35:54 -0700481 NextNeighbors nhops = dsNextObjStore.get(dskey);
482 Set<DeviceId> currNeighbors = nhops.nextHops(destSw);
483 int edgeLabel = dskey.destinationSet().getEdgeLabel(destSw);
484 Integer nextId = nhops.nextId();
Saurav Dase0d4c872018-03-05 14:37:16 -0800485 if (currNeighbors == null || nextHops == null) {
486 log.warn("fixing hash groups but found currNeighbors:{} or nextHops:{}"
487 + " in targetSw:{} for dstSw:{}", currNeighbors,
488 nextHops, targetSw, destSw);
489 success &= false;
490 continue;
491 }
Charles Chan0b4e6182015-11-03 10:42:14 -0800492
Saurav Dasa4020382018-02-14 14:14:54 -0800493 // some store elements may not be hashed next-objectives - ignore them
494 if (isSimpleNextObjective(dskey)) {
495 log.debug("Ignoring {} of SIMPLE nextObj for targetSw:{}"
496 + " -> dstSw:{} with current nextHops:{} to new"
497 + " nextHops: {} in nextId:{}",
498 (revoke) ? "removal" : "addition", targetSw, destSw,
499 currNeighbors, nextHops, nextId);
Saurav Dase0d4c872018-03-05 14:37:16 -0800500 if ((revoke && !nextHops.isEmpty())
501 || (!revoke && !nextHops.equals(currNeighbors))) {
Saurav Das137f27f2018-06-11 17:02:31 -0700502 log.debug("Simple next objective cannot be edited to "
Saurav Dasa4020382018-02-14 14:14:54 -0800503 + "move from {} to {}", currNeighbors, nextHops);
504 }
505 continue;
506 }
507
Saurav Dasc88d4662017-05-15 15:34:25 -0700508 Set<DeviceId> diff;
509 if (revoke) {
510 diff = Sets.difference(currNeighbors, nextHops);
511 log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next "
512 + "hops:{} ..removing {}", targetSw, destSw, nextId,
513 currNeighbors, diff);
514 } else {
515 diff = Sets.difference(nextHops, currNeighbors);
516 log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next "
517 + "hops:{} ..adding {}", targetSw, destSw, nextId,
518 currNeighbors, diff);
519 }
Charles Chane0260e12018-08-22 17:56:47 -0700520 boolean suc = updateAllPortsToNextHop(diff, edgeLabel, nextId, popVlanInHashGroup(dskey.destinationSet()),
Saurav Das9df5b7c2017-08-14 16:44:43 -0700521 revoke);
522 if (suc) {
523 // to update neighbor set with changes made
Saurav Dasc88d4662017-05-15 15:34:25 -0700524 if (revoke) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700525 tempStore.put(dskey, Sets.difference(currNeighbors, diff));
Saurav Dasc88d4662017-05-15 15:34:25 -0700526 } else {
Saurav Das7bcbe702017-06-13 15:35:54 -0700527 tempStore.put(dskey, Sets.union(currNeighbors, diff));
Saurav Dasc88d4662017-05-15 15:34:25 -0700528 }
sangho834e4b02015-05-01 09:38:25 -0700529 }
Saurav Das9df5b7c2017-08-14 16:44:43 -0700530 success &= suc;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800531 }
532
Saurav Dasc88d4662017-05-15 15:34:25 -0700533 if (!foundNextObjective) {
534 log.debug("Cannot find any nextObjectives for route targetSw:{} "
535 + "-> dstSw:{}", targetSw, destSw);
536 return true; // nothing to do, return true so ECMPspg is updated
537 }
538
Saurav Das7bcbe702017-06-13 15:35:54 -0700539 // update the dsNextObjectiveStore with new destinationSet to nextId mappings
540 for (DestinationSetNextObjectiveStoreKey key : tempStore.keySet()) {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700541 NextNeighbors currentNextHops = dsNextObjStore.get(key);
542 if (currentNextHops == null) {
543 log.warn("fixHashGroups could not update global store in "
544 + "device {} .. missing nextNeighbors for key {}",
545 deviceId, key);
Saurav Dasc88d4662017-05-15 15:34:25 -0700546 continue;
547 }
Saurav Das9df5b7c2017-08-14 16:44:43 -0700548 Set<DeviceId> newNeighbors = new HashSet<>();
549 newNeighbors.addAll(tempStore.get(key));
550 Map<DeviceId, Set<DeviceId>> oldDstNextHops =
551 ImmutableMap.copyOf(currentNextHops.dstNextHops());
552 currentNextHops.dstNextHops().put(destSw, newNeighbors); //local change
553 log.debug("Updating nsNextObjStore target:{} -> dst:{} in key:{} nextId:{}",
554 targetSw, destSw, key, currentNextHops.nextId());
555 log.debug("Old dstNextHops: {}", oldDstNextHops);
556 log.debug("New dstNextHops: {}", currentNextHops.dstNextHops());
557 // update global store
558 dsNextObjStore.put(key,
559 new NextNeighbors(currentNextHops.dstNextHops(),
560 currentNextHops.nextId()));
Saurav Dasc88d4662017-05-15 15:34:25 -0700561 }
Saurav Dasa4020382018-02-14 14:14:54 -0800562
Saurav Das9df5b7c2017-08-14 16:44:43 -0700563 // even if one fails and others succeed, return false so ECMPspg not updated
564 return success;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800565 }
566
Saurav Dasceccf242017-08-03 18:30:35 -0700567 /**
568 * Updates the DestinationSetNextObjectiveStore with any per-destination nexthops
569 * that are not already in the store for the given DestinationSet. Note that
570 * this method does not remove existing next hops for the destinations in the
571 * DestinationSet.
572 *
573 * @param ds the DestinationSet for which the next hops need to be updated
574 * @param newDstNextHops a map of per-destination next hops to update the
575 * destinationSet with
576 * @return true if successful in updating all next hops
577 */
578 private boolean updateNextHops(DestinationSet ds,
Saurav Das7bcbe702017-06-13 15:35:54 -0700579 Map<DeviceId, Set<DeviceId>> newDstNextHops) {
580 DestinationSetNextObjectiveStoreKey key =
581 new DestinationSetNextObjectiveStoreKey(deviceId, ds);
582 NextNeighbors currNext = dsNextObjStore.get(key);
583 Map<DeviceId, Set<DeviceId>> currDstNextHops = currNext.dstNextHops();
584
585 // add newDstNextHops to currDstNextHops for each dst
586 boolean success = true;
587 for (DeviceId dstSw : ds.getDestinationSwitches()) {
588 Set<DeviceId> currNhops = currDstNextHops.get(dstSw);
589 Set<DeviceId> newNhops = newDstNextHops.get(dstSw);
590 currNhops = (currNhops == null) ? Sets.newHashSet() : currNhops;
591 newNhops = (newNhops == null) ? Sets.newHashSet() : newNhops;
592 int edgeLabel = ds.getEdgeLabel(dstSw);
593 int nextId = currNext.nextId();
594
595 // new next hops should be added
596 boolean suc = updateAllPortsToNextHop(Sets.difference(newNhops, currNhops),
Charles Chane0260e12018-08-22 17:56:47 -0700597 edgeLabel, nextId, popVlanInHashGroup(key.destinationSet()), false);
Saurav Das7bcbe702017-06-13 15:35:54 -0700598 if (suc) {
599 currNhops.addAll(newNhops);
600 currDstNextHops.put(dstSw, currNhops); // this is only a local change
601 }
602 success &= suc;
603 }
604
605 if (success) {
606 // update global store
607 dsNextObjStore.put(key, new NextNeighbors(currDstNextHops,
608 currNext.nextId()));
609 log.debug("Updated device:{} ds:{} new next-hops: {}", deviceId, ds,
610 dsNextObjStore.get(key));
611 }
612 return success;
613 }
614
Saurav Dasceccf242017-08-03 18:30:35 -0700615 /**
Saurav Das9df5b7c2017-08-14 16:44:43 -0700616 * Adds or removes buckets for all ports to a set of neighbor devices. Caller
617 * needs to ensure that the given neighbors are all next hops towards the
618 * same destination (represented by the given edgeLabel).
Saurav Dasceccf242017-08-03 18:30:35 -0700619 *
620 * @param neighbors set of neighbor device ids
621 * @param edgeLabel MPLS label to use in buckets
622 * @param nextId the nextObjective to change
Charles Chane0260e12018-08-22 17:56:47 -0700623 * @param popVlan this hash group bucket shuold includes a popVlan action
Saurav Dasceccf242017-08-03 18:30:35 -0700624 * @param revoke true if buckets need to be removed, false if they need to
625 * be added
626 * @return true if successful in adding or removing buckets for all ports
627 * to the neighbors
628 */
629 private boolean updateAllPortsToNextHop(Set<DeviceId> neighbors, int edgeLabel,
Charles Chane0260e12018-08-22 17:56:47 -0700630 int nextId, boolean popVlan, boolean revoke) {
Saurav Dasceccf242017-08-03 18:30:35 -0700631 for (DeviceId neighbor : neighbors) {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700632 MacAddress neighborMac;
Saurav Das7bcbe702017-06-13 15:35:54 -0700633 try {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700634 neighborMac = deviceConfig.getDeviceMac(neighbor);
Saurav Das7bcbe702017-06-13 15:35:54 -0700635 } catch (DeviceConfigNotFoundException e) {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700636 log.warn(e.getMessage() + " Aborting updateAllPortsToNextHop"
637 + " for nextId:" + nextId);
Saurav Das7bcbe702017-06-13 15:35:54 -0700638 return false;
639 }
Saurav Das9df5b7c2017-08-14 16:44:43 -0700640 Collection<PortNumber> portsToNeighbor = devicePortMap.get(neighbor);
641 if (portsToNeighbor == null || portsToNeighbor.isEmpty()) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700642 log.warn("No ports found in dev:{} for neighbor:{} .. cannot "
Saurav Das9df5b7c2017-08-14 16:44:43 -0700643 + "updateAllPortsToNextHop for nextId: {}",
Saurav Das7bcbe702017-06-13 15:35:54 -0700644 deviceId, neighbor, nextId);
645 return false;
646 }
Saurav Das9df5b7c2017-08-14 16:44:43 -0700647 List<PortLabel> pl = Lists.newArrayList();
Charles Chane0260e12018-08-22 17:56:47 -0700648 portsToNeighbor.forEach(p -> pl.add(new PortLabel(p, edgeLabel, popVlan)));
Saurav Das7bcbe702017-06-13 15:35:54 -0700649 if (revoke) {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700650 log.debug("updateAllPortsToNextHops in device {}: Removing Bucket(s) "
651 + "with Port/Label:{} to next object id {}",
652 deviceId, pl, nextId);
653 removeFromHashedNextObjective(pl, neighborMac, nextId);
Saurav Das7bcbe702017-06-13 15:35:54 -0700654 } else {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700655 log.debug("fixHashGroup in device {}: Adding Bucket(s) "
656 + "with Port/Label: {} to next object id {}",
657 deviceId, pl, nextId);
658 addToHashedNextObjective(pl, neighborMac, nextId);
Saurav Das7bcbe702017-06-13 15:35:54 -0700659 }
660 }
661 return true;
662 }
663
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800664 /**
Saurav Dasa4020382018-02-14 14:14:54 -0800665 * Returns true if the destination set is meant for swap or multi-labeled
666 * packet transport, and MPLS ECMP is not supported.
667 *
668 * @param dskey the key representing the destination set
669 * @return true if destination set is meant for simple next objectives
670 */
671 boolean isSimpleNextObjective(DestinationSetNextObjectiveStoreKey dskey) {
672 return (dskey.destinationSet().notBos() || dskey.destinationSet().swap())
673 && !srManager.getMplsEcmp();
674 }
675
676 /**
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800677 * Adds or removes a port that has been configured with a vlan to a broadcast group
678 * for bridging. Should only be called by the master instance for this device.
Saurav Das1a129a02016-11-18 15:21:57 -0800679 *
680 * @param port the port on this device that needs to be added/removed to a bcast group
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800681 * @param vlanId the vlan id corresponding to the broadcast domain/group
682 * @param popVlan indicates if packets should be sent out untagged or not out
683 * of the port. If true, indicates an access (untagged) or native vlan
684 * configuration. If false, indicates a trunk (tagged) vlan config.
Saurav Das1a129a02016-11-18 15:21:57 -0800685 * @param portUp true if port is enabled, false if disabled
Saurav Das1a129a02016-11-18 15:21:57 -0800686 */
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800687 public void processEdgePort(PortNumber port, VlanId vlanId,
688 boolean popVlan, boolean portUp) {
Saurav Das1a129a02016-11-18 15:21:57 -0800689 //get the next id for the subnet and edit it.
Charles Chan59cc16d2017-02-02 16:20:42 -0800690 Integer nextId = getVlanNextObjectiveId(vlanId);
Saurav Das1a129a02016-11-18 15:21:57 -0800691 if (nextId == -1) {
692 if (portUp) {
693 log.debug("**Creating flooding group for first port enabled in"
Saurav Dasf14d9ef2017-12-05 15:00:23 -0800694 + " vlan {} on dev {} port {}", vlanId, deviceId, port);
Charles Chan59cc16d2017-02-02 16:20:42 -0800695 createBcastGroupFromVlan(vlanId, Collections.singleton(port));
Saurav Das1a129a02016-11-18 15:21:57 -0800696 } else {
697 log.warn("Could not find flooding group for subnet {} on dev:{} when"
Charles Chan59cc16d2017-02-02 16:20:42 -0800698 + " removing port:{}", vlanId, deviceId, port);
Saurav Das1a129a02016-11-18 15:21:57 -0800699 }
700 return;
701 }
702
703 log.info("**port{} in device {}: {} Bucket with Port {} to"
704 + " next-id {}", (portUp) ? "UP" : "DOWN", deviceId,
705 (portUp) ? "Adding" : "Removing",
706 port, nextId);
707 // Create the bucket to be added or removed
708 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800709 if (popVlan) {
710 tBuilder.popVlan();
711 }
Saurav Das1a129a02016-11-18 15:21:57 -0800712 tBuilder.setOutput(port);
713
Saurav Das1a129a02016-11-18 15:21:57 -0800714 TrafficSelector metadata =
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800715 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
Saurav Das1a129a02016-11-18 15:21:57 -0800716
717 NextObjective.Builder nextObjBuilder = DefaultNextObjective
718 .builder().withId(nextId)
719 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
720 .addTreatment(tBuilder.build())
721 .withMeta(metadata);
722
723 ObjectiveContext context = new DefaultObjectiveContext(
724 (objective) -> log.debug("port {} successfully {} NextObj {} on {}",
725 port, (portUp) ? "addedTo" : "removedFrom",
726 nextId, deviceId),
Charles Chan55b806f2018-08-23 14:30:33 -0700727 (objective, error) -> {
728 log.warn("port {} failed to {} NextObj {} on {}: {}",
729 port, (portUp) ? "addTo" : "removeFrom", nextId, deviceId, error);
730 srManager.invalidateNextObj(objective.id());
731 });
Saurav Das1a129a02016-11-18 15:21:57 -0800732
733 NextObjective nextObj = (portUp) ? nextObjBuilder.addToExisting(context)
734 : nextObjBuilder.removeFromExisting(context);
735 log.debug("edgePort processed: Submited next objective {} in device {}",
736 nextId, deviceId);
737 flowObjectiveService.next(deviceId, nextObj);
738 }
739
740 /**
Saurav Dasa4020382018-02-14 14:14:54 -0800741 * Returns the next objective of type hashed (or simple) associated with the
742 * destination set. In addition, updates the existing next-objective if new
743 * route-paths found have resulted in the addition of new next-hops to a
744 * particular destination. If there is no existing next objective for this
745 * destination set, this method would create a next objective and return the
746 * nextId. Optionally metadata can be passed in for the creation of the next
747 * objective. If the parameter simple is true then a simple next objective
748 * is created instead of a hashed one.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800749 *
Saurav Das7bcbe702017-06-13 15:35:54 -0700750 * @param ds destination set
751 * @param nextHops a map of per destination next hops
Saurav Das8a0732e2015-11-20 15:27:53 -0800752 * @param meta metadata passed into the creation of a Next Objective
Saurav Dasa4020382018-02-14 14:14:54 -0800753 * @param simple if true, a simple next objective will be created instead of
754 * a hashed next objective
Saurav Das8a0732e2015-11-20 15:27:53 -0800755 * @return int if found or -1 if there are errors in the creation of the
Saurav Dasa4020382018-02-14 14:14:54 -0800756 * neighbor set.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800757 */
Saurav Das7bcbe702017-06-13 15:35:54 -0700758 public int getNextObjectiveId(DestinationSet ds,
759 Map<DeviceId, Set<DeviceId>> nextHops,
Saurav Dasa4020382018-02-14 14:14:54 -0800760 TrafficSelector meta, boolean simple) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700761 NextNeighbors next = dsNextObjStore.
762 get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
763 if (next == null) {
764 log.debug("getNextObjectiveId in device{}: Next objective id "
765 + "not found for {} ... creating", deviceId, ds);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700766 log.trace("getNextObjectiveId: nsNextObjStore contents for device {}: {}",
767 deviceId,
Saurav Das7bcbe702017-06-13 15:35:54 -0700768 dsNextObjStore.entrySet()
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700769 .stream()
770 .filter((nsStoreEntry) ->
771 (nsStoreEntry.getKey().deviceId().equals(deviceId)))
772 .collect(Collectors.toList()));
Saurav Das7bcbe702017-06-13 15:35:54 -0700773
Saurav Dasa4020382018-02-14 14:14:54 -0800774 createGroupFromDestinationSet(ds, nextHops, meta, simple);
Saurav Das7bcbe702017-06-13 15:35:54 -0700775 next = dsNextObjStore.
776 get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
777 if (next == null) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700778 log.warn("getNextObjectiveId: unable to create next objective");
Saurav Das7bcbe702017-06-13 15:35:54 -0700779 // failure in creating group
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700780 return -1;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700781 } else {
782 log.debug("getNextObjectiveId in device{}: Next objective id {} "
Saurav Das7bcbe702017-06-13 15:35:54 -0700783 + "created for {}", deviceId, next.nextId(), ds);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700784 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700785 } else {
786 log.trace("getNextObjectiveId in device{}: Next objective id {} "
Saurav Das7bcbe702017-06-13 15:35:54 -0700787 + "found for {}", deviceId, next.nextId(), ds);
788 // should fix hash groups too if next-hops have changed
789 if (!next.dstNextHops().equals(nextHops)) {
790 log.debug("Nexthops have changed for dev:{} nextId:{} ..updating",
791 deviceId, next.nextId());
792 if (!updateNextHops(ds, nextHops)) {
793 // failure in updating group
794 return -1;
795 }
796 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700797 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700798 return next.nextId();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800799 }
800
sangho0b2b6d12015-05-20 22:16:38 -0700801 /**
Charles Chan59cc16d2017-02-02 16:20:42 -0800802 * Returns the next objective of type broadcast associated with the vlan,
Saurav Das4ce45962015-11-24 23:21:05 -0800803 * or -1 if no such objective exists. Note that this method does NOT create
804 * the next objective as a side-effect. It is expected that is objective is
Saurav Das1a129a02016-11-18 15:21:57 -0800805 * created at startup from network configuration. Typically this is used
806 * for L2 flooding within the subnet configured on the switch.
Charles Chanc42e84e2015-10-20 16:24:19 -0700807 *
Charles Chan59cc16d2017-02-02 16:20:42 -0800808 * @param vlanId vlan id
Charles Chanc42e84e2015-10-20 16:24:19 -0700809 * @return int if found or -1
810 */
Charles Chan59cc16d2017-02-02 16:20:42 -0800811 public int getVlanNextObjectiveId(VlanId vlanId) {
812 Integer nextId = vlanNextObjStore.
813 get(new VlanNextObjectiveStoreKey(deviceId, vlanId));
Charles Chan9f676b62015-10-29 14:58:10 -0700814
815 return (nextId != null) ? nextId : -1;
Charles Chanc42e84e2015-10-20 16:24:19 -0700816 }
817
818 /**
Saurav Das4ce45962015-11-24 23:21:05 -0800819 * Returns the next objective of type simple associated with the port on the
820 * device, given the treatment. Different treatments to the same port result
821 * in different next objectives. If no such objective exists, this method
Saurav Das961beb22017-03-29 19:09:17 -0700822 * creates one (if requested) and returns the id. Optionally metadata can be passed in for
Saurav Das1a129a02016-11-18 15:21:57 -0800823 * the creation of the objective. Typically this is used for L2 and L3 forwarding
824 * to compute nodes and containers/VMs on the compute nodes directly attached
825 * to the switch.
Saurav Das4ce45962015-11-24 23:21:05 -0800826 *
827 * @param portNum the port number for the simple next objective
828 * @param treatment the actions to apply on the packets (should include outport)
829 * @param meta optional metadata passed into the creation of the next objective
Saurav Das961beb22017-03-29 19:09:17 -0700830 * @param createIfMissing true if a next object should be created if not found
Saurav Das4ce45962015-11-24 23:21:05 -0800831 * @return int if found or created, -1 if there are errors during the
832 * creation of the next objective.
833 */
834 public int getPortNextObjectiveId(PortNumber portNum, TrafficTreatment treatment,
Saurav Das961beb22017-03-29 19:09:17 -0700835 TrafficSelector meta, boolean createIfMissing) {
Charles Chane849c192016-01-11 18:28:54 -0800836 Integer nextId = portNextObjStore
Saurav Das76ae6812017-03-15 15:15:14 -0700837 .get(new PortNextObjectiveStoreKey(deviceId, portNum, treatment, meta));
Saurav Das961beb22017-03-29 19:09:17 -0700838 if (nextId != null) {
839 return nextId;
840 }
841 log.debug("getPortNextObjectiveId in device {}: Next objective id "
842 + "not found for port: {} .. {}", deviceId, portNum,
843 (createIfMissing) ? "creating" : "aborting");
844 if (!createIfMissing) {
845 return -1;
846 }
847 // create missing next objective
848 createGroupFromPort(portNum, treatment, meta);
849 nextId = portNextObjStore.get(new PortNextObjectiveStoreKey(deviceId, portNum,
850 treatment, meta));
Saurav Das4ce45962015-11-24 23:21:05 -0800851 if (nextId == null) {
Saurav Das961beb22017-03-29 19:09:17 -0700852 log.warn("getPortNextObjectiveId: unable to create next obj"
853 + "for dev:{} port:{}", deviceId, portNum);
854 return -1;
Charles Chane849c192016-01-11 18:28:54 -0800855 }
856 return nextId;
857 }
858
859 /**
sangho0b2b6d12015-05-20 22:16:38 -0700860 * Checks if the next objective ID (group) for the neighbor set exists or not.
861 *
862 * @param ns neighbor set to check
863 * @return true if it exists, false otherwise
864 */
Saurav Das7bcbe702017-06-13 15:35:54 -0700865 public boolean hasNextObjectiveId(DestinationSet ns) {
866 NextNeighbors nextHops = dsNextObjStore.
867 get(new DestinationSetNextObjectiveStoreKey(deviceId, ns));
868 if (nextHops == null) {
sangho0b2b6d12015-05-20 22:16:38 -0700869 return false;
870 }
871
872 return true;
873 }
874
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800875 private void populateNeighborMaps() {
876 Set<Link> outgoingLinks = linkService.getDeviceEgressLinks(deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700877 for (Link link : outgoingLinks) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800878 if (link.type() != Link.Type.DIRECT) {
879 continue;
880 }
881 addNeighborAtPort(link.dst().deviceId(), link.src().port());
882 }
883 }
884
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700885 protected void addNeighborAtPort(DeviceId neighborId,
886 PortNumber portToNeighbor) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800887 // Update DeviceToPort database
888 log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
889 deviceId, neighborId, portToNeighbor);
Saurav Das8a0732e2015-11-20 15:27:53 -0800890 Set<PortNumber> ports = Collections
891 .newSetFromMap(new ConcurrentHashMap<PortNumber, Boolean>());
892 ports.add(portToNeighbor);
893 Set<PortNumber> portnums = devicePortMap.putIfAbsent(neighborId, ports);
894 if (portnums != null) {
895 portnums.add(portToNeighbor);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800896 }
897
898 // Update portToDevice database
Saurav Dasa4020382018-02-14 14:14:54 -0800899 // should always update as neighbor could have changed on this port
900 DeviceId prev = portDeviceMap.put(portToNeighbor, neighborId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800901 if (prev != null) {
Saurav Das5a356042018-04-06 20:16:01 -0700902 log.warn("Device/port: {}/{} previous neighbor: {}, current neighbor: {} ",
Saurav Dasc88d4662017-05-15 15:34:25 -0700903 deviceId, portToNeighbor, prev, neighborId);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800904 }
905 }
906
sangho1e575652015-05-14 00:39:53 -0700907 /**
Saurav Das7bcbe702017-06-13 15:35:54 -0700908 * Creates a NextObjective for a hash group in this device from a given
Saurav Dasa4020382018-02-14 14:14:54 -0800909 * DestinationSet. If the parameter simple is true, a simple next objective
910 * is created instead.
sangho1e575652015-05-14 00:39:53 -0700911 *
Saurav Das7bcbe702017-06-13 15:35:54 -0700912 * @param ds the DestinationSet
913 * @param neighbors a map for each destination and its next-hops
Saurav Das8a0732e2015-11-20 15:27:53 -0800914 * @param meta metadata passed into the creation of a Next Objective
Saurav Dasa4020382018-02-14 14:14:54 -0800915 * @param simple if true, a simple next objective will be created instead of
916 * a hashed next objective
sangho1e575652015-05-14 00:39:53 -0700917 */
Saurav Das7bcbe702017-06-13 15:35:54 -0700918 public void createGroupFromDestinationSet(DestinationSet ds,
919 Map<DeviceId, Set<DeviceId>> neighbors,
920 TrafficSelector meta,
Saurav Dasa4020382018-02-14 14:14:54 -0800921 boolean simple) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700922 int nextId = flowObjectiveService.allocateNextId();
Saurav Dasa4020382018-02-14 14:14:54 -0800923 NextObjective.Type type = (simple) ? NextObjective.Type.SIMPLE
924 : NextObjective.Type.HASHED;
Saurav Das7bcbe702017-06-13 15:35:54 -0700925 if (neighbors == null || neighbors.isEmpty()) {
926 log.warn("createGroupsFromDestinationSet: needs at least one neighbor"
927 + "to create group in dev:{} for ds: {} with next-hops {}",
928 deviceId, ds, neighbors);
929 return;
930 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700931
932 NextObjective.Builder nextObjBuilder = DefaultNextObjective
933 .builder()
934 .withId(nextId)
935 .withType(type)
936 .fromApp(appId);
937 if (meta != null) {
938 nextObjBuilder.withMeta(meta);
939 }
940
941 // create treatment buckets for each neighbor for each dst Device
942 // except in the special case where we only want to pick a single
Saurav Dasa4020382018-02-14 14:14:54 -0800943 // neighbor/port for a simple nextObj
Saurav Das7bcbe702017-06-13 15:35:54 -0700944 boolean foundSingleNeighbor = false;
945 boolean treatmentAdded = false;
946 Map<DeviceId, Set<DeviceId>> dstNextHops = new ConcurrentHashMap<>();
947 for (DeviceId dst : ds.getDestinationSwitches()) {
948 Set<DeviceId> nextHops = neighbors.get(dst);
949 if (nextHops == null || nextHops.isEmpty()) {
950 continue;
Pier Ventre917127a2016-10-31 16:49:19 -0700951 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700952
953 if (foundSingleNeighbor) {
954 break;
955 }
956
957 for (DeviceId neighborId : nextHops) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800958 if (devicePortMap.get(neighborId) == null) {
959 log.warn("Neighbor {} is not in the port map yet for dev:{}",
960 neighborId, deviceId);
sangho834e4b02015-05-01 09:38:25 -0700961 return;
Jon Hallcbd1b392017-01-18 20:15:44 -0800962 } else if (devicePortMap.get(neighborId).isEmpty()) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700963 log.warn("There are no ports for "
Saurav Das8a0732e2015-11-20 15:27:53 -0800964 + "the Device {} in the port map yet", neighborId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700965 return;
sangho834e4b02015-05-01 09:38:25 -0700966 }
967
Saurav Das8a0732e2015-11-20 15:27:53 -0800968 MacAddress neighborMac;
Charles Chan0b4e6182015-11-03 10:42:14 -0800969 try {
Saurav Das8a0732e2015-11-20 15:27:53 -0800970 neighborMac = deviceConfig.getDeviceMac(neighborId);
Charles Chan0b4e6182015-11-03 10:42:14 -0800971 } catch (DeviceConfigNotFoundException e) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700972 log.warn(e.getMessage() + " Aborting createGroupsFromDestinationset.");
Charles Chan0b4e6182015-11-03 10:42:14 -0800973 return;
974 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700975 // For each port to the neighbor, we create a new treatment
Pier Ventre917127a2016-10-31 16:49:19 -0700976 Set<PortNumber> neighborPorts = devicePortMap.get(neighborId);
Saurav Dasa4020382018-02-14 14:14:54 -0800977 // In this case we need a SIMPLE nextObj. We randomly pick a port
978 if (simple) {
Pier Ventre917127a2016-10-31 16:49:19 -0700979 int size = devicePortMap.get(neighborId).size();
980 int index = RandomUtils.nextInt(0, size);
981 neighborPorts = Collections.singleton(
Saurav Das7bcbe702017-06-13 15:35:54 -0700982 Iterables.get(devicePortMap.get(neighborId),
983 index));
984 foundSingleNeighbor = true;
Pier Ventre917127a2016-10-31 16:49:19 -0700985 }
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700986
Pier Ventre917127a2016-10-31 16:49:19 -0700987 for (PortNumber sp : neighborPorts) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700988 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
989 .builder();
Saurav Das7bcbe702017-06-13 15:35:54 -0700990 tBuilder.setEthDst(neighborMac).setEthSrc(nodeMacAddr);
991 int edgeLabel = ds.getEdgeLabel(dst);
992 if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
Saurav Dasa4020382018-02-14 14:14:54 -0800993 if (simple) {
994 // swap label case
995 tBuilder.setMpls(MplsLabel.mplsLabel(edgeLabel));
996 } else {
997 // ecmp with label push case
998 tBuilder.pushMpls().copyTtlOut()
999 .setMpls(MplsLabel.mplsLabel(edgeLabel));
1000 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001001 }
Charles Chan69b7ae62018-08-12 15:19:01 -07001002
1003 // Set VLAN ID for PW transport. Otherwise pop vlan
Charles Chane0260e12018-08-22 17:56:47 -07001004 if (!popVlanInHashGroup(ds)) {
Saurav Das09c2c4d2018-08-13 15:34:26 -07001005 tBuilder.setVlanId(srManager.getPwTransportVlan());
Charles Chan69b7ae62018-08-12 15:19:01 -07001006 } else {
1007 tBuilder.popVlan();
Andreas Pantelopoulos192284c2018-05-01 14:56:05 -07001008 }
Charles Chan69b7ae62018-08-12 15:19:01 -07001009
Saurav Das8a0732e2015-11-20 15:27:53 -08001010 tBuilder.setOutput(sp);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001011 nextObjBuilder.addTreatment(tBuilder.build());
Saurav Das7bcbe702017-06-13 15:35:54 -07001012 treatmentAdded = true;
1013 //update store
1014 Set<DeviceId> existingNeighbors = dstNextHops.get(dst);
1015 if (existingNeighbors == null) {
1016 existingNeighbors = new HashSet<>();
1017 }
1018 existingNeighbors.add(neighborId);
1019 dstNextHops.put(dst, existingNeighbors);
1020 log.debug("creating treatment for port/label {}/{} in next:{}",
1021 sp, edgeLabel, nextId);
1022 }
1023
1024 if (foundSingleNeighbor) {
1025 break;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -08001026 }
1027 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -08001028 }
Saurav Das7bcbe702017-06-13 15:35:54 -07001029
1030 if (!treatmentAdded) {
1031 log.warn("Could not createGroup from DestinationSet {} without any"
1032 + "next hops {}", ds, neighbors);
1033 return;
1034 }
1035 ObjectiveContext context = new DefaultObjectiveContext(
1036 (objective) ->
1037 log.debug("createGroupsFromDestinationSet installed "
1038 + "NextObj {} on {}", nextId, deviceId),
Charles Chan55b806f2018-08-23 14:30:33 -07001039 (objective, error) -> {
1040 log.warn("createGroupsFromDestinationSet failed to install NextObj {} on {}: {}",
1041 nextId, deviceId, error);
1042 srManager.invalidateNextObj(objective.id());
1043 });
Saurav Das7bcbe702017-06-13 15:35:54 -07001044 NextObjective nextObj = nextObjBuilder.add(context);
1045 log.debug(".. createGroupsFromDestinationSet: Submitted "
1046 + "next objective {} in device {}", nextId, deviceId);
1047 flowObjectiveService.next(deviceId, nextObj);
1048 //update store
1049 dsNextObjStore.put(new DestinationSetNextObjectiveStoreKey(deviceId, ds),
1050 new NextNeighbors(dstNextHops, nextId));
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -08001051 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001052
Saurav Das4ce45962015-11-24 23:21:05 -08001053 /**
Saurav Das1a129a02016-11-18 15:21:57 -08001054 * Creates broadcast groups for all ports in the same subnet for
1055 * all configured subnets.
Saurav Das4ce45962015-11-24 23:21:05 -08001056 */
Charles Chan59cc16d2017-02-02 16:20:42 -08001057 public void createGroupsFromVlanConfig() {
Charles Chan7ffd81f2017-02-08 15:52:08 -08001058 srManager.getVlanPortMap(deviceId).asMap().forEach((vlanId, ports) -> {
Charles Chan59cc16d2017-02-02 16:20:42 -08001059 createBcastGroupFromVlan(vlanId, ports);
Pier Ventre10bd8d12016-11-26 21:05:22 -08001060 });
Saurav Das1a129a02016-11-18 15:21:57 -08001061 }
Charles Chan9f676b62015-10-29 14:58:10 -07001062
Saurav Das1a129a02016-11-18 15:21:57 -08001063 /**
Charles Chan59cc16d2017-02-02 16:20:42 -08001064 * Creates a single broadcast group from a given vlan id and list of ports.
Saurav Das1a129a02016-11-18 15:21:57 -08001065 *
Charles Chan59cc16d2017-02-02 16:20:42 -08001066 * @param vlanId vlan id
Saurav Das1a129a02016-11-18 15:21:57 -08001067 * @param ports list of ports in the subnet
1068 */
Charles Chan7ffd81f2017-02-08 15:52:08 -08001069 public void createBcastGroupFromVlan(VlanId vlanId, Collection<PortNumber> ports) {
Charles Chan59cc16d2017-02-02 16:20:42 -08001070 VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
Charles Chan9f676b62015-10-29 14:58:10 -07001071
Charles Chan59cc16d2017-02-02 16:20:42 -08001072 if (vlanNextObjStore.containsKey(key)) {
Saurav Das1a129a02016-11-18 15:21:57 -08001073 log.debug("Broadcast group for device {} and subnet {} exists",
Charles Chan59cc16d2017-02-02 16:20:42 -08001074 deviceId, vlanId);
Saurav Das1a129a02016-11-18 15:21:57 -08001075 return;
1076 }
Charles Chan188ebf52015-12-23 00:15:11 -08001077
Saurav Das1a129a02016-11-18 15:21:57 -08001078 TrafficSelector metadata =
Charles Chan59cc16d2017-02-02 16:20:42 -08001079 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
Charles Chanc42e84e2015-10-20 16:24:19 -07001080
Saurav Das1a129a02016-11-18 15:21:57 -08001081 int nextId = flowObjectiveService.allocateNextId();
Charles Chanc42e84e2015-10-20 16:24:19 -07001082
Saurav Das1a129a02016-11-18 15:21:57 -08001083 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1084 .builder().withId(nextId)
1085 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1086 .withMeta(metadata);
Charles Chanc42e84e2015-10-20 16:24:19 -07001087
Saurav Das1a129a02016-11-18 15:21:57 -08001088 ports.forEach(port -> {
1089 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Charles Chan7ffd81f2017-02-08 15:52:08 -08001090 if (toPopVlan(port, vlanId)) {
1091 tBuilder.popVlan();
1092 }
Saurav Das1a129a02016-11-18 15:21:57 -08001093 tBuilder.setOutput(port);
1094 nextObjBuilder.addTreatment(tBuilder.build());
Charles Chanc42e84e2015-10-20 16:24:19 -07001095 });
Saurav Das1a129a02016-11-18 15:21:57 -08001096
Saurav Das961beb22017-03-29 19:09:17 -07001097 ObjectiveContext context = new DefaultObjectiveContext(
1098 (objective) ->
1099 log.debug("createBroadcastGroupFromVlan installed "
1100 + "NextObj {} on {}", nextId, deviceId),
Charles Chan55b806f2018-08-23 14:30:33 -07001101 (objective, error) -> {
1102 log.warn("createBroadcastGroupFromVlan failed to install NextObj {} on {}: {}",
1103 nextId, deviceId, error);
1104 srManager.invalidateNextObj(objective.id());
1105 });
Saurav Das961beb22017-03-29 19:09:17 -07001106 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Das1a129a02016-11-18 15:21:57 -08001107 flowObjectiveService.next(deviceId, nextObj);
Saurav Dasf14d9ef2017-12-05 15:00:23 -08001108 log.debug("createBcastGroupFromVlan: Submitted next objective {} "
1109 + "for vlan: {} in device {}", nextId, vlanId, deviceId);
Saurav Das1a129a02016-11-18 15:21:57 -08001110
Charles Chan59cc16d2017-02-02 16:20:42 -08001111 vlanNextObjStore.put(key, nextId);
Charles Chanc42e84e2015-10-20 16:24:19 -07001112 }
1113
Charles Chane849c192016-01-11 18:28:54 -08001114 /**
Jonghwan Hyun42fe1052017-08-25 17:48:36 -07001115 * Removes a single broadcast group from a given vlan id.
1116 * The group should be empty.
1117 * @param deviceId device Id to remove the group
1118 * @param portNum port number related to the group
1119 * @param vlanId vlan id of the broadcast group to remove
1120 * @param popVlan true if the TrafficTreatment involves pop vlan tag action
1121 */
1122 public void removeBcastGroupFromVlan(DeviceId deviceId, PortNumber portNum,
1123 VlanId vlanId, boolean popVlan) {
1124 VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
1125
1126 if (!vlanNextObjStore.containsKey(key)) {
1127 log.debug("Broadcast group for device {} and subnet {} does not exist",
1128 deviceId, vlanId);
1129 return;
1130 }
1131
1132 TrafficSelector metadata =
1133 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
1134
1135 int nextId = vlanNextObjStore.get(key);
1136
1137 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1138 .builder().withId(nextId)
1139 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1140 .withMeta(metadata);
1141
1142 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1143 if (popVlan) {
1144 tBuilder.popVlan();
1145 }
1146 tBuilder.setOutput(portNum);
1147 nextObjBuilder.addTreatment(tBuilder.build());
1148
1149 ObjectiveContext context = new DefaultObjectiveContext(
1150 (objective) ->
1151 log.debug("removeBroadcastGroupFromVlan removed "
1152 + "NextObj {} on {}", nextId, deviceId),
Charles Chan55b806f2018-08-23 14:30:33 -07001153 (objective, error) -> {
1154 log.warn("removeBroadcastGroupFromVlan failed to remove NextObj {} on {}: {}",
1155 nextId, deviceId, error);
1156 srManager.invalidateNextObj(objective.id());
1157 });
Jonghwan Hyun42fe1052017-08-25 17:48:36 -07001158 NextObjective nextObj = nextObjBuilder.remove(context);
1159 flowObjectiveService.next(deviceId, nextObj);
1160 log.debug("removeBcastGroupFromVlan: Submited next objective {} in device {}",
1161 nextId, deviceId);
1162
1163 vlanNextObjStore.remove(key, nextId);
1164 }
1165
1166 /**
Charles Chan7ffd81f2017-02-08 15:52:08 -08001167 * Determine if we should pop given vlan before sending packets to the given port.
1168 *
1169 * @param portNumber port number
1170 * @param vlanId vlan id
1171 * @return true if the vlan id is not contained in any vlanTagged config
1172 */
1173 private boolean toPopVlan(PortNumber portNumber, VlanId vlanId) {
Saurav Das7bcbe702017-06-13 15:35:54 -07001174 return srManager.interfaceService
1175 .getInterfacesByPort(new ConnectPoint(deviceId, portNumber))
Charles Chan7ffd81f2017-02-08 15:52:08 -08001176 .stream().noneMatch(intf -> intf.vlanTagged().contains(vlanId));
1177 }
1178
1179 /**
Saurav Das4ce45962015-11-24 23:21:05 -08001180 * Create simple next objective for a single port. The treatments can include
1181 * all outgoing actions that need to happen on the packet.
1182 *
1183 * @param portNum the outgoing port on the device
1184 * @param treatment the actions to apply on the packets (should include outport)
1185 * @param meta optional data to pass to the driver
1186 */
1187 public void createGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
1188 TrafficSelector meta) {
1189 int nextId = flowObjectiveService.allocateNextId();
1190 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
Saurav Das76ae6812017-03-15 15:15:14 -07001191 deviceId, portNum, treatment, meta);
Saurav Das4ce45962015-11-24 23:21:05 -08001192
1193 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1194 .builder().withId(nextId)
1195 .withType(NextObjective.Type.SIMPLE)
1196 .addTreatment(treatment)
1197 .fromApp(appId)
1198 .withMeta(meta);
1199
Saurav Das961beb22017-03-29 19:09:17 -07001200 ObjectiveContext context = new DefaultObjectiveContext(
1201 (objective) ->
1202 log.debug("createGroupFromPort installed "
1203 + "NextObj {} on {}", nextId, deviceId),
Charles Chan55b806f2018-08-23 14:30:33 -07001204 (objective, error) -> {
1205 log.warn("createGroupFromPort failed to install NextObj {} on {}: {}", nextId, deviceId, error);
1206 srManager.invalidateNextObj(objective.id());
1207 });
Saurav Das961beb22017-03-29 19:09:17 -07001208 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Das4ce45962015-11-24 23:21:05 -08001209 flowObjectiveService.next(deviceId, nextObj);
1210 log.debug("createGroupFromPort: Submited next objective {} in device {} "
1211 + "for port {}", nextId, deviceId, portNum);
1212
1213 portNextObjStore.put(key, nextId);
1214 }
1215
sangho1e575652015-05-14 00:39:53 -07001216 /**
Jonghwan Hyun42fe1052017-08-25 17:48:36 -07001217 * Removes simple next objective for a single port.
1218 *
1219 * @param deviceId device id that has the port to deal with
1220 * @param portNum the outgoing port on the device
1221 * @param vlanId vlan id associated with the port
1222 * @param popVlan true if POP_VLAN action is applied on the packets, false otherwise
1223 */
1224 public void removePortNextObjective(DeviceId deviceId, PortNumber portNum, VlanId vlanId, boolean popVlan) {
1225 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
1226 mbuilder.matchVlanId(vlanId);
1227
1228 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
1229 tbuilder.immediate().setOutput(portNum);
1230 if (popVlan) {
1231 tbuilder.immediate().popVlan();
1232 }
1233
1234 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, portNum,
1235 tbuilder.build(), mbuilder.build(), false);
1236
1237 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
1238 deviceId, portNum, tbuilder.build(), mbuilder.build());
1239 if (portNextObjId != -1 && portNextObjStore.containsKey(key)) {
1240 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1241 .builder().withId(portNextObjId)
1242 .withType(NextObjective.Type.SIMPLE).fromApp(appId);
1243 ObjectiveContext context = new DefaultObjectiveContext(
1244 (objective) -> log.debug("removePortNextObjective removes NextObj {} on {}",
1245 portNextObjId, deviceId),
Charles Chan55b806f2018-08-23 14:30:33 -07001246 (objective, error) -> {
1247 log.warn("removePortNextObjective failed to remove NextObj {} on {}: {}",
1248 portNextObjId, deviceId, error);
1249 srManager.invalidateNextObj(objective.id());
1250 });
Jonghwan Hyun42fe1052017-08-25 17:48:36 -07001251 NextObjective nextObjective = nextObjBuilder.remove(context);
1252 log.info("**removePortNextObjective: Submitted "
1253 + "next objective {} in device {}",
1254 portNextObjId, deviceId);
1255 flowObjectiveService.next(deviceId, nextObjective);
1256
1257 portNextObjStore.remove(key);
1258 }
1259 }
1260 /**
sangho1e575652015-05-14 00:39:53 -07001261 * Removes groups for the next objective ID given.
1262 *
1263 * @param objectiveId next objective ID to remove
1264 * @return true if succeeds, false otherwise
1265 */
1266 public boolean removeGroup(int objectiveId) {
Saurav Das7bcbe702017-06-13 15:35:54 -07001267 for (Map.Entry<DestinationSetNextObjectiveStoreKey, NextNeighbors> e :
1268 dsNextObjStore.entrySet()) {
1269 if (e.getValue().nextId() != objectiveId) {
1270 continue;
1271 }
Pier Luigi63edd932018-01-14 21:56:11 +01001272 // Right now it is just used in TunnelHandler
1273 // remember in future that PW transit groups could
1274 // be Indirect groups
sangho1e575652015-05-14 00:39:53 -07001275 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1276 .builder().withId(objectiveId)
1277 .withType(NextObjective.Type.HASHED).fromApp(appId);
Charles Chan216e3c82016-04-23 14:48:16 -07001278 ObjectiveContext context = new DefaultObjectiveContext(
1279 (objective) -> log.debug("RemoveGroup removes NextObj {} on {}",
1280 objectiveId, deviceId),
Charles Chan55b806f2018-08-23 14:30:33 -07001281 (objective, error) -> {
1282 log.warn("RemoveGroup failed to remove NextObj {} on {}: {}", objectiveId, deviceId, error);
1283 srManager.invalidateNextObj(objective.id());
1284 });
Charles Chan216e3c82016-04-23 14:48:16 -07001285 NextObjective nextObjective = nextObjBuilder.remove(context);
Saurav Das8a0732e2015-11-20 15:27:53 -08001286 log.info("**removeGroup: Submited "
1287 + "next objective {} in device {}",
1288 objectiveId, deviceId);
sangho1e575652015-05-14 00:39:53 -07001289 flowObjectiveService.next(deviceId, nextObjective);
1290
Saurav Das7bcbe702017-06-13 15:35:54 -07001291 dsNextObjStore.remove(e.getKey());
sangho0b2b6d12015-05-20 22:16:38 -07001292 return true;
sangho1e575652015-05-14 00:39:53 -07001293 }
1294
1295 return false;
1296 }
Jonghwan Hyun42fe1052017-08-25 17:48:36 -07001297 /**
1298 * Remove simple next objective for a single port. The treatments can include
1299 * all outgoing actions that need to happen on the packet.
1300 *
1301 * @param portNum the outgoing port on the device
1302 * @param treatment the actions applied on the packets (should include outport)
1303 * @param meta optional data to pass to the driver
1304 */
1305 public void removeGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
1306 TrafficSelector meta) {
1307 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
1308 deviceId, portNum, treatment, meta);
1309 Integer nextId = portNextObjStore.get(key);
1310
1311 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1312 .builder().withId(nextId)
1313 .withType(NextObjective.Type.SIMPLE)
1314 .addTreatment(treatment)
1315 .fromApp(appId)
1316 .withMeta(meta);
1317
1318 ObjectiveContext context = new DefaultObjectiveContext(
1319 (objective) ->
1320 log.info("removeGroupFromPort installed "
1321 + "NextObj {} on {}", nextId, deviceId),
Charles Chan55b806f2018-08-23 14:30:33 -07001322 (objective, error) -> {
1323 log.warn("removeGroupFromPort failed to install NextObj {} on {}: {}", nextId, deviceId, error);
1324 srManager.invalidateNextObj(objective.id());
1325 }
Jonghwan Hyun42fe1052017-08-25 17:48:36 -07001326 );
1327 NextObjective nextObj = nextObjBuilder.remove(context);
1328 flowObjectiveService.next(deviceId, nextObj);
1329 log.info("removeGroupFromPort: Submitted next objective {} in device {} "
1330 + "for port {}", nextId, deviceId, portNum);
1331
1332 portNextObjStore.remove(key);
1333 }
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -07001334
Charles Chane849c192016-01-11 18:28:54 -08001335 /**
1336 * Removes all groups from all next objective stores.
1337 */
Saurav Das7bcbe702017-06-13 15:35:54 -07001338 /*public void removeAllGroups() {
1339 for (Map.Entry<NeighborSetNextObjectiveStoreKey, NextNeighbors> entry:
Saurav Das423fe2b2015-12-04 10:52:59 -08001340 nsNextObjStore.entrySet()) {
Saurav Das7bcbe702017-06-13 15:35:54 -07001341 removeGroup(entry.getValue().nextId());
Saurav Das423fe2b2015-12-04 10:52:59 -08001342 }
1343 for (Map.Entry<PortNextObjectiveStoreKey, Integer> entry:
1344 portNextObjStore.entrySet()) {
1345 removeGroup(entry.getValue());
1346 }
Charles Chan59cc16d2017-02-02 16:20:42 -08001347 for (Map.Entry<VlanNextObjectiveStoreKey, Integer> entry:
1348 vlanNextObjStore.entrySet()) {
Saurav Das423fe2b2015-12-04 10:52:59 -08001349 removeGroup(entry.getValue());
1350 }
Saurav Das7bcbe702017-06-13 15:35:54 -07001351 }*/ //XXX revisit
1352
Saurav Dasceccf242017-08-03 18:30:35 -07001353 /**
1354 * Triggers a one time bucket verification operation on all hash groups
1355 * on this device.
1356 */
1357 public void triggerBucketCorrector() {
1358 BucketCorrector bc = new BucketCorrector();
1359 bc.run();
1360 }
1361
Jonghwan Hyunf810a7a2018-02-12 16:43:45 +09001362 /**
1363 * Modifies L2IG bucket when the interface configuration is updated, especially
1364 * when the interface has same VLAN ID but the VLAN type is changed (e.g., from
1365 * vlan-tagged [10] to vlan-untagged 10), which requires changes on
1366 * TrafficTreatment in turn.
1367 *
1368 * @param portNumber the port on this device that needs to be updated
1369 * @param vlanId the vlan id corresponding to this port
1370 * @param pushVlan indicates if packets should be sent out untagged or not out
1371 * from the port. If true, updated TrafficTreatment involves
1372 * pop vlan tag action. If false, updated TrafficTreatment
1373 * does not involve pop vlan tag action.
1374 */
1375 public void updateL2InterfaceGroupBucket(PortNumber portNumber, VlanId vlanId, boolean pushVlan) {
1376 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1377 if (pushVlan) {
1378 tBuilder.popVlan();
1379 }
1380 tBuilder.setOutput(portNumber);
1381
1382 TrafficSelector metadata =
1383 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
1384
1385 int nextId = getVlanNextObjectiveId(vlanId);
1386
1387 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1388 .builder().withId(nextId)
1389 .withType(NextObjective.Type.SIMPLE).fromApp(appId)
1390 .addTreatment(tBuilder.build())
1391 .withMeta(metadata);
1392
1393 ObjectiveContext context = new DefaultObjectiveContext(
1394 (objective) -> log.debug("port {} successfully updated NextObj {} on {}",
1395 portNumber, nextId, deviceId),
Charles Chan55b806f2018-08-23 14:30:33 -07001396 (objective, error) -> {
1397 log.warn("port {} failed to updated NextObj {} on {}: {}", portNumber, nextId, deviceId, error);
1398 srManager.invalidateNextObj(objective.id());
1399 });
Jonghwan Hyunf810a7a2018-02-12 16:43:45 +09001400
1401 flowObjectiveService.next(deviceId, nextObjBuilder.modify(context));
Jonghwan Hyun42fe1052017-08-25 17:48:36 -07001402 }
1403
Jonghwan Hyunf810a7a2018-02-12 16:43:45 +09001404 /**
1405 * Adds a single port to the L2FG or removes it from the L2FG.
1406 *
1407 * @param vlanId the vlan id corresponding to this port
1408 * @param portNum the port on this device to be updated
1409 * @param nextId the next objective ID for the given vlan id
1410 * @param install if true, adds the port to L2FG. If false, removes it from L2FG.
1411 */
1412 public void updateGroupFromVlanConfiguration(VlanId vlanId, PortNumber portNum, int nextId, boolean install) {
Jonghwan Hyun42fe1052017-08-25 17:48:36 -07001413 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1414 if (toPopVlan(portNum, vlanId)) {
1415 tBuilder.popVlan();
1416 }
1417 tBuilder.setOutput(portNum);
1418
1419 TrafficSelector metadata =
1420 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
1421
1422 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1423 .builder().withId(nextId)
1424 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1425 .addTreatment(tBuilder.build())
1426 .withMeta(metadata);
1427
1428 ObjectiveContext context = new DefaultObjectiveContext(
1429 (objective) -> log.debug("port {} successfully removedFrom NextObj {} on {}",
1430 portNum, nextId, deviceId),
Charles Chan55b806f2018-08-23 14:30:33 -07001431 (objective, error) -> {
1432 log.warn("port {} failed to removedFrom NextObj {} on {}: {}", portNum, nextId, deviceId, error);
1433 srManager.invalidateNextObj(objective.id());
1434 });
Jonghwan Hyun42fe1052017-08-25 17:48:36 -07001435
1436 if (install) {
1437 flowObjectiveService.next(deviceId, nextObjBuilder.addToExisting(context));
1438 } else {
1439 flowObjectiveService.next(deviceId, nextObjBuilder.removeFromExisting(context));
1440 }
1441 }
Saurav Das1547b3f2017-05-05 17:01:08 -07001442
1443 /**
Saurav Das9df5b7c2017-08-14 16:44:43 -07001444 * Performs bucket verification operation for all hash groups in this device.
1445 * Checks RouteHandler to ensure that routing is stable before attempting
1446 * verification. Verification involves creating a nextObjective with
1447 * operation VERIFY for existing next objectives in the store, and passing
1448 * it to the driver. It is the driver that actually performs the verification
1449 * by adding or removing buckets to match the verification next objective
1450 * created here.
Saurav Das1547b3f2017-05-05 17:01:08 -07001451 */
Saurav Dasceccf242017-08-03 18:30:35 -07001452 protected final class BucketCorrector implements Runnable {
1453 Integer nextId;
Saurav Das1547b3f2017-05-05 17:01:08 -07001454
Saurav Dasceccf242017-08-03 18:30:35 -07001455 BucketCorrector() {
1456 this.nextId = null;
1457 }
1458
1459 BucketCorrector(Integer nextId) {
1460 this.nextId = nextId;
Saurav Das1547b3f2017-05-05 17:01:08 -07001461 }
1462
1463 @Override
1464 public void run() {
Saurav Dasceccf242017-08-03 18:30:35 -07001465 if (!srManager.mastershipService.isLocalMaster(deviceId)) {
1466 return;
Saurav Das1547b3f2017-05-05 17:01:08 -07001467 }
Saurav Dasceccf242017-08-03 18:30:35 -07001468 DefaultRoutingHandler rh = srManager.getRoutingHandler();
1469 if (rh == null) {
1470 return;
1471 }
1472 if (!rh.isRoutingStable()) {
1473 return;
1474 }
1475 rh.acquireRoutingLock();
1476 try {
Saurav Das9df5b7c2017-08-14 16:44:43 -07001477 log.trace("running bucket corrector for dev: {}", deviceId);
Saurav Dasceccf242017-08-03 18:30:35 -07001478 Set<DestinationSetNextObjectiveStoreKey> dsKeySet = dsNextObjStore.entrySet()
1479 .stream()
1480 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
Pier Luigi63edd932018-01-14 21:56:11 +01001481 // Filter out PW transit groups or include them if MPLS ECMP is supported
Saurav Dasa4020382018-02-14 14:14:54 -08001482 .filter(entry -> !entry.getKey().destinationSet().notBos() ||
1483 (entry.getKey().destinationSet().notBos() && srManager.getMplsEcmp()))
1484 // Filter out simple SWAP groups or include them if MPLS ECMP is supported
1485 .filter(entry -> !entry.getKey().destinationSet().swap() ||
1486 (entry.getKey().destinationSet().swap() && srManager.getMplsEcmp()))
Saurav Dasceccf242017-08-03 18:30:35 -07001487 .map(entry -> entry.getKey())
1488 .collect(Collectors.toSet());
1489 for (DestinationSetNextObjectiveStoreKey dsKey : dsKeySet) {
1490 NextNeighbors next = dsNextObjStore.get(dsKey);
1491 if (next == null) {
1492 continue;
1493 }
1494 int nid = next.nextId();
1495 if (nextId != null && nextId != nid) {
1496 continue;
1497 }
Saurav Das9df5b7c2017-08-14 16:44:43 -07001498 log.trace("bkt-corr: dsNextObjStore for device {}: {}",
Saurav Dasceccf242017-08-03 18:30:35 -07001499 deviceId, dsKey, next);
1500 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
Saurav Das09c2c4d2018-08-13 15:34:26 -07001501 metabuilder.matchVlanId(srManager.getDefaultInternalVlan());
Saurav Dasceccf242017-08-03 18:30:35 -07001502 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
1503 .withId(nid)
1504 .withType(NextObjective.Type.HASHED)
1505 .withMeta(metabuilder.build())
1506 .fromApp(appId);
1507
1508 next.dstNextHops().forEach((dstDev, nextHops) -> {
1509 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dstDev);
1510 nextHops.forEach(neighbor -> {
1511 MacAddress neighborMac;
1512 try {
1513 neighborMac = deviceConfig.getDeviceMac(neighbor);
1514 } catch (DeviceConfigNotFoundException e) {
1515 log.warn(e.getMessage() + " Aborting neighbor"
1516 + neighbor);
1517 return;
1518 }
1519 devicePortMap.get(neighbor).forEach(port -> {
Saurav Das9df5b7c2017-08-14 16:44:43 -07001520 log.trace("verify in device {} nextId {}: bucket with"
Saurav Dasceccf242017-08-03 18:30:35 -07001521 + " port/label {}/{} to dst {} via {}",
1522 deviceId, nid, port, edgeLabel,
1523 dstDev, neighbor);
Saurav Dasa4020382018-02-14 14:14:54 -08001524 nextObjBuilder
1525 .addTreatment(treatmentBuilder(port,
1526 neighborMac,
1527 dsKey.destinationSet().swap(),
1528 edgeLabel));
Saurav Dasceccf242017-08-03 18:30:35 -07001529 });
1530 });
1531 });
1532
1533 NextObjective nextObjective = nextObjBuilder.verify();
1534 flowObjectiveService.next(deviceId, nextObjective);
1535 }
1536 } finally {
1537 rh.releaseRoutingLock();
1538 }
1539
1540 }
1541
1542 TrafficTreatment treatmentBuilder(PortNumber outport, MacAddress dstMac,
Saurav Dasa4020382018-02-14 14:14:54 -08001543 boolean swap, int edgeLabel) {
Saurav Dasceccf242017-08-03 18:30:35 -07001544 TrafficTreatment.Builder tBuilder =
1545 DefaultTrafficTreatment.builder();
1546 tBuilder.setOutput(outport)
1547 .setEthDst(dstMac)
1548 .setEthSrc(nodeMacAddr);
1549 if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
Saurav Dasa4020382018-02-14 14:14:54 -08001550 if (swap) {
1551 // swap label case
1552 tBuilder.setMpls(MplsLabel.mplsLabel(edgeLabel));
1553 } else {
1554 // ecmp with label push case
1555 tBuilder.pushMpls()
1556 .copyTtlOut()
1557 .setMpls(MplsLabel.mplsLabel(edgeLabel));
1558 }
Saurav Dasceccf242017-08-03 18:30:35 -07001559 }
1560 return tBuilder.build();
Saurav Das1547b3f2017-05-05 17:01:08 -07001561 }
1562 }
1563
Charles Chane0260e12018-08-22 17:56:47 -07001564 /**
1565 * Determines whether the hash group bucket should include a popVlan action.
1566 * We don't popVlan for PW.
1567 *
1568 * @param ds destination set
1569 * @return true if VLAN needs to be popped
1570 */
1571 private boolean popVlanInHashGroup(DestinationSet ds) {
1572 return (ds.getTypeOfDstSet() != DestinationSet.DestinationSetType.SWAP_NOT_BOS) &&
1573 (ds.getTypeOfDstSet() != DestinationSet.DestinationSetType.POP_NOT_BOS);
1574 }
Pier Luigi63edd932018-01-14 21:56:11 +01001575}