blob: 5f32f25b9fdfc0ef1ff265d06d26b067779c7408 [file] [log] [blame]
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -07001/*
Brian O'Connor0947d7e2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.segmentrouting.grouphandler;
17
Saurav Dasfe0b05e2017-08-14 16:44:43 -070018import com.google.common.collect.ImmutableMap;
Pier Ventre229fd0b2016-10-31 16:49:19 -070019import com.google.common.collect.Iterables;
Saurav Dasfe0b05e2017-08-14 16:44:43 -070020import com.google.common.collect.Lists;
Saurav Das62ae6792017-05-15 15:34:25 -070021import com.google.common.collect.Sets;
22
Pier Ventre229fd0b2016-10-31 16:49:19 -070023import org.apache.commons.lang3.RandomUtils;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070024import org.onlab.packet.MacAddress;
25import org.onlab.packet.MplsLabel;
Saurav Das62af8802015-12-04 10:52:59 -080026import org.onlab.packet.VlanId;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070027import org.onlab.util.KryoNamespace;
28import org.onosproject.core.ApplicationId;
Charles Chan10b0fb72017-02-02 16:20:42 -080029import org.onosproject.net.ConnectPoint;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070030import org.onosproject.net.DeviceId;
31import org.onosproject.net.Link;
32import org.onosproject.net.PortNumber;
Saurav Das62af8802015-12-04 10:52:59 -080033import org.onosproject.net.flow.DefaultTrafficSelector;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070034import org.onosproject.net.flow.DefaultTrafficTreatment;
Saurav Das4c35fc42015-11-20 15:27:53 -080035import org.onosproject.net.flow.TrafficSelector;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070036import org.onosproject.net.flow.TrafficTreatment;
Srikanth Vavilapalli64505482015-04-21 13:04:13 -070037import org.onosproject.net.flowobjective.DefaultNextObjective;
Charles Chana4ee4f92016-04-23 14:48:16 -070038import org.onosproject.net.flowobjective.DefaultObjectiveContext;
Srikanth Vavilapalli64505482015-04-21 13:04:13 -070039import org.onosproject.net.flowobjective.FlowObjectiveService;
40import org.onosproject.net.flowobjective.NextObjective;
Srikanth Vavilapalli8c83f1d2015-05-22 13:47:31 -070041import org.onosproject.net.flowobjective.ObjectiveContext;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070042import org.onosproject.net.link.LinkService;
Saurav Dasfbe74572017-08-03 18:30:35 -070043import org.onosproject.segmentrouting.DefaultRoutingHandler;
Saurav Das62af8802015-12-04 10:52:59 -080044import org.onosproject.segmentrouting.SegmentRoutingManager;
Charles Chan319d1a22015-11-03 10:42:14 -080045import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
46import org.onosproject.segmentrouting.config.DeviceProperties;
Saurav Das261c3002017-06-13 15:35:54 -070047import org.onosproject.segmentrouting.storekey.DestinationSetNextObjectiveStoreKey;
Charles Chan1eaf4802016-04-18 13:44:03 -070048import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
Charles Chan10b0fb72017-02-02 16:20:42 -080049import org.onosproject.segmentrouting.storekey.VlanNextObjectiveStoreKey;
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -070050import org.onosproject.store.service.EventuallyConsistentMap;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070051import org.slf4j.Logger;
52
Pier Ventre229fd0b2016-10-31 16:49:19 -070053import java.net.URI;
Charles Chan90772a72017-02-08 15:52:08 -080054import java.util.Collection;
Pier Ventre229fd0b2016-10-31 16:49:19 -070055import java.util.Collections;
Saurav Das62ae6792017-05-15 15:34:25 -070056import java.util.HashMap;
Pier Ventre229fd0b2016-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 Das8a3022d2017-05-05 17:01:08 -070062import java.util.concurrent.ScheduledExecutorService;
63import java.util.concurrent.TimeUnit;
Pier Ventre229fd0b2016-10-31 16:49:19 -070064import java.util.stream.Collectors;
65
66import static com.google.common.base.Preconditions.checkNotNull;
Saurav Das8a3022d2017-05-05 17:01:08 -070067import static java.util.concurrent.Executors.newScheduledThreadPool;
68import static org.onlab.util.Tools.groupedThreads;
Pier Ventre229fd0b2016-10-31 16:49:19 -070069import static org.slf4j.LoggerFactory.getLogger;
70
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070071/**
Srikanth Vavilapalli64505482015-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 Vavilapalli37a461b2015-04-07 15:12:32 -070075 */
76public class DefaultGroupHandler {
Ray Milkey5247fa92018-01-12 14:22:06 -080077 private static final Logger log = getLogger(DefaultGroupHandler.class);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070078
Saurav Dasfbe74572017-08-03 18:30:35 -070079 private static final long VERIFY_INTERVAL = 30; // secs
80
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070081 protected final DeviceId deviceId;
82 protected final ApplicationId appId;
83 protected final DeviceProperties deviceConfig;
84 protected final List<Integer> allSegmentIds;
Pier Ventreadb4ae62016-11-23 09:57:42 -080085 protected int ipv4NodeSegmentId = -1;
86 protected int ipv6NodeSegmentId = -1;
Charles Chan319d1a22015-11-03 10:42:14 -080087 protected boolean isEdgeRouter = false;
88 protected MacAddress nodeMacAddr = null;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070089 protected LinkService linkService;
Srikanth Vavilapalli64505482015-04-21 13:04:13 -070090 protected FlowObjectiveService flowObjectiveService;
Saurav Das62ae6792017-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 Das4c35fc42015-11-20 15:27:53 -080095 protected ConcurrentHashMap<DeviceId, Set<PortNumber>> devicePortMap =
96 new ConcurrentHashMap<>();
Saurav Das62ae6792017-05-15 15:34:25 -070097 /**
98 * local store for ports on this device connected to neighbor-device-id.
99 */
Saurav Das4c35fc42015-11-20 15:27:53 -0800100 protected ConcurrentHashMap<PortNumber, DeviceId> portDeviceMap =
101 new ConcurrentHashMap<>();
Saurav Das62ae6792017-05-15 15:34:25 -0700102
Saurav Das261c3002017-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 Dasf0f592d2016-11-18 15:21:57 -0800106 // distributed store for (device+subnet-ip-prefix) mapped to next-id
Charles Chan10b0fb72017-02-02 16:20:42 -0800107 protected EventuallyConsistentMap<VlanNextObjectiveStoreKey, Integer>
108 vlanNextObjStore = null;
Saurav Dasf0f592d2016-11-18 15:21:57 -0800109 // distributed store for (device+port+treatment) mapped to next-id
Charles Chanb7f75ac2016-01-11 18:28:54 -0800110 protected EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
111 portNextObjStore = null;
Charles Chande6655c2015-12-23 00:15:11 -0800112 private SegmentRoutingManager srManager;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700113
Saurav Das8a3022d2017-05-05 17:01:08 -0700114 private ScheduledExecutorService executorService
Saurav Dasfbe74572017-08-03 18:30:35 -0700115 = newScheduledThreadPool(1, groupedThreads("bktCorrector", "bktC-%d", log));
Saurav Das8a3022d2017-05-05 17:01:08 -0700116
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700117 protected KryoNamespace.Builder kryo = new KryoNamespace.Builder()
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700118 .register(URI.class).register(HashSet.class)
Saurav Das261c3002017-06-13 15:35:54 -0700119 .register(DeviceId.class).register(PortNumber.class)
120 .register(DestinationSet.class).register(PolicyGroupIdentifier.class)
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700121 .register(PolicyGroupParams.class)
122 .register(GroupBucketIdentifier.class)
123 .register(GroupBucketIdentifier.BucketOutputType.class);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700124
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700125 protected DefaultGroupHandler(DeviceId deviceId, ApplicationId appId,
126 DeviceProperties config,
127 LinkService linkService,
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700128 FlowObjectiveService flowObjService,
Charles Chande6655c2015-12-23 00:15:11 -0800129 SegmentRoutingManager srManager) {
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700130 this.deviceId = checkNotNull(deviceId);
131 this.appId = checkNotNull(appId);
132 this.deviceConfig = checkNotNull(config);
133 this.linkService = checkNotNull(linkService);
Charles Chan319d1a22015-11-03 10:42:14 -0800134 this.allSegmentIds = checkNotNull(config.getAllDeviceSegmentIds());
135 try {
Pier Ventreadb4ae62016-11-23 09:57:42 -0800136 this.ipv4NodeSegmentId = config.getIPv4SegmentId(deviceId);
137 this.ipv6NodeSegmentId = config.getIPv6SegmentId(deviceId);
Charles Chan319d1a22015-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 Vavilapalli64505482015-04-21 13:04:13 -0700144 this.flowObjectiveService = flowObjService;
Saurav Das261c3002017-06-13 15:35:54 -0700145 this.dsNextObjStore = srManager.dsNextObjStore();
Ray Milkeyb85de082017-04-05 09:42:04 -0700146 this.vlanNextObjStore = srManager.vlanNextObjStore();
147 this.portNextObjStore = srManager.portNextObjStore();
Charles Chande6655c2015-12-23 00:15:11 -0800148 this.srManager = srManager;
Saurav Dasfbe74572017-08-03 18:30:35 -0700149 executorService.scheduleWithFixedDelay(new BucketCorrector(), 10,
150 VERIFY_INTERVAL,
151 TimeUnit.SECONDS);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700152 populateNeighborMaps();
153 }
154
155 /**
Saurav Dasfbe74572017-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 Das62ae6792017-05-15 15:34:25 -0700164 * Creates a group handler object.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700165 *
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 Vavilapalli64505482015-04-21 13:04:13 -0700170 * @param flowObjService flow objective service object
Charles Chanb7f75ac2016-01-11 18:28:54 -0800171 * @param srManager segment routing manager
Charles Chan319d1a22015-11-03 10:42:14 -0800172 * @throws DeviceConfigNotFoundException if the device configuration is not found
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700173 * @return default group handler type
174 */
Saurav Das2d94d312015-11-24 23:21:05 -0800175 public static DefaultGroupHandler createGroupHandler(
Saurav Dasfbe74572017-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 Das62ae6792017-05-15 15:34:25 -0700183 return new DefaultGroupHandler(deviceId, appId, config,
184 linkService,
185 flowObjService,
186 srManager);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700187 }
188
189 /**
Saurav Das97241862018-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 Das62ae6792017-05-15 15:34:25 -0700192 *
193 * @param link the infrastructure link
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700194 */
Saurav Das62ae6792017-05-15 15:34:25 -0700195 public void portUpForLink(Link link) {
Saurav Dasfbe74572017-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 Das62ae6792017-05-15 15:34:25 -0700201
Saurav Dasfbe74572017-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 Das62ae6792017-05-15 15:34:25 -0700208
Saurav Dasfbe74572017-08-03 18:30:35 -0700209 /**
Saurav Das97241862018-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 Dasfbe74572017-08-03 18:30:35 -0700212 *
Saurav Das97241862018-02-14 14:14:54 -0800213 * @param link the infrastructure link
Saurav Dasfbe74572017-08-03 18:30:35 -0700214 */
Saurav Das97241862018-02-14 14:14:54 -0800215 public void portDownForLink(Link link) {
216 PortNumber port = link.src().port();
Saurav Dasfbe74572017-08-03 18:30:35 -0700217 if (portDeviceMap.get(port) == null) {
218 log.warn("portDown: unknown port");
219 return;
220 }
Saurav Das62ae6792017-05-15 15:34:25 -0700221
Saurav Dasfbe74572017-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 Vavilapalli37a461b2015-04-07 15:12:32 -0700227
228 /**
Saurav Das97241862018-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 Das62ae6792017-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 Vavilapalli37a461b2015-04-07 15:12:32 -0700246 *
Saurav Das62ae6792017-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 Vavilapalli37a461b2015-04-07 15:12:32 -0700251 */
Saurav Das62ae6792017-05-15 15:34:25 -0700252 public void retryHash(Link link, boolean linkDown, boolean firstTime) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700253 MacAddress neighborMac;
Charles Chan319d1a22015-11-03 10:42:14 -0800254 try {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700255 neighborMac = deviceConfig.getDeviceMac(link.dst().deviceId());
Charles Chan319d1a22015-11-03 10:42:14 -0800256 } catch (DeviceConfigNotFoundException e) {
Saurav Das62ae6792017-05-15 15:34:25 -0700257 log.warn(e.getMessage() + " Aborting retryHash.");
Charles Chan319d1a22015-11-03 10:42:14 -0800258 return;
259 }
Saurav Das261c3002017-06-13 15:35:54 -0700260 // find all the destinationSets related to link
261 Set<DestinationSetNextObjectiveStoreKey> dsKeySet = dsNextObjStore.entrySet()
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700262 .stream()
Saurav Das261c3002017-06-13 15:35:54 -0700263 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
Pier Luigiecb09f42018-01-14 21:56:11 +0100264 // Filter out PW transit groups or include them if MPLS ECMP is supported
Saurav Das97241862018-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 Das261c3002017-06-13 15:35:54 -0700270 .filter(entry -> entry.getValue().containsNextHop(link.dst().deviceId()))
271 .map(entry -> entry.getKey())
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700272 .collect(Collectors.toSet());
Saurav Das62ae6792017-05-15 15:34:25 -0700273
Saurav Das261c3002017-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 Das62ae6792017-05-15 15:34:25 -0700280 log.warn("retryHash in device {}, but global store has no record "
Saurav Das261c3002017-06-13 15:35:54 -0700281 + "for dsKey:{}", deviceId, dsKey);
Saurav Das62ae6792017-05-15 15:34:25 -0700282 continue;
283 }
Saurav Das261c3002017-06-13 15:35:54 -0700284 int nextId = nextHops.nextId();
285 Set<DeviceId> dstSet = nextHops.getDstForNextHop(link.dst().deviceId());
Saurav Das62ae6792017-05-15 15:34:25 -0700286 if (!linkDown) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700287 List<PortLabel> pl = Lists.newArrayList();
Saurav Das62ae6792017-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 Das261c3002017-06-13 15:35:54 -0700294 dstSet.forEach(dst -> {
295 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700296 pl.add(new PortLabel(p, edgeLabel));
Saurav Das261c3002017-06-13 15:35:54 -0700297 });
Saurav Das62ae6792017-05-15 15:34:25 -0700298 }
Saurav Dasfe0b05e2017-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);
304 pl.add(new PortLabel(link.src().port(), edgeLabel));
305 });
306 addToHashedNextObjective(pl, neighborMac, nextId);
Saurav Das62ae6792017-05-15 15:34:25 -0700307 }
308 } else {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700309 // linkdown
310 List<PortLabel> pl = Lists.newArrayList();
Saurav Das261c3002017-06-13 15:35:54 -0700311 dstSet.forEach(dst -> {
312 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700313 pl.add(new PortLabel(link.src().port(), edgeLabel));
Saurav Das261c3002017-06-13 15:35:54 -0700314 });
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700315 removeFromHashedNextObjective(pl, neighborMac, nextId);
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700316 }
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700317 }
Saurav Das8a3022d2017-05-05 17:01:08 -0700318 }
319
Saurav Das62ae6792017-05-15 15:34:25 -0700320 /**
Saurav Dasfe0b05e2017-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;
331
332 PortLabel(PortNumber port, int edgeLabel) {
333 this.port = port;
334 this.edgeLabel = edgeLabel;
335 }
336
337 @Override
338 public String toString() {
339 return port.toString() + "/" + String.valueOf(edgeLabel);
340 }
341 }
342
343 /**
344 * Makes a call to the FlowObjective service to add buckets to
345 * a hashed group. User must ensure that all the ports & labels are meant
346 * same neighbor (ie. dstMac).
Saurav Das62ae6792017-05-15 15:34:25 -0700347 *
Pier Luigiecb09f42018-01-14 21:56:11 +0100348 * @param portLabels a collection of port & label combinations to add
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700349 * to the hash group identified by the nextId
Saurav Das62ae6792017-05-15 15:34:25 -0700350 * @param dstMac destination mac address of next-hop
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700351 * @param nextId id for next-objective to which buckets will be added
Saurav Dasfbe74572017-08-03 18:30:35 -0700352 *
Saurav Das62ae6792017-05-15 15:34:25 -0700353 */
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700354 private void addToHashedNextObjective(Collection<PortLabel> portLabels,
355 MacAddress dstMac, Integer nextId) {
Saurav Das8a3022d2017-05-05 17:01:08 -0700356 // setup metadata to pass to nextObjective - indicate the vlan on egress
357 // if needed by the switch pipeline. Since hashed next-hops are always to
358 // other neighboring routers, there is no subnet assigned on those ports.
359 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
Saurav Das9bf49582018-08-13 15:34:26 -0700360 metabuilder.matchVlanId(srManager.getDefaultInternalVlan());
Saurav Das8a3022d2017-05-05 17:01:08 -0700361 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
362 .withId(nextId)
363 .withType(NextObjective.Type.HASHED)
Saurav Das8a3022d2017-05-05 17:01:08 -0700364 .withMeta(metabuilder.build())
365 .fromApp(appId);
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700366 // Create the new buckets to be updated
367 portLabels.forEach(pl -> {
368 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
369 tBuilder.setOutput(pl.port)
370 .setEthDst(dstMac)
371 .setEthSrc(nodeMacAddr);
372 if (pl.edgeLabel != DestinationSet.NO_EDGE_LABEL) {
373 tBuilder.pushMpls()
374 .copyTtlOut()
375 .setMpls(MplsLabel.mplsLabel(pl.edgeLabel));
376 }
377 nextObjBuilder.addTreatment(tBuilder.build());
378 });
379
380 log.debug("addToHash in device {}: Adding Bucket with port/label {} "
381 + "to nextId {}", deviceId, portLabels, nextId);
Saurav Das8a3022d2017-05-05 17:01:08 -0700382
383 ObjectiveContext context = new DefaultObjectiveContext(
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700384 (objective) -> log.debug("addToHash port/label {} addedTo "
385 + "NextObj {} on {}", portLabels, nextId, deviceId),
Saurav Das8a3022d2017-05-05 17:01:08 -0700386 (objective, error) ->
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700387 log.warn("addToHash failed to add port/label {} to"
388 + " NextObj {} on {}: {}", portLabels,
Saurav Das62ae6792017-05-15 15:34:25 -0700389 nextId, deviceId, error));
Saurav Das8a3022d2017-05-05 17:01:08 -0700390 NextObjective nextObjective = nextObjBuilder.addToExisting(context);
391 flowObjectiveService.next(deviceId, nextObjective);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700392 }
393
394 /**
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700395 * Makes a call to the FlowObjective service to remove buckets from
396 * a hash group. User must ensure that all the ports & labels are meant
397 * same neighbor (ie. dstMac).
Saurav Dasfbe74572017-08-03 18:30:35 -0700398 *
Pier Luigiecb09f42018-01-14 21:56:11 +0100399 * @param portLabels a collection of port & label combinations to remove
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700400 * from the hash group identified by the nextId
Saurav Dasfbe74572017-08-03 18:30:35 -0700401 * @param dstMac destination mac address of next-hop
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700402 * @param nextId id for next-objective from which buckets will be removed
Saurav Dasfbe74572017-08-03 18:30:35 -0700403 */
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700404 private void removeFromHashedNextObjective(Collection<PortLabel> portLabels,
405 MacAddress dstMac, Integer nextId) {
Saurav Dasfbe74572017-08-03 18:30:35 -0700406 NextObjective.Builder nextObjBuilder = DefaultNextObjective
407 .builder()
408 .withType(NextObjective.Type.HASHED) //same as original
409 .withId(nextId)
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700410 .fromApp(appId);
411 // Create the buckets to be removed
412 portLabels.forEach(pl -> {
413 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
414 tBuilder.setOutput(pl.port)
415 .setEthDst(dstMac)
416 .setEthSrc(nodeMacAddr);
417 if (pl.edgeLabel != DestinationSet.NO_EDGE_LABEL) {
418 tBuilder.pushMpls()
419 .copyTtlOut()
420 .setMpls(MplsLabel.mplsLabel(pl.edgeLabel));
421 }
422 nextObjBuilder.addTreatment(tBuilder.build());
423 });
424 log.debug("removeFromHash in device {}: Removing Bucket with port/label"
425 + " {} from nextId {}", deviceId, portLabels, nextId);
Saurav Das62ae6792017-05-15 15:34:25 -0700426
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700427 ObjectiveContext context = new DefaultObjectiveContext(
428 (objective) -> log.debug("port/label {} removedFrom NextObj"
429 + " {} on {}", portLabels, nextId, deviceId),
430 (objective, error) ->
431 log.warn("port/label {} failed to removeFrom NextObj {} on "
432 + "{}: {}", portLabels, nextId, deviceId, error));
433 NextObjective nextObjective = nextObjBuilder.removeFromExisting(context);
Saurav Dasfbe74572017-08-03 18:30:35 -0700434 flowObjectiveService.next(deviceId, nextObjective);
435 }
Saurav Das62ae6792017-05-15 15:34:25 -0700436
437 /**
438 * Checks all the hash-groups in the target-switch meant for the destination
439 * switch, and either adds or removes buckets to make the neighbor-set
440 * match the given next-hops. Typically called by the master instance of the
441 * destination switch, which may be different from the master instance of the
442 * target switch where hash-group changes are made.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700443 *
Saurav Das62ae6792017-05-15 15:34:25 -0700444 * @param targetSw the switch in which the hash groups will be edited
445 * @param nextHops the current next hops for the target switch to reach
446 * the dest sw
447 * @param destSw the destination switch
448 * @param revoke true if hash groups need to remove buckets from the
449 * the groups to match the current next hops
450 * @return true if calls are made to edit buckets, or if no edits are required
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700451 */
Saurav Das62ae6792017-05-15 15:34:25 -0700452 public boolean fixHashGroups(DeviceId targetSw, Set<DeviceId> nextHops,
453 DeviceId destSw, boolean revoke) {
454 // temporary storage of keys to be updated
Saurav Das261c3002017-06-13 15:35:54 -0700455 Map<DestinationSetNextObjectiveStoreKey, Set<DeviceId>> tempStore =
Saurav Das62ae6792017-05-15 15:34:25 -0700456 new HashMap<>();
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700457 boolean foundNextObjective = false, success = true;
Charles Chan319d1a22015-11-03 10:42:14 -0800458
Saurav Das261c3002017-06-13 15:35:54 -0700459 // retrieve hash-groups meant for destSw, which have destinationSets
Saurav Das62ae6792017-05-15 15:34:25 -0700460 // with different neighbors than the given next-hops
Saurav Das261c3002017-06-13 15:35:54 -0700461 for (DestinationSetNextObjectiveStoreKey dskey : dsNextObjStore.keySet()) {
462 if (!dskey.deviceId().equals(targetSw) ||
463 !dskey.destinationSet().getDestinationSwitches().contains(destSw)) {
Saurav Das62ae6792017-05-15 15:34:25 -0700464 continue;
465 }
466 foundNextObjective = true;
Saurav Das261c3002017-06-13 15:35:54 -0700467 NextNeighbors nhops = dsNextObjStore.get(dskey);
468 Set<DeviceId> currNeighbors = nhops.nextHops(destSw);
469 int edgeLabel = dskey.destinationSet().getEdgeLabel(destSw);
470 Integer nextId = nhops.nextId();
Saurav Dascea556f2018-03-05 14:37:16 -0800471 if (currNeighbors == null || nextHops == null) {
472 log.warn("fixing hash groups but found currNeighbors:{} or nextHops:{}"
473 + " in targetSw:{} for dstSw:{}", currNeighbors,
474 nextHops, targetSw, destSw);
475 success &= false;
476 continue;
477 }
Charles Chan319d1a22015-11-03 10:42:14 -0800478
Saurav Das97241862018-02-14 14:14:54 -0800479 // some store elements may not be hashed next-objectives - ignore them
480 if (isSimpleNextObjective(dskey)) {
481 log.debug("Ignoring {} of SIMPLE nextObj for targetSw:{}"
482 + " -> dstSw:{} with current nextHops:{} to new"
483 + " nextHops: {} in nextId:{}",
484 (revoke) ? "removal" : "addition", targetSw, destSw,
485 currNeighbors, nextHops, nextId);
Saurav Dascea556f2018-03-05 14:37:16 -0800486 if ((revoke && !nextHops.isEmpty())
487 || (!revoke && !nextHops.equals(currNeighbors))) {
Saurav Das68e1b6a2018-06-11 17:02:31 -0700488 log.debug("Simple next objective cannot be edited to "
Saurav Das97241862018-02-14 14:14:54 -0800489 + "move from {} to {}", currNeighbors, nextHops);
490 }
491 continue;
492 }
493
Saurav Das62ae6792017-05-15 15:34:25 -0700494 Set<DeviceId> diff;
495 if (revoke) {
496 diff = Sets.difference(currNeighbors, nextHops);
497 log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next "
498 + "hops:{} ..removing {}", targetSw, destSw, nextId,
499 currNeighbors, diff);
500 } else {
501 diff = Sets.difference(nextHops, currNeighbors);
502 log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next "
503 + "hops:{} ..adding {}", targetSw, destSw, nextId,
504 currNeighbors, diff);
505 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700506 boolean suc = updateAllPortsToNextHop(diff, edgeLabel, nextId,
507 revoke);
508 if (suc) {
509 // to update neighbor set with changes made
Saurav Das62ae6792017-05-15 15:34:25 -0700510 if (revoke) {
Saurav Das261c3002017-06-13 15:35:54 -0700511 tempStore.put(dskey, Sets.difference(currNeighbors, diff));
Saurav Das62ae6792017-05-15 15:34:25 -0700512 } else {
Saurav Das261c3002017-06-13 15:35:54 -0700513 tempStore.put(dskey, Sets.union(currNeighbors, diff));
Saurav Das62ae6792017-05-15 15:34:25 -0700514 }
sangho2165d222015-05-01 09:38:25 -0700515 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700516 success &= suc;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700517 }
518
Saurav Das62ae6792017-05-15 15:34:25 -0700519 if (!foundNextObjective) {
520 log.debug("Cannot find any nextObjectives for route targetSw:{} "
521 + "-> dstSw:{}", targetSw, destSw);
522 return true; // nothing to do, return true so ECMPspg is updated
523 }
524
Saurav Das261c3002017-06-13 15:35:54 -0700525 // update the dsNextObjectiveStore with new destinationSet to nextId mappings
526 for (DestinationSetNextObjectiveStoreKey key : tempStore.keySet()) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700527 NextNeighbors currentNextHops = dsNextObjStore.get(key);
528 if (currentNextHops == null) {
529 log.warn("fixHashGroups could not update global store in "
530 + "device {} .. missing nextNeighbors for key {}",
531 deviceId, key);
Saurav Das62ae6792017-05-15 15:34:25 -0700532 continue;
533 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700534 Set<DeviceId> newNeighbors = new HashSet<>();
535 newNeighbors.addAll(tempStore.get(key));
536 Map<DeviceId, Set<DeviceId>> oldDstNextHops =
537 ImmutableMap.copyOf(currentNextHops.dstNextHops());
538 currentNextHops.dstNextHops().put(destSw, newNeighbors); //local change
539 log.debug("Updating nsNextObjStore target:{} -> dst:{} in key:{} nextId:{}",
540 targetSw, destSw, key, currentNextHops.nextId());
541 log.debug("Old dstNextHops: {}", oldDstNextHops);
542 log.debug("New dstNextHops: {}", currentNextHops.dstNextHops());
543 // update global store
544 dsNextObjStore.put(key,
545 new NextNeighbors(currentNextHops.dstNextHops(),
546 currentNextHops.nextId()));
Saurav Das62ae6792017-05-15 15:34:25 -0700547 }
Saurav Das97241862018-02-14 14:14:54 -0800548
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700549 // even if one fails and others succeed, return false so ECMPspg not updated
550 return success;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700551 }
552
Saurav Dasfbe74572017-08-03 18:30:35 -0700553 /**
554 * Updates the DestinationSetNextObjectiveStore with any per-destination nexthops
555 * that are not already in the store for the given DestinationSet. Note that
556 * this method does not remove existing next hops for the destinations in the
557 * DestinationSet.
558 *
559 * @param ds the DestinationSet for which the next hops need to be updated
560 * @param newDstNextHops a map of per-destination next hops to update the
561 * destinationSet with
562 * @return true if successful in updating all next hops
563 */
564 private boolean updateNextHops(DestinationSet ds,
Saurav Das261c3002017-06-13 15:35:54 -0700565 Map<DeviceId, Set<DeviceId>> newDstNextHops) {
566 DestinationSetNextObjectiveStoreKey key =
567 new DestinationSetNextObjectiveStoreKey(deviceId, ds);
568 NextNeighbors currNext = dsNextObjStore.get(key);
569 Map<DeviceId, Set<DeviceId>> currDstNextHops = currNext.dstNextHops();
570
571 // add newDstNextHops to currDstNextHops for each dst
572 boolean success = true;
573 for (DeviceId dstSw : ds.getDestinationSwitches()) {
574 Set<DeviceId> currNhops = currDstNextHops.get(dstSw);
575 Set<DeviceId> newNhops = newDstNextHops.get(dstSw);
576 currNhops = (currNhops == null) ? Sets.newHashSet() : currNhops;
577 newNhops = (newNhops == null) ? Sets.newHashSet() : newNhops;
578 int edgeLabel = ds.getEdgeLabel(dstSw);
579 int nextId = currNext.nextId();
580
581 // new next hops should be added
582 boolean suc = updateAllPortsToNextHop(Sets.difference(newNhops, currNhops),
583 edgeLabel, nextId, false);
584 if (suc) {
585 currNhops.addAll(newNhops);
586 currDstNextHops.put(dstSw, currNhops); // this is only a local change
587 }
588 success &= suc;
589 }
590
591 if (success) {
592 // update global store
593 dsNextObjStore.put(key, new NextNeighbors(currDstNextHops,
594 currNext.nextId()));
595 log.debug("Updated device:{} ds:{} new next-hops: {}", deviceId, ds,
596 dsNextObjStore.get(key));
597 }
598 return success;
599 }
600
Saurav Dasfbe74572017-08-03 18:30:35 -0700601 /**
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700602 * Adds or removes buckets for all ports to a set of neighbor devices. Caller
603 * needs to ensure that the given neighbors are all next hops towards the
604 * same destination (represented by the given edgeLabel).
Saurav Dasfbe74572017-08-03 18:30:35 -0700605 *
606 * @param neighbors set of neighbor device ids
607 * @param edgeLabel MPLS label to use in buckets
608 * @param nextId the nextObjective to change
609 * @param revoke true if buckets need to be removed, false if they need to
610 * be added
611 * @return true if successful in adding or removing buckets for all ports
612 * to the neighbors
613 */
614 private boolean updateAllPortsToNextHop(Set<DeviceId> neighbors, int edgeLabel,
Saurav Das261c3002017-06-13 15:35:54 -0700615 int nextId, boolean revoke) {
Saurav Dasfbe74572017-08-03 18:30:35 -0700616 for (DeviceId neighbor : neighbors) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700617 MacAddress neighborMac;
Saurav Das261c3002017-06-13 15:35:54 -0700618 try {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700619 neighborMac = deviceConfig.getDeviceMac(neighbor);
Saurav Das261c3002017-06-13 15:35:54 -0700620 } catch (DeviceConfigNotFoundException e) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700621 log.warn(e.getMessage() + " Aborting updateAllPortsToNextHop"
622 + " for nextId:" + nextId);
Saurav Das261c3002017-06-13 15:35:54 -0700623 return false;
624 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700625 Collection<PortNumber> portsToNeighbor = devicePortMap.get(neighbor);
626 if (portsToNeighbor == null || portsToNeighbor.isEmpty()) {
Saurav Das261c3002017-06-13 15:35:54 -0700627 log.warn("No ports found in dev:{} for neighbor:{} .. cannot "
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700628 + "updateAllPortsToNextHop for nextId: {}",
Saurav Das261c3002017-06-13 15:35:54 -0700629 deviceId, neighbor, nextId);
630 return false;
631 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700632 List<PortLabel> pl = Lists.newArrayList();
633 portsToNeighbor.forEach(p -> pl.add(new PortLabel(p, edgeLabel)));
Saurav Das261c3002017-06-13 15:35:54 -0700634 if (revoke) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700635 log.debug("updateAllPortsToNextHops in device {}: Removing Bucket(s) "
636 + "with Port/Label:{} to next object id {}",
637 deviceId, pl, nextId);
638 removeFromHashedNextObjective(pl, neighborMac, nextId);
Saurav Das261c3002017-06-13 15:35:54 -0700639 } else {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700640 log.debug("fixHashGroup in device {}: Adding Bucket(s) "
641 + "with Port/Label: {} to next object id {}",
642 deviceId, pl, nextId);
643 addToHashedNextObjective(pl, neighborMac, nextId);
Saurav Das261c3002017-06-13 15:35:54 -0700644 }
645 }
646 return true;
647 }
648
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700649 /**
Saurav Das97241862018-02-14 14:14:54 -0800650 * Returns true if the destination set is meant for swap or multi-labeled
651 * packet transport, and MPLS ECMP is not supported.
652 *
653 * @param dskey the key representing the destination set
654 * @return true if destination set is meant for simple next objectives
655 */
656 boolean isSimpleNextObjective(DestinationSetNextObjectiveStoreKey dskey) {
657 return (dskey.destinationSet().notBos() || dskey.destinationSet().swap())
658 && !srManager.getMplsEcmp();
659 }
660
661 /**
Saurav Das3fb28272017-03-04 16:08:47 -0800662 * Adds or removes a port that has been configured with a vlan to a broadcast group
663 * for bridging. Should only be called by the master instance for this device.
Saurav Dasf0f592d2016-11-18 15:21:57 -0800664 *
665 * @param port the port on this device that needs to be added/removed to a bcast group
Saurav Das3fb28272017-03-04 16:08:47 -0800666 * @param vlanId the vlan id corresponding to the broadcast domain/group
667 * @param popVlan indicates if packets should be sent out untagged or not out
668 * of the port. If true, indicates an access (untagged) or native vlan
669 * configuration. If false, indicates a trunk (tagged) vlan config.
Saurav Dasf0f592d2016-11-18 15:21:57 -0800670 * @param portUp true if port is enabled, false if disabled
Saurav Dasf0f592d2016-11-18 15:21:57 -0800671 */
Saurav Das3fb28272017-03-04 16:08:47 -0800672 public void processEdgePort(PortNumber port, VlanId vlanId,
673 boolean popVlan, boolean portUp) {
Saurav Dasf0f592d2016-11-18 15:21:57 -0800674 //get the next id for the subnet and edit it.
Charles Chan10b0fb72017-02-02 16:20:42 -0800675 Integer nextId = getVlanNextObjectiveId(vlanId);
Saurav Dasf0f592d2016-11-18 15:21:57 -0800676 if (nextId == -1) {
677 if (portUp) {
678 log.debug("**Creating flooding group for first port enabled in"
Saurav Das2b6a00f2017-12-05 15:00:23 -0800679 + " vlan {} on dev {} port {}", vlanId, deviceId, port);
Charles Chan10b0fb72017-02-02 16:20:42 -0800680 createBcastGroupFromVlan(vlanId, Collections.singleton(port));
Saurav Dasf0f592d2016-11-18 15:21:57 -0800681 } else {
682 log.warn("Could not find flooding group for subnet {} on dev:{} when"
Charles Chan10b0fb72017-02-02 16:20:42 -0800683 + " removing port:{}", vlanId, deviceId, port);
Saurav Dasf0f592d2016-11-18 15:21:57 -0800684 }
685 return;
686 }
687
688 log.info("**port{} in device {}: {} Bucket with Port {} to"
689 + " next-id {}", (portUp) ? "UP" : "DOWN", deviceId,
690 (portUp) ? "Adding" : "Removing",
691 port, nextId);
692 // Create the bucket to be added or removed
693 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Saurav Das3fb28272017-03-04 16:08:47 -0800694 if (popVlan) {
695 tBuilder.popVlan();
696 }
Saurav Dasf0f592d2016-11-18 15:21:57 -0800697 tBuilder.setOutput(port);
698
Saurav Dasf0f592d2016-11-18 15:21:57 -0800699 TrafficSelector metadata =
Saurav Das3fb28272017-03-04 16:08:47 -0800700 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
Saurav Dasf0f592d2016-11-18 15:21:57 -0800701
702 NextObjective.Builder nextObjBuilder = DefaultNextObjective
703 .builder().withId(nextId)
704 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
705 .addTreatment(tBuilder.build())
706 .withMeta(metadata);
707
708 ObjectiveContext context = new DefaultObjectiveContext(
709 (objective) -> log.debug("port {} successfully {} NextObj {} on {}",
710 port, (portUp) ? "addedTo" : "removedFrom",
711 nextId, deviceId),
712 (objective, error) ->
713 log.warn("port {} failed to {} NextObj {} on {}: {}",
714 port, (portUp) ? "addTo" : "removeFrom",
715 nextId, deviceId, error));
716
717 NextObjective nextObj = (portUp) ? nextObjBuilder.addToExisting(context)
718 : nextObjBuilder.removeFromExisting(context);
719 log.debug("edgePort processed: Submited next objective {} in device {}",
720 nextId, deviceId);
721 flowObjectiveService.next(deviceId, nextObj);
722 }
723
724 /**
Saurav Das97241862018-02-14 14:14:54 -0800725 * Returns the next objective of type hashed (or simple) associated with the
726 * destination set. In addition, updates the existing next-objective if new
727 * route-paths found have resulted in the addition of new next-hops to a
728 * particular destination. If there is no existing next objective for this
729 * destination set, this method would create a next objective and return the
730 * nextId. Optionally metadata can be passed in for the creation of the next
731 * objective. If the parameter simple is true then a simple next objective
732 * is created instead of a hashed one.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700733 *
Saurav Das261c3002017-06-13 15:35:54 -0700734 * @param ds destination set
735 * @param nextHops a map of per destination next hops
Saurav Das4c35fc42015-11-20 15:27:53 -0800736 * @param meta metadata passed into the creation of a Next Objective
Saurav Das97241862018-02-14 14:14:54 -0800737 * @param simple if true, a simple next objective will be created instead of
738 * a hashed next objective
Saurav Das4c35fc42015-11-20 15:27:53 -0800739 * @return int if found or -1 if there are errors in the creation of the
Saurav Das97241862018-02-14 14:14:54 -0800740 * neighbor set.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700741 */
Saurav Das261c3002017-06-13 15:35:54 -0700742 public int getNextObjectiveId(DestinationSet ds,
743 Map<DeviceId, Set<DeviceId>> nextHops,
Saurav Das97241862018-02-14 14:14:54 -0800744 TrafficSelector meta, boolean simple) {
Saurav Das261c3002017-06-13 15:35:54 -0700745 NextNeighbors next = dsNextObjStore.
746 get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
747 if (next == null) {
748 log.debug("getNextObjectiveId in device{}: Next objective id "
749 + "not found for {} ... creating", deviceId, ds);
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700750 log.trace("getNextObjectiveId: nsNextObjStore contents for device {}: {}",
751 deviceId,
Saurav Das261c3002017-06-13 15:35:54 -0700752 dsNextObjStore.entrySet()
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700753 .stream()
754 .filter((nsStoreEntry) ->
755 (nsStoreEntry.getKey().deviceId().equals(deviceId)))
756 .collect(Collectors.toList()));
Saurav Das261c3002017-06-13 15:35:54 -0700757
Saurav Das97241862018-02-14 14:14:54 -0800758 createGroupFromDestinationSet(ds, nextHops, meta, simple);
Saurav Das261c3002017-06-13 15:35:54 -0700759 next = dsNextObjStore.
760 get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
761 if (next == null) {
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700762 log.warn("getNextObjectiveId: unable to create next objective");
Saurav Das261c3002017-06-13 15:35:54 -0700763 // failure in creating group
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700764 return -1;
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700765 } else {
766 log.debug("getNextObjectiveId in device{}: Next objective id {} "
Saurav Das261c3002017-06-13 15:35:54 -0700767 + "created for {}", deviceId, next.nextId(), ds);
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700768 }
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700769 } else {
770 log.trace("getNextObjectiveId in device{}: Next objective id {} "
Saurav Das261c3002017-06-13 15:35:54 -0700771 + "found for {}", deviceId, next.nextId(), ds);
772 // should fix hash groups too if next-hops have changed
773 if (!next.dstNextHops().equals(nextHops)) {
774 log.debug("Nexthops have changed for dev:{} nextId:{} ..updating",
775 deviceId, next.nextId());
776 if (!updateNextHops(ds, nextHops)) {
777 // failure in updating group
778 return -1;
779 }
780 }
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700781 }
Saurav Das261c3002017-06-13 15:35:54 -0700782 return next.nextId();
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700783 }
784
sangho4a5c42a2015-05-20 22:16:38 -0700785 /**
Charles Chan10b0fb72017-02-02 16:20:42 -0800786 * Returns the next objective of type broadcast associated with the vlan,
Saurav Das2d94d312015-11-24 23:21:05 -0800787 * or -1 if no such objective exists. Note that this method does NOT create
788 * the next objective as a side-effect. It is expected that is objective is
Saurav Dasf0f592d2016-11-18 15:21:57 -0800789 * created at startup from network configuration. Typically this is used
790 * for L2 flooding within the subnet configured on the switch.
Charles Chan77277672015-10-20 16:24:19 -0700791 *
Charles Chan10b0fb72017-02-02 16:20:42 -0800792 * @param vlanId vlan id
Charles Chan77277672015-10-20 16:24:19 -0700793 * @return int if found or -1
794 */
Charles Chan10b0fb72017-02-02 16:20:42 -0800795 public int getVlanNextObjectiveId(VlanId vlanId) {
796 Integer nextId = vlanNextObjStore.
797 get(new VlanNextObjectiveStoreKey(deviceId, vlanId));
Charles Chanc6ad7752015-10-29 14:58:10 -0700798
799 return (nextId != null) ? nextId : -1;
Charles Chan77277672015-10-20 16:24:19 -0700800 }
801
802 /**
Saurav Das2d94d312015-11-24 23:21:05 -0800803 * Returns the next objective of type simple associated with the port on the
804 * device, given the treatment. Different treatments to the same port result
805 * in different next objectives. If no such objective exists, this method
Saurav Das2cb38292017-03-29 19:09:17 -0700806 * creates one (if requested) and returns the id. Optionally metadata can be passed in for
Saurav Dasf0f592d2016-11-18 15:21:57 -0800807 * the creation of the objective. Typically this is used for L2 and L3 forwarding
808 * to compute nodes and containers/VMs on the compute nodes directly attached
809 * to the switch.
Saurav Das2d94d312015-11-24 23:21:05 -0800810 *
811 * @param portNum the port number for the simple next objective
812 * @param treatment the actions to apply on the packets (should include outport)
813 * @param meta optional metadata passed into the creation of the next objective
Saurav Das2cb38292017-03-29 19:09:17 -0700814 * @param createIfMissing true if a next object should be created if not found
Saurav Das2d94d312015-11-24 23:21:05 -0800815 * @return int if found or created, -1 if there are errors during the
816 * creation of the next objective.
817 */
818 public int getPortNextObjectiveId(PortNumber portNum, TrafficTreatment treatment,
Saurav Das2cb38292017-03-29 19:09:17 -0700819 TrafficSelector meta, boolean createIfMissing) {
Charles Chanb7f75ac2016-01-11 18:28:54 -0800820 Integer nextId = portNextObjStore
Saurav Das368cf212017-03-15 15:15:14 -0700821 .get(new PortNextObjectiveStoreKey(deviceId, portNum, treatment, meta));
Saurav Das2cb38292017-03-29 19:09:17 -0700822 if (nextId != null) {
823 return nextId;
824 }
825 log.debug("getPortNextObjectiveId in device {}: Next objective id "
826 + "not found for port: {} .. {}", deviceId, portNum,
827 (createIfMissing) ? "creating" : "aborting");
828 if (!createIfMissing) {
829 return -1;
830 }
831 // create missing next objective
832 createGroupFromPort(portNum, treatment, meta);
833 nextId = portNextObjStore.get(new PortNextObjectiveStoreKey(deviceId, portNum,
834 treatment, meta));
Saurav Das2d94d312015-11-24 23:21:05 -0800835 if (nextId == null) {
Saurav Das2cb38292017-03-29 19:09:17 -0700836 log.warn("getPortNextObjectiveId: unable to create next obj"
837 + "for dev:{} port:{}", deviceId, portNum);
838 return -1;
Charles Chanb7f75ac2016-01-11 18:28:54 -0800839 }
840 return nextId;
841 }
842
843 /**
sangho4a5c42a2015-05-20 22:16:38 -0700844 * Checks if the next objective ID (group) for the neighbor set exists or not.
845 *
846 * @param ns neighbor set to check
847 * @return true if it exists, false otherwise
848 */
Saurav Das261c3002017-06-13 15:35:54 -0700849 public boolean hasNextObjectiveId(DestinationSet ns) {
850 NextNeighbors nextHops = dsNextObjStore.
851 get(new DestinationSetNextObjectiveStoreKey(deviceId, ns));
852 if (nextHops == null) {
sangho4a5c42a2015-05-20 22:16:38 -0700853 return false;
854 }
855
856 return true;
857 }
858
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700859 private void populateNeighborMaps() {
860 Set<Link> outgoingLinks = linkService.getDeviceEgressLinks(deviceId);
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700861 for (Link link : outgoingLinks) {
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700862 if (link.type() != Link.Type.DIRECT) {
863 continue;
864 }
865 addNeighborAtPort(link.dst().deviceId(), link.src().port());
866 }
867 }
868
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700869 protected void addNeighborAtPort(DeviceId neighborId,
870 PortNumber portToNeighbor) {
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700871 // Update DeviceToPort database
872 log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
873 deviceId, neighborId, portToNeighbor);
Saurav Das4c35fc42015-11-20 15:27:53 -0800874 Set<PortNumber> ports = Collections
875 .newSetFromMap(new ConcurrentHashMap<PortNumber, Boolean>());
876 ports.add(portToNeighbor);
877 Set<PortNumber> portnums = devicePortMap.putIfAbsent(neighborId, ports);
878 if (portnums != null) {
879 portnums.add(portToNeighbor);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700880 }
881
882 // Update portToDevice database
Saurav Das97241862018-02-14 14:14:54 -0800883 // should always update as neighbor could have changed on this port
884 DeviceId prev = portDeviceMap.put(portToNeighbor, neighborId);
Saurav Das4c35fc42015-11-20 15:27:53 -0800885 if (prev != null) {
Saurav Dasdebcf882018-04-06 20:16:01 -0700886 log.warn("Device/port: {}/{} previous neighbor: {}, current neighbor: {} ",
Saurav Das62ae6792017-05-15 15:34:25 -0700887 deviceId, portToNeighbor, prev, neighborId);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700888 }
889 }
890
sangho27462c62015-05-14 00:39:53 -0700891 /**
Saurav Das261c3002017-06-13 15:35:54 -0700892 * Creates a NextObjective for a hash group in this device from a given
Saurav Das97241862018-02-14 14:14:54 -0800893 * DestinationSet. If the parameter simple is true, a simple next objective
894 * is created instead.
sangho27462c62015-05-14 00:39:53 -0700895 *
Saurav Das261c3002017-06-13 15:35:54 -0700896 * @param ds the DestinationSet
897 * @param neighbors a map for each destination and its next-hops
Saurav Das4c35fc42015-11-20 15:27:53 -0800898 * @param meta metadata passed into the creation of a Next Objective
Saurav Das97241862018-02-14 14:14:54 -0800899 * @param simple if true, a simple next objective will be created instead of
900 * a hashed next objective
sangho27462c62015-05-14 00:39:53 -0700901 */
Saurav Das261c3002017-06-13 15:35:54 -0700902 public void createGroupFromDestinationSet(DestinationSet ds,
903 Map<DeviceId, Set<DeviceId>> neighbors,
904 TrafficSelector meta,
Saurav Das97241862018-02-14 14:14:54 -0800905 boolean simple) {
Saurav Das261c3002017-06-13 15:35:54 -0700906 int nextId = flowObjectiveService.allocateNextId();
Saurav Das97241862018-02-14 14:14:54 -0800907 NextObjective.Type type = (simple) ? NextObjective.Type.SIMPLE
908 : NextObjective.Type.HASHED;
Saurav Das261c3002017-06-13 15:35:54 -0700909 if (neighbors == null || neighbors.isEmpty()) {
910 log.warn("createGroupsFromDestinationSet: needs at least one neighbor"
911 + "to create group in dev:{} for ds: {} with next-hops {}",
912 deviceId, ds, neighbors);
913 return;
914 }
Saurav Das261c3002017-06-13 15:35:54 -0700915
916 NextObjective.Builder nextObjBuilder = DefaultNextObjective
917 .builder()
918 .withId(nextId)
919 .withType(type)
920 .fromApp(appId);
921 if (meta != null) {
922 nextObjBuilder.withMeta(meta);
923 }
924
925 // create treatment buckets for each neighbor for each dst Device
926 // except in the special case where we only want to pick a single
Saurav Das97241862018-02-14 14:14:54 -0800927 // neighbor/port for a simple nextObj
Saurav Das261c3002017-06-13 15:35:54 -0700928 boolean foundSingleNeighbor = false;
929 boolean treatmentAdded = false;
930 Map<DeviceId, Set<DeviceId>> dstNextHops = new ConcurrentHashMap<>();
931 for (DeviceId dst : ds.getDestinationSwitches()) {
932 Set<DeviceId> nextHops = neighbors.get(dst);
933 if (nextHops == null || nextHops.isEmpty()) {
934 continue;
Pier Ventre229fd0b2016-10-31 16:49:19 -0700935 }
Saurav Das261c3002017-06-13 15:35:54 -0700936
937 if (foundSingleNeighbor) {
938 break;
939 }
940
941 for (DeviceId neighborId : nextHops) {
Saurav Das4c35fc42015-11-20 15:27:53 -0800942 if (devicePortMap.get(neighborId) == null) {
943 log.warn("Neighbor {} is not in the port map yet for dev:{}",
944 neighborId, deviceId);
sangho2165d222015-05-01 09:38:25 -0700945 return;
Jon Hall31d84782017-01-18 20:15:44 -0800946 } else if (devicePortMap.get(neighborId).isEmpty()) {
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700947 log.warn("There are no ports for "
Saurav Das4c35fc42015-11-20 15:27:53 -0800948 + "the Device {} in the port map yet", neighborId);
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700949 return;
sangho2165d222015-05-01 09:38:25 -0700950 }
951
Saurav Das4c35fc42015-11-20 15:27:53 -0800952 MacAddress neighborMac;
Charles Chan319d1a22015-11-03 10:42:14 -0800953 try {
Saurav Das4c35fc42015-11-20 15:27:53 -0800954 neighborMac = deviceConfig.getDeviceMac(neighborId);
Charles Chan319d1a22015-11-03 10:42:14 -0800955 } catch (DeviceConfigNotFoundException e) {
Saurav Das261c3002017-06-13 15:35:54 -0700956 log.warn(e.getMessage() + " Aborting createGroupsFromDestinationset.");
Charles Chan319d1a22015-11-03 10:42:14 -0800957 return;
958 }
Saurav Das261c3002017-06-13 15:35:54 -0700959 // For each port to the neighbor, we create a new treatment
Pier Ventre229fd0b2016-10-31 16:49:19 -0700960 Set<PortNumber> neighborPorts = devicePortMap.get(neighborId);
Saurav Das97241862018-02-14 14:14:54 -0800961 // In this case we need a SIMPLE nextObj. We randomly pick a port
962 if (simple) {
Pier Ventre229fd0b2016-10-31 16:49:19 -0700963 int size = devicePortMap.get(neighborId).size();
964 int index = RandomUtils.nextInt(0, size);
965 neighborPorts = Collections.singleton(
Saurav Das261c3002017-06-13 15:35:54 -0700966 Iterables.get(devicePortMap.get(neighborId),
967 index));
968 foundSingleNeighbor = true;
Pier Ventre229fd0b2016-10-31 16:49:19 -0700969 }
Andreas Pantelopoulos5e7be3d2017-10-23 12:18:25 -0700970
Pier Ventre229fd0b2016-10-31 16:49:19 -0700971 for (PortNumber sp : neighborPorts) {
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700972 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
973 .builder();
Saurav Das261c3002017-06-13 15:35:54 -0700974 tBuilder.setEthDst(neighborMac).setEthSrc(nodeMacAddr);
975 int edgeLabel = ds.getEdgeLabel(dst);
976 if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
Saurav Das97241862018-02-14 14:14:54 -0800977 if (simple) {
978 // swap label case
979 tBuilder.setMpls(MplsLabel.mplsLabel(edgeLabel));
980 } else {
981 // ecmp with label push case
982 tBuilder.pushMpls().copyTtlOut()
983 .setMpls(MplsLabel.mplsLabel(edgeLabel));
984 }
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700985 }
Charles Chan81ddf6f2018-08-12 15:19:01 -0700986
987 // Set VLAN ID for PW transport. Otherwise pop vlan
Andreas Pantelopoulosb281ae22018-05-01 14:56:05 -0700988 if ((ds.getTypeOfDstSet() == DestinationSet.DestinationSetType.SWAP_NOT_BOS) ||
989 (ds.getTypeOfDstSet() == DestinationSet.DestinationSetType.POP_NOT_BOS)) {
Saurav Das9bf49582018-08-13 15:34:26 -0700990 tBuilder.setVlanId(srManager.getPwTransportVlan());
Charles Chan81ddf6f2018-08-12 15:19:01 -0700991 } else {
992 tBuilder.popVlan();
Andreas Pantelopoulosb281ae22018-05-01 14:56:05 -0700993 }
Charles Chan81ddf6f2018-08-12 15:19:01 -0700994
Saurav Das4c35fc42015-11-20 15:27:53 -0800995 tBuilder.setOutput(sp);
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700996 nextObjBuilder.addTreatment(tBuilder.build());
Saurav Das261c3002017-06-13 15:35:54 -0700997 treatmentAdded = true;
998 //update store
999 Set<DeviceId> existingNeighbors = dstNextHops.get(dst);
1000 if (existingNeighbors == null) {
1001 existingNeighbors = new HashSet<>();
1002 }
1003 existingNeighbors.add(neighborId);
1004 dstNextHops.put(dst, existingNeighbors);
1005 log.debug("creating treatment for port/label {}/{} in next:{}",
1006 sp, edgeLabel, nextId);
1007 }
1008
1009 if (foundSingleNeighbor) {
1010 break;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -07001011 }
1012 }
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -07001013 }
Saurav Das261c3002017-06-13 15:35:54 -07001014
1015 if (!treatmentAdded) {
1016 log.warn("Could not createGroup from DestinationSet {} without any"
1017 + "next hops {}", ds, neighbors);
1018 return;
1019 }
1020 ObjectiveContext context = new DefaultObjectiveContext(
1021 (objective) ->
1022 log.debug("createGroupsFromDestinationSet installed "
1023 + "NextObj {} on {}", nextId, deviceId),
1024 (objective, error) ->
1025 log.warn("createGroupsFromDestinationSet failed to install"
1026 + " NextObj {} on {}: {}", nextId, deviceId, error)
1027 );
1028 NextObjective nextObj = nextObjBuilder.add(context);
1029 log.debug(".. createGroupsFromDestinationSet: Submitted "
1030 + "next objective {} in device {}", nextId, deviceId);
1031 flowObjectiveService.next(deviceId, nextObj);
1032 //update store
1033 dsNextObjStore.put(new DestinationSetNextObjectiveStoreKey(deviceId, ds),
1034 new NextNeighbors(dstNextHops, nextId));
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -07001035 }
1036
Saurav Das2d94d312015-11-24 23:21:05 -08001037 /**
Saurav Dasf0f592d2016-11-18 15:21:57 -08001038 * Creates broadcast groups for all ports in the same subnet for
1039 * all configured subnets.
Saurav Das2d94d312015-11-24 23:21:05 -08001040 */
Charles Chan10b0fb72017-02-02 16:20:42 -08001041 public void createGroupsFromVlanConfig() {
Charles Chan90772a72017-02-08 15:52:08 -08001042 srManager.getVlanPortMap(deviceId).asMap().forEach((vlanId, ports) -> {
Charles Chan10b0fb72017-02-02 16:20:42 -08001043 createBcastGroupFromVlan(vlanId, ports);
Pier Ventreb6a7f342016-11-26 21:05:22 -08001044 });
Saurav Dasf0f592d2016-11-18 15:21:57 -08001045 }
Charles Chanc6ad7752015-10-29 14:58:10 -07001046
Saurav Dasf0f592d2016-11-18 15:21:57 -08001047 /**
Charles Chan10b0fb72017-02-02 16:20:42 -08001048 * Creates a single broadcast group from a given vlan id and list of ports.
Saurav Dasf0f592d2016-11-18 15:21:57 -08001049 *
Charles Chan10b0fb72017-02-02 16:20:42 -08001050 * @param vlanId vlan id
Saurav Dasf0f592d2016-11-18 15:21:57 -08001051 * @param ports list of ports in the subnet
1052 */
Charles Chan90772a72017-02-08 15:52:08 -08001053 public void createBcastGroupFromVlan(VlanId vlanId, Collection<PortNumber> ports) {
Charles Chan10b0fb72017-02-02 16:20:42 -08001054 VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
Charles Chanc6ad7752015-10-29 14:58:10 -07001055
Charles Chan10b0fb72017-02-02 16:20:42 -08001056 if (vlanNextObjStore.containsKey(key)) {
Saurav Dasf0f592d2016-11-18 15:21:57 -08001057 log.debug("Broadcast group for device {} and subnet {} exists",
Charles Chan10b0fb72017-02-02 16:20:42 -08001058 deviceId, vlanId);
Saurav Dasf0f592d2016-11-18 15:21:57 -08001059 return;
1060 }
Charles Chande6655c2015-12-23 00:15:11 -08001061
Saurav Dasf0f592d2016-11-18 15:21:57 -08001062 TrafficSelector metadata =
Charles Chan10b0fb72017-02-02 16:20:42 -08001063 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
Charles Chan77277672015-10-20 16:24:19 -07001064
Saurav Dasf0f592d2016-11-18 15:21:57 -08001065 int nextId = flowObjectiveService.allocateNextId();
Charles Chan77277672015-10-20 16:24:19 -07001066
Saurav Dasf0f592d2016-11-18 15:21:57 -08001067 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1068 .builder().withId(nextId)
1069 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1070 .withMeta(metadata);
Charles Chan77277672015-10-20 16:24:19 -07001071
Saurav Dasf0f592d2016-11-18 15:21:57 -08001072 ports.forEach(port -> {
1073 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Charles Chan90772a72017-02-08 15:52:08 -08001074 if (toPopVlan(port, vlanId)) {
1075 tBuilder.popVlan();
1076 }
Saurav Dasf0f592d2016-11-18 15:21:57 -08001077 tBuilder.setOutput(port);
1078 nextObjBuilder.addTreatment(tBuilder.build());
Charles Chan77277672015-10-20 16:24:19 -07001079 });
Saurav Dasf0f592d2016-11-18 15:21:57 -08001080
Saurav Das2cb38292017-03-29 19:09:17 -07001081 ObjectiveContext context = new DefaultObjectiveContext(
1082 (objective) ->
1083 log.debug("createBroadcastGroupFromVlan installed "
1084 + "NextObj {} on {}", nextId, deviceId),
1085 (objective, error) ->
1086 log.warn("createBroadcastGroupFromVlan failed to install"
1087 + " NextObj {} on {}: {}", nextId, deviceId, error)
1088 );
1089 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Dasf0f592d2016-11-18 15:21:57 -08001090 flowObjectiveService.next(deviceId, nextObj);
Saurav Das2b6a00f2017-12-05 15:00:23 -08001091 log.debug("createBcastGroupFromVlan: Submitted next objective {} "
1092 + "for vlan: {} in device {}", nextId, vlanId, deviceId);
Saurav Dasf0f592d2016-11-18 15:21:57 -08001093
Charles Chan10b0fb72017-02-02 16:20:42 -08001094 vlanNextObjStore.put(key, nextId);
Charles Chan77277672015-10-20 16:24:19 -07001095 }
1096
Charles Chanb7f75ac2016-01-11 18:28:54 -08001097 /**
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001098 * Removes a single broadcast group from a given vlan id.
1099 * The group should be empty.
1100 * @param deviceId device Id to remove the group
1101 * @param portNum port number related to the group
1102 * @param vlanId vlan id of the broadcast group to remove
1103 * @param popVlan true if the TrafficTreatment involves pop vlan tag action
1104 */
1105 public void removeBcastGroupFromVlan(DeviceId deviceId, PortNumber portNum,
1106 VlanId vlanId, boolean popVlan) {
1107 VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
1108
1109 if (!vlanNextObjStore.containsKey(key)) {
1110 log.debug("Broadcast group for device {} and subnet {} does not exist",
1111 deviceId, vlanId);
1112 return;
1113 }
1114
1115 TrafficSelector metadata =
1116 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
1117
1118 int nextId = vlanNextObjStore.get(key);
1119
1120 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1121 .builder().withId(nextId)
1122 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1123 .withMeta(metadata);
1124
1125 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1126 if (popVlan) {
1127 tBuilder.popVlan();
1128 }
1129 tBuilder.setOutput(portNum);
1130 nextObjBuilder.addTreatment(tBuilder.build());
1131
1132 ObjectiveContext context = new DefaultObjectiveContext(
1133 (objective) ->
1134 log.debug("removeBroadcastGroupFromVlan removed "
1135 + "NextObj {} on {}", nextId, deviceId),
1136 (objective, error) ->
1137 log.warn("removeBroadcastGroupFromVlan failed to remove "
1138 + " NextObj {} on {}: {}", nextId, deviceId, error)
1139 );
1140 NextObjective nextObj = nextObjBuilder.remove(context);
1141 flowObjectiveService.next(deviceId, nextObj);
1142 log.debug("removeBcastGroupFromVlan: Submited next objective {} in device {}",
1143 nextId, deviceId);
1144
1145 vlanNextObjStore.remove(key, nextId);
1146 }
1147
1148 /**
Charles Chan90772a72017-02-08 15:52:08 -08001149 * Determine if we should pop given vlan before sending packets to the given port.
1150 *
1151 * @param portNumber port number
1152 * @param vlanId vlan id
1153 * @return true if the vlan id is not contained in any vlanTagged config
1154 */
1155 private boolean toPopVlan(PortNumber portNumber, VlanId vlanId) {
Saurav Das261c3002017-06-13 15:35:54 -07001156 return srManager.interfaceService
1157 .getInterfacesByPort(new ConnectPoint(deviceId, portNumber))
Charles Chan90772a72017-02-08 15:52:08 -08001158 .stream().noneMatch(intf -> intf.vlanTagged().contains(vlanId));
1159 }
1160
1161 /**
Saurav Das2d94d312015-11-24 23:21:05 -08001162 * Create simple next objective for a single port. The treatments can include
1163 * all outgoing actions that need to happen on the packet.
1164 *
1165 * @param portNum the outgoing port on the device
1166 * @param treatment the actions to apply on the packets (should include outport)
1167 * @param meta optional data to pass to the driver
1168 */
1169 public void createGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
1170 TrafficSelector meta) {
1171 int nextId = flowObjectiveService.allocateNextId();
1172 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
Saurav Das368cf212017-03-15 15:15:14 -07001173 deviceId, portNum, treatment, meta);
Saurav Das2d94d312015-11-24 23:21:05 -08001174
1175 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1176 .builder().withId(nextId)
1177 .withType(NextObjective.Type.SIMPLE)
1178 .addTreatment(treatment)
1179 .fromApp(appId)
1180 .withMeta(meta);
1181
Saurav Das2cb38292017-03-29 19:09:17 -07001182 ObjectiveContext context = new DefaultObjectiveContext(
1183 (objective) ->
1184 log.debug("createGroupFromPort installed "
1185 + "NextObj {} on {}", nextId, deviceId),
1186 (objective, error) ->
1187 log.warn("createGroupFromPort failed to install"
1188 + " NextObj {} on {}: {}", nextId, deviceId, error)
1189 );
1190 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Das2d94d312015-11-24 23:21:05 -08001191 flowObjectiveService.next(deviceId, nextObj);
1192 log.debug("createGroupFromPort: Submited next objective {} in device {} "
1193 + "for port {}", nextId, deviceId, portNum);
1194
1195 portNextObjStore.put(key, nextId);
1196 }
1197
sangho27462c62015-05-14 00:39:53 -07001198 /**
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001199 * Removes simple next objective for a single port.
1200 *
1201 * @param deviceId device id that has the port to deal with
1202 * @param portNum the outgoing port on the device
1203 * @param vlanId vlan id associated with the port
1204 * @param popVlan true if POP_VLAN action is applied on the packets, false otherwise
1205 */
1206 public void removePortNextObjective(DeviceId deviceId, PortNumber portNum, VlanId vlanId, boolean popVlan) {
1207 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
1208 mbuilder.matchVlanId(vlanId);
1209
1210 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
1211 tbuilder.immediate().setOutput(portNum);
1212 if (popVlan) {
1213 tbuilder.immediate().popVlan();
1214 }
1215
1216 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, portNum,
1217 tbuilder.build(), mbuilder.build(), false);
1218
1219 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
1220 deviceId, portNum, tbuilder.build(), mbuilder.build());
1221 if (portNextObjId != -1 && portNextObjStore.containsKey(key)) {
1222 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1223 .builder().withId(portNextObjId)
1224 .withType(NextObjective.Type.SIMPLE).fromApp(appId);
1225 ObjectiveContext context = new DefaultObjectiveContext(
1226 (objective) -> log.debug("removePortNextObjective removes NextObj {} on {}",
1227 portNextObjId, deviceId),
1228 (objective, error) ->
1229 log.warn("removePortNextObjective failed to remove NextObj {} on {}: {}",
1230 portNextObjId, deviceId, error));
1231 NextObjective nextObjective = nextObjBuilder.remove(context);
1232 log.info("**removePortNextObjective: Submitted "
1233 + "next objective {} in device {}",
1234 portNextObjId, deviceId);
1235 flowObjectiveService.next(deviceId, nextObjective);
1236
1237 portNextObjStore.remove(key);
1238 }
1239 }
1240 /**
sangho27462c62015-05-14 00:39:53 -07001241 * Removes groups for the next objective ID given.
1242 *
1243 * @param objectiveId next objective ID to remove
1244 * @return true if succeeds, false otherwise
1245 */
1246 public boolean removeGroup(int objectiveId) {
Saurav Das261c3002017-06-13 15:35:54 -07001247 for (Map.Entry<DestinationSetNextObjectiveStoreKey, NextNeighbors> e :
1248 dsNextObjStore.entrySet()) {
1249 if (e.getValue().nextId() != objectiveId) {
1250 continue;
1251 }
Pier Luigiecb09f42018-01-14 21:56:11 +01001252 // Right now it is just used in TunnelHandler
1253 // remember in future that PW transit groups could
1254 // be Indirect groups
sangho27462c62015-05-14 00:39:53 -07001255 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1256 .builder().withId(objectiveId)
1257 .withType(NextObjective.Type.HASHED).fromApp(appId);
Charles Chana4ee4f92016-04-23 14:48:16 -07001258 ObjectiveContext context = new DefaultObjectiveContext(
1259 (objective) -> log.debug("RemoveGroup removes NextObj {} on {}",
1260 objectiveId, deviceId),
1261 (objective, error) ->
1262 log.warn("RemoveGroup failed to remove NextObj {} on {}: {}",
1263 objectiveId, deviceId, error));
1264 NextObjective nextObjective = nextObjBuilder.remove(context);
Saurav Das4c35fc42015-11-20 15:27:53 -08001265 log.info("**removeGroup: Submited "
1266 + "next objective {} in device {}",
1267 objectiveId, deviceId);
sangho27462c62015-05-14 00:39:53 -07001268 flowObjectiveService.next(deviceId, nextObjective);
1269
Saurav Das261c3002017-06-13 15:35:54 -07001270 dsNextObjStore.remove(e.getKey());
sangho4a5c42a2015-05-20 22:16:38 -07001271 return true;
sangho27462c62015-05-14 00:39:53 -07001272 }
1273
1274 return false;
1275 }
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001276 /**
1277 * Remove simple next objective for a single port. The treatments can include
1278 * all outgoing actions that need to happen on the packet.
1279 *
1280 * @param portNum the outgoing port on the device
1281 * @param treatment the actions applied on the packets (should include outport)
1282 * @param meta optional data to pass to the driver
1283 */
1284 public void removeGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
1285 TrafficSelector meta) {
1286 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
1287 deviceId, portNum, treatment, meta);
1288 Integer nextId = portNextObjStore.get(key);
1289
1290 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1291 .builder().withId(nextId)
1292 .withType(NextObjective.Type.SIMPLE)
1293 .addTreatment(treatment)
1294 .fromApp(appId)
1295 .withMeta(meta);
1296
1297 ObjectiveContext context = new DefaultObjectiveContext(
1298 (objective) ->
1299 log.info("removeGroupFromPort installed "
1300 + "NextObj {} on {}", nextId, deviceId),
1301 (objective, error) ->
1302 log.warn("removeGroupFromPort failed to install"
1303 + " NextObj {} on {}: {}", nextId, deviceId, error)
1304 );
1305 NextObjective nextObj = nextObjBuilder.remove(context);
1306 flowObjectiveService.next(deviceId, nextObj);
1307 log.info("removeGroupFromPort: Submitted next objective {} in device {} "
1308 + "for port {}", nextId, deviceId, portNum);
1309
1310 portNextObjStore.remove(key);
1311 }
Srikanth Vavilapalli8c83f1d2015-05-22 13:47:31 -07001312
Charles Chanb7f75ac2016-01-11 18:28:54 -08001313 /**
1314 * Removes all groups from all next objective stores.
1315 */
Saurav Das261c3002017-06-13 15:35:54 -07001316 /*public void removeAllGroups() {
1317 for (Map.Entry<NeighborSetNextObjectiveStoreKey, NextNeighbors> entry:
Saurav Das62af8802015-12-04 10:52:59 -08001318 nsNextObjStore.entrySet()) {
Saurav Das261c3002017-06-13 15:35:54 -07001319 removeGroup(entry.getValue().nextId());
Saurav Das62af8802015-12-04 10:52:59 -08001320 }
1321 for (Map.Entry<PortNextObjectiveStoreKey, Integer> entry:
1322 portNextObjStore.entrySet()) {
1323 removeGroup(entry.getValue());
1324 }
Charles Chan10b0fb72017-02-02 16:20:42 -08001325 for (Map.Entry<VlanNextObjectiveStoreKey, Integer> entry:
1326 vlanNextObjStore.entrySet()) {
Saurav Das62af8802015-12-04 10:52:59 -08001327 removeGroup(entry.getValue());
1328 }
Saurav Das261c3002017-06-13 15:35:54 -07001329 }*/ //XXX revisit
1330
Saurav Dasfbe74572017-08-03 18:30:35 -07001331 /**
1332 * Triggers a one time bucket verification operation on all hash groups
1333 * on this device.
1334 */
1335 public void triggerBucketCorrector() {
1336 BucketCorrector bc = new BucketCorrector();
1337 bc.run();
1338 }
1339
Jonghwan Hyuna4ce0aa2018-02-12 16:43:45 +09001340 /**
1341 * Modifies L2IG bucket when the interface configuration is updated, especially
1342 * when the interface has same VLAN ID but the VLAN type is changed (e.g., from
1343 * vlan-tagged [10] to vlan-untagged 10), which requires changes on
1344 * TrafficTreatment in turn.
1345 *
1346 * @param portNumber the port on this device that needs to be updated
1347 * @param vlanId the vlan id corresponding to this port
1348 * @param pushVlan indicates if packets should be sent out untagged or not out
1349 * from the port. If true, updated TrafficTreatment involves
1350 * pop vlan tag action. If false, updated TrafficTreatment
1351 * does not involve pop vlan tag action.
1352 */
1353 public void updateL2InterfaceGroupBucket(PortNumber portNumber, VlanId vlanId, boolean pushVlan) {
1354 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1355 if (pushVlan) {
1356 tBuilder.popVlan();
1357 }
1358 tBuilder.setOutput(portNumber);
1359
1360 TrafficSelector metadata =
1361 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
1362
1363 int nextId = getVlanNextObjectiveId(vlanId);
1364
1365 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1366 .builder().withId(nextId)
1367 .withType(NextObjective.Type.SIMPLE).fromApp(appId)
1368 .addTreatment(tBuilder.build())
1369 .withMeta(metadata);
1370
1371 ObjectiveContext context = new DefaultObjectiveContext(
1372 (objective) -> log.debug("port {} successfully updated NextObj {} on {}",
1373 portNumber, nextId, deviceId),
1374 (objective, error) ->
1375 log.warn("port {} failed to updated NextObj {} on {}: {}",
1376 portNumber, nextId, deviceId, error));
1377
1378 flowObjectiveService.next(deviceId, nextObjBuilder.modify(context));
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001379 }
1380
Jonghwan Hyuna4ce0aa2018-02-12 16:43:45 +09001381 /**
1382 * Adds a single port to the L2FG or removes it from the L2FG.
1383 *
1384 * @param vlanId the vlan id corresponding to this port
1385 * @param portNum the port on this device to be updated
1386 * @param nextId the next objective ID for the given vlan id
1387 * @param install if true, adds the port to L2FG. If false, removes it from L2FG.
1388 */
1389 public void updateGroupFromVlanConfiguration(VlanId vlanId, PortNumber portNum, int nextId, boolean install) {
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001390 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1391 if (toPopVlan(portNum, vlanId)) {
1392 tBuilder.popVlan();
1393 }
1394 tBuilder.setOutput(portNum);
1395
1396 TrafficSelector metadata =
1397 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
1398
1399 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1400 .builder().withId(nextId)
1401 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1402 .addTreatment(tBuilder.build())
1403 .withMeta(metadata);
1404
1405 ObjectiveContext context = new DefaultObjectiveContext(
1406 (objective) -> log.debug("port {} successfully removedFrom NextObj {} on {}",
1407 portNum, nextId, deviceId),
1408 (objective, error) ->
1409 log.warn("port {} failed to removedFrom NextObj {} on {}: {}",
1410 portNum, nextId, deviceId, error));
1411
1412 if (install) {
1413 flowObjectiveService.next(deviceId, nextObjBuilder.addToExisting(context));
1414 } else {
1415 flowObjectiveService.next(deviceId, nextObjBuilder.removeFromExisting(context));
1416 }
1417 }
Saurav Das8a3022d2017-05-05 17:01:08 -07001418
1419 /**
Saurav Dasfe0b05e2017-08-14 16:44:43 -07001420 * Performs bucket verification operation for all hash groups in this device.
1421 * Checks RouteHandler to ensure that routing is stable before attempting
1422 * verification. Verification involves creating a nextObjective with
1423 * operation VERIFY for existing next objectives in the store, and passing
1424 * it to the driver. It is the driver that actually performs the verification
1425 * by adding or removing buckets to match the verification next objective
1426 * created here.
Saurav Das8a3022d2017-05-05 17:01:08 -07001427 */
Saurav Dasfbe74572017-08-03 18:30:35 -07001428 protected final class BucketCorrector implements Runnable {
1429 Integer nextId;
Saurav Das8a3022d2017-05-05 17:01:08 -07001430
Saurav Dasfbe74572017-08-03 18:30:35 -07001431 BucketCorrector() {
1432 this.nextId = null;
1433 }
1434
1435 BucketCorrector(Integer nextId) {
1436 this.nextId = nextId;
Saurav Das8a3022d2017-05-05 17:01:08 -07001437 }
1438
1439 @Override
1440 public void run() {
Saurav Dasfbe74572017-08-03 18:30:35 -07001441 if (!srManager.mastershipService.isLocalMaster(deviceId)) {
1442 return;
Saurav Das8a3022d2017-05-05 17:01:08 -07001443 }
Saurav Dasfbe74572017-08-03 18:30:35 -07001444 DefaultRoutingHandler rh = srManager.getRoutingHandler();
1445 if (rh == null) {
1446 return;
1447 }
1448 if (!rh.isRoutingStable()) {
1449 return;
1450 }
1451 rh.acquireRoutingLock();
1452 try {
Saurav Dasfe0b05e2017-08-14 16:44:43 -07001453 log.trace("running bucket corrector for dev: {}", deviceId);
Saurav Dasfbe74572017-08-03 18:30:35 -07001454 Set<DestinationSetNextObjectiveStoreKey> dsKeySet = dsNextObjStore.entrySet()
1455 .stream()
1456 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
Pier Luigiecb09f42018-01-14 21:56:11 +01001457 // Filter out PW transit groups or include them if MPLS ECMP is supported
Saurav Das97241862018-02-14 14:14:54 -08001458 .filter(entry -> !entry.getKey().destinationSet().notBos() ||
1459 (entry.getKey().destinationSet().notBos() && srManager.getMplsEcmp()))
1460 // Filter out simple SWAP groups or include them if MPLS ECMP is supported
1461 .filter(entry -> !entry.getKey().destinationSet().swap() ||
1462 (entry.getKey().destinationSet().swap() && srManager.getMplsEcmp()))
Saurav Dasfbe74572017-08-03 18:30:35 -07001463 .map(entry -> entry.getKey())
1464 .collect(Collectors.toSet());
1465 for (DestinationSetNextObjectiveStoreKey dsKey : dsKeySet) {
1466 NextNeighbors next = dsNextObjStore.get(dsKey);
1467 if (next == null) {
1468 continue;
1469 }
1470 int nid = next.nextId();
1471 if (nextId != null && nextId != nid) {
1472 continue;
1473 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -07001474 log.trace("bkt-corr: dsNextObjStore for device {}: {}",
Saurav Dasfbe74572017-08-03 18:30:35 -07001475 deviceId, dsKey, next);
1476 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
Saurav Das9bf49582018-08-13 15:34:26 -07001477 metabuilder.matchVlanId(srManager.getDefaultInternalVlan());
Saurav Dasfbe74572017-08-03 18:30:35 -07001478 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
1479 .withId(nid)
1480 .withType(NextObjective.Type.HASHED)
1481 .withMeta(metabuilder.build())
1482 .fromApp(appId);
1483
1484 next.dstNextHops().forEach((dstDev, nextHops) -> {
1485 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dstDev);
1486 nextHops.forEach(neighbor -> {
1487 MacAddress neighborMac;
1488 try {
1489 neighborMac = deviceConfig.getDeviceMac(neighbor);
1490 } catch (DeviceConfigNotFoundException e) {
1491 log.warn(e.getMessage() + " Aborting neighbor"
1492 + neighbor);
1493 return;
1494 }
1495 devicePortMap.get(neighbor).forEach(port -> {
Saurav Dasfe0b05e2017-08-14 16:44:43 -07001496 log.trace("verify in device {} nextId {}: bucket with"
Saurav Dasfbe74572017-08-03 18:30:35 -07001497 + " port/label {}/{} to dst {} via {}",
1498 deviceId, nid, port, edgeLabel,
1499 dstDev, neighbor);
Saurav Das97241862018-02-14 14:14:54 -08001500 nextObjBuilder
1501 .addTreatment(treatmentBuilder(port,
1502 neighborMac,
1503 dsKey.destinationSet().swap(),
1504 edgeLabel));
Saurav Dasfbe74572017-08-03 18:30:35 -07001505 });
1506 });
1507 });
1508
1509 NextObjective nextObjective = nextObjBuilder.verify();
1510 flowObjectiveService.next(deviceId, nextObjective);
1511 }
1512 } finally {
1513 rh.releaseRoutingLock();
1514 }
1515
1516 }
1517
1518 TrafficTreatment treatmentBuilder(PortNumber outport, MacAddress dstMac,
Saurav Das97241862018-02-14 14:14:54 -08001519 boolean swap, int edgeLabel) {
Saurav Dasfbe74572017-08-03 18:30:35 -07001520 TrafficTreatment.Builder tBuilder =
1521 DefaultTrafficTreatment.builder();
1522 tBuilder.setOutput(outport)
1523 .setEthDst(dstMac)
1524 .setEthSrc(nodeMacAddr);
1525 if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
Saurav Das97241862018-02-14 14:14:54 -08001526 if (swap) {
1527 // swap label case
1528 tBuilder.setMpls(MplsLabel.mplsLabel(edgeLabel));
1529 } else {
1530 // ecmp with label push case
1531 tBuilder.pushMpls()
1532 .copyTtlOut()
1533 .setMpls(MplsLabel.mplsLabel(edgeLabel));
1534 }
Saurav Dasfbe74572017-08-03 18:30:35 -07001535 }
1536 return tBuilder.build();
Saurav Das8a3022d2017-05-05 17:01:08 -07001537 }
1538 }
1539
Pier Luigiecb09f42018-01-14 21:56:11 +01001540}