blob: 1ae24e5cfa84f26c6bd9fb7395865f4a27de3228 [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;
Charles Chan10b0fb72017-02-02 16:20:42 -080069import static org.onosproject.segmentrouting.SegmentRoutingManager.INTERNAL_VLAN;
Pier Ventre229fd0b2016-10-31 16:49:19 -070070import static org.slf4j.LoggerFactory.getLogger;
71
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070072/**
Srikanth Vavilapalli64505482015-04-21 13:04:13 -070073 * Default ECMP group handler creation module. This component creates a set of
74 * ECMP groups for every neighbor that this device is connected to based on
75 * whether the current device is an edge device or a transit device.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070076 */
77public class DefaultGroupHandler {
Ray Milkey5247fa92018-01-12 14:22:06 -080078 private static final Logger log = getLogger(DefaultGroupHandler.class);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070079
Saurav Dasfbe74572017-08-03 18:30:35 -070080 private static final long VERIFY_INTERVAL = 30; // secs
81
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070082 protected final DeviceId deviceId;
83 protected final ApplicationId appId;
84 protected final DeviceProperties deviceConfig;
85 protected final List<Integer> allSegmentIds;
Pier Ventreadb4ae62016-11-23 09:57:42 -080086 protected int ipv4NodeSegmentId = -1;
87 protected int ipv6NodeSegmentId = -1;
Charles Chan319d1a22015-11-03 10:42:14 -080088 protected boolean isEdgeRouter = false;
89 protected MacAddress nodeMacAddr = null;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070090 protected LinkService linkService;
Srikanth Vavilapalli64505482015-04-21 13:04:13 -070091 protected FlowObjectiveService flowObjectiveService;
Saurav Das62ae6792017-05-15 15:34:25 -070092 /**
93 * local store for neighbor-device-ids and the set of ports on this device
94 * that connect to the same neighbor.
95 */
Saurav Das4c35fc42015-11-20 15:27:53 -080096 protected ConcurrentHashMap<DeviceId, Set<PortNumber>> devicePortMap =
97 new ConcurrentHashMap<>();
Saurav Das62ae6792017-05-15 15:34:25 -070098 /**
99 * local store for ports on this device connected to neighbor-device-id.
100 */
Saurav Das4c35fc42015-11-20 15:27:53 -0800101 protected ConcurrentHashMap<PortNumber, DeviceId> portDeviceMap =
102 new ConcurrentHashMap<>();
Saurav Das62ae6792017-05-15 15:34:25 -0700103
Saurav Das261c3002017-06-13 15:35:54 -0700104 // distributed store for (device+destination-set) mapped to next-id and neighbors
105 protected EventuallyConsistentMap<DestinationSetNextObjectiveStoreKey, NextNeighbors>
106 dsNextObjStore = null;
Saurav Dasf0f592d2016-11-18 15:21:57 -0800107 // distributed store for (device+subnet-ip-prefix) mapped to next-id
Charles Chan10b0fb72017-02-02 16:20:42 -0800108 protected EventuallyConsistentMap<VlanNextObjectiveStoreKey, Integer>
109 vlanNextObjStore = null;
Saurav Dasf0f592d2016-11-18 15:21:57 -0800110 // distributed store for (device+port+treatment) mapped to next-id
Charles Chanb7f75ac2016-01-11 18:28:54 -0800111 protected EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
112 portNextObjStore = null;
Charles Chande6655c2015-12-23 00:15:11 -0800113 private SegmentRoutingManager srManager;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700114
Saurav Das8a3022d2017-05-05 17:01:08 -0700115 private ScheduledExecutorService executorService
Saurav Dasfbe74572017-08-03 18:30:35 -0700116 = newScheduledThreadPool(1, groupedThreads("bktCorrector", "bktC-%d", log));
Saurav Das8a3022d2017-05-05 17:01:08 -0700117
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700118 protected KryoNamespace.Builder kryo = new KryoNamespace.Builder()
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700119 .register(URI.class).register(HashSet.class)
Saurav Das261c3002017-06-13 15:35:54 -0700120 .register(DeviceId.class).register(PortNumber.class)
121 .register(DestinationSet.class).register(PolicyGroupIdentifier.class)
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700122 .register(PolicyGroupParams.class)
123 .register(GroupBucketIdentifier.class)
124 .register(GroupBucketIdentifier.BucketOutputType.class);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700125
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700126 protected DefaultGroupHandler(DeviceId deviceId, ApplicationId appId,
127 DeviceProperties config,
128 LinkService linkService,
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700129 FlowObjectiveService flowObjService,
Charles Chande6655c2015-12-23 00:15:11 -0800130 SegmentRoutingManager srManager) {
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700131 this.deviceId = checkNotNull(deviceId);
132 this.appId = checkNotNull(appId);
133 this.deviceConfig = checkNotNull(config);
134 this.linkService = checkNotNull(linkService);
Charles Chan319d1a22015-11-03 10:42:14 -0800135 this.allSegmentIds = checkNotNull(config.getAllDeviceSegmentIds());
136 try {
Pier Ventreadb4ae62016-11-23 09:57:42 -0800137 this.ipv4NodeSegmentId = config.getIPv4SegmentId(deviceId);
138 this.ipv6NodeSegmentId = config.getIPv6SegmentId(deviceId);
Charles Chan319d1a22015-11-03 10:42:14 -0800139 this.isEdgeRouter = config.isEdgeDevice(deviceId);
140 this.nodeMacAddr = checkNotNull(config.getDeviceMac(deviceId));
141 } catch (DeviceConfigNotFoundException e) {
142 log.warn(e.getMessage()
143 + " Skipping value assignment in DefaultGroupHandler");
144 }
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700145 this.flowObjectiveService = flowObjService;
Saurav Das261c3002017-06-13 15:35:54 -0700146 this.dsNextObjStore = srManager.dsNextObjStore();
Ray Milkeyb85de082017-04-05 09:42:04 -0700147 this.vlanNextObjStore = srManager.vlanNextObjStore();
148 this.portNextObjStore = srManager.portNextObjStore();
Charles Chande6655c2015-12-23 00:15:11 -0800149 this.srManager = srManager;
Saurav Dasfbe74572017-08-03 18:30:35 -0700150 executorService.scheduleWithFixedDelay(new BucketCorrector(), 10,
151 VERIFY_INTERVAL,
152 TimeUnit.SECONDS);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700153 populateNeighborMaps();
154 }
155
156 /**
Saurav Dasfbe74572017-08-03 18:30:35 -0700157 * Gracefully shuts down a groupHandler. Typically called when the handler is
158 * no longer needed.
159 */
160 public void shutdown() {
161 executorService.shutdown();
162 }
163
164 /**
Saurav Das62ae6792017-05-15 15:34:25 -0700165 * Creates a group handler object.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700166 *
167 * @param deviceId device identifier
168 * @param appId application identifier
169 * @param config interface to retrieve the device properties
170 * @param linkService link service object
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700171 * @param flowObjService flow objective service object
Charles Chanb7f75ac2016-01-11 18:28:54 -0800172 * @param srManager segment routing manager
Charles Chan319d1a22015-11-03 10:42:14 -0800173 * @throws DeviceConfigNotFoundException if the device configuration is not found
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700174 * @return default group handler type
175 */
Saurav Das2d94d312015-11-24 23:21:05 -0800176 public static DefaultGroupHandler createGroupHandler(
Saurav Dasfbe74572017-08-03 18:30:35 -0700177 DeviceId deviceId,
178 ApplicationId appId,
179 DeviceProperties config,
180 LinkService linkService,
181 FlowObjectiveService flowObjService,
182 SegmentRoutingManager srManager)
183 throws DeviceConfigNotFoundException {
Saurav Das62ae6792017-05-15 15:34:25 -0700184 return new DefaultGroupHandler(deviceId, appId, config,
185 linkService,
186 flowObjService,
187 srManager);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700188 }
189
190 /**
Saurav Das97241862018-02-14 14:14:54 -0800191 * Updates local stores for link-src-device/port to neighbor (link-dst) for
192 * link that has come up.
Saurav Das62ae6792017-05-15 15:34:25 -0700193 *
194 * @param link the infrastructure link
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700195 */
Saurav Das62ae6792017-05-15 15:34:25 -0700196 public void portUpForLink(Link link) {
Saurav Dasfbe74572017-08-03 18:30:35 -0700197 if (!link.src().deviceId().equals(deviceId)) {
198 log.warn("linkUp: deviceId{} doesn't match with link src {}",
199 deviceId, link.src().deviceId());
200 return;
201 }
Saurav Das62ae6792017-05-15 15:34:25 -0700202
Saurav Dasfbe74572017-08-03 18:30:35 -0700203 log.info("* portUpForLink: Device {} linkUp at local port {} to "
204 + "neighbor {}", deviceId, link.src().port(), link.dst().deviceId());
205 // ensure local state is updated even if linkup is aborted later on
206 addNeighborAtPort(link.dst().deviceId(),
207 link.src().port());
208 }
Saurav Das62ae6792017-05-15 15:34:25 -0700209
Saurav Dasfbe74572017-08-03 18:30:35 -0700210 /**
Saurav Das97241862018-02-14 14:14:54 -0800211 * Updates local stores for link-src-device/port to neighbor (link-dst) for
212 * link that has gone down.
Saurav Dasfbe74572017-08-03 18:30:35 -0700213 *
Saurav Das97241862018-02-14 14:14:54 -0800214 * @param link the infrastructure link
Saurav Dasfbe74572017-08-03 18:30:35 -0700215 */
Saurav Das97241862018-02-14 14:14:54 -0800216 public void portDownForLink(Link link) {
217 PortNumber port = link.src().port();
Saurav Dasfbe74572017-08-03 18:30:35 -0700218 if (portDeviceMap.get(port) == null) {
219 log.warn("portDown: unknown port");
220 return;
221 }
Saurav Das62ae6792017-05-15 15:34:25 -0700222
Saurav Dasfbe74572017-08-03 18:30:35 -0700223 log.debug("Device {} portDown {} to neighbor {}", deviceId, port,
224 portDeviceMap.get(port));
225 devicePortMap.get(portDeviceMap.get(port)).remove(port);
226 portDeviceMap.remove(port);
227 }
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700228
229 /**
Saurav Das97241862018-02-14 14:14:54 -0800230 * Cleans up local stores for removed neighbor device.
231 *
232 * @param neighborId the device identifier for the neighbor device
233 */
234 public void cleanUpForNeighborDown(DeviceId neighborId) {
235 Set<PortNumber> ports = devicePortMap.remove(neighborId);
236 if (ports != null) {
237 ports.forEach(p -> portDeviceMap.remove(p));
238 }
239 }
240
241 /**
Saurav Das62ae6792017-05-15 15:34:25 -0700242 * Checks all groups in the src-device of link for neighbor sets that include
243 * the dst-device of link, and edits the hash groups according to link up
244 * or down. Should only be called by the master instance of the src-switch
245 * of link. Typically used when there are no route-path changes due to the
246 * link up or down, as the ECMPspg does not change.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700247 *
Saurav Das62ae6792017-05-15 15:34:25 -0700248 * @param link the infrastructure link that has gone down or come up
249 * @param linkDown true if link has gone down
250 * @param firstTime true if link has come up for the first time i.e a link
251 * not seen-before
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700252 */
Saurav Das62ae6792017-05-15 15:34:25 -0700253 public void retryHash(Link link, boolean linkDown, boolean firstTime) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700254 MacAddress neighborMac;
Charles Chan319d1a22015-11-03 10:42:14 -0800255 try {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700256 neighborMac = deviceConfig.getDeviceMac(link.dst().deviceId());
Charles Chan319d1a22015-11-03 10:42:14 -0800257 } catch (DeviceConfigNotFoundException e) {
Saurav Das62ae6792017-05-15 15:34:25 -0700258 log.warn(e.getMessage() + " Aborting retryHash.");
Charles Chan319d1a22015-11-03 10:42:14 -0800259 return;
260 }
Saurav Das261c3002017-06-13 15:35:54 -0700261 // find all the destinationSets related to link
262 Set<DestinationSetNextObjectiveStoreKey> dsKeySet = dsNextObjStore.entrySet()
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700263 .stream()
Saurav Das261c3002017-06-13 15:35:54 -0700264 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
Pier Luigiecb09f42018-01-14 21:56:11 +0100265 // Filter out PW transit groups or include them if MPLS ECMP is supported
Saurav Das97241862018-02-14 14:14:54 -0800266 .filter(entry -> !entry.getKey().destinationSet().notBos() ||
267 (entry.getKey().destinationSet().notBos() && srManager.getMplsEcmp()))
268 // Filter out simple SWAP groups or include them if MPLS ECMP is supported
269 .filter(entry -> !entry.getKey().destinationSet().swap() ||
270 (entry.getKey().destinationSet().swap() && srManager.getMplsEcmp()))
Saurav Das261c3002017-06-13 15:35:54 -0700271 .filter(entry -> entry.getValue().containsNextHop(link.dst().deviceId()))
272 .map(entry -> entry.getKey())
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700273 .collect(Collectors.toSet());
Saurav Das62ae6792017-05-15 15:34:25 -0700274
Saurav Das261c3002017-06-13 15:35:54 -0700275 log.debug("retryHash: dsNextObjStore contents for linkSrc {} -> linkDst {}: {}",
276 deviceId, link.dst().deviceId(), dsKeySet);
277
278 for (DestinationSetNextObjectiveStoreKey dsKey : dsKeySet) {
279 NextNeighbors nextHops = dsNextObjStore.get(dsKey);
280 if (nextHops == null) {
Saurav Das62ae6792017-05-15 15:34:25 -0700281 log.warn("retryHash in device {}, but global store has no record "
Saurav Das261c3002017-06-13 15:35:54 -0700282 + "for dsKey:{}", deviceId, dsKey);
Saurav Das62ae6792017-05-15 15:34:25 -0700283 continue;
284 }
Saurav Das261c3002017-06-13 15:35:54 -0700285 int nextId = nextHops.nextId();
286 Set<DeviceId> dstSet = nextHops.getDstForNextHop(link.dst().deviceId());
Saurav Das62ae6792017-05-15 15:34:25 -0700287 if (!linkDown) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700288 List<PortLabel> pl = Lists.newArrayList();
Saurav Das62ae6792017-05-15 15:34:25 -0700289 if (firstTime) {
290 // some links may have come up before the next-objective was created
291 // we take this opportunity to ensure other ports to same next-hop-dst
292 // are part of the hash group (see CORD-1180). Duplicate additions
293 // to the same hash group are avoided by the driver.
294 for (PortNumber p : devicePortMap.get(link.dst().deviceId())) {
Saurav Das261c3002017-06-13 15:35:54 -0700295 dstSet.forEach(dst -> {
296 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700297 pl.add(new PortLabel(p, edgeLabel));
Saurav Das261c3002017-06-13 15:35:54 -0700298 });
Saurav Das62ae6792017-05-15 15:34:25 -0700299 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700300 addToHashedNextObjective(pl, neighborMac, nextId);
301 } else {
302 // handle only the port that came up
303 dstSet.forEach(dst -> {
304 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
305 pl.add(new PortLabel(link.src().port(), edgeLabel));
306 });
307 addToHashedNextObjective(pl, neighborMac, nextId);
Saurav Das62ae6792017-05-15 15:34:25 -0700308 }
309 } else {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700310 // linkdown
311 List<PortLabel> pl = Lists.newArrayList();
Saurav Das261c3002017-06-13 15:35:54 -0700312 dstSet.forEach(dst -> {
313 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700314 pl.add(new PortLabel(link.src().port(), edgeLabel));
Saurav Das261c3002017-06-13 15:35:54 -0700315 });
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700316 removeFromHashedNextObjective(pl, neighborMac, nextId);
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700317 }
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700318 }
Saurav Das8a3022d2017-05-05 17:01:08 -0700319 }
320
Saurav Das62ae6792017-05-15 15:34:25 -0700321 /**
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700322 * Utility class for associating output ports and the corresponding MPLS
323 * labels to push. In dual-homing, there are different labels to push
324 * corresponding to the destination switches in an edge-pair. If both
325 * destinations are reachable via the same spine, then the output-port to
326 * the spine will be associated with two labels i.e. there will be two
327 * PortLabel objects for the same port but with different labels.
328 */
329 private class PortLabel {
330 PortNumber port;
331 int edgeLabel;
332
333 PortLabel(PortNumber port, int edgeLabel) {
334 this.port = port;
335 this.edgeLabel = edgeLabel;
336 }
337
338 @Override
339 public String toString() {
340 return port.toString() + "/" + String.valueOf(edgeLabel);
341 }
342 }
343
344 /**
345 * Makes a call to the FlowObjective service to add buckets to
346 * a hashed group. User must ensure that all the ports & labels are meant
347 * same neighbor (ie. dstMac).
Saurav Das62ae6792017-05-15 15:34:25 -0700348 *
Pier Luigiecb09f42018-01-14 21:56:11 +0100349 * @param portLabels a collection of port & label combinations to add
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700350 * to the hash group identified by the nextId
Saurav Das62ae6792017-05-15 15:34:25 -0700351 * @param dstMac destination mac address of next-hop
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700352 * @param nextId id for next-objective to which buckets will be added
Saurav Dasfbe74572017-08-03 18:30:35 -0700353 *
Saurav Das62ae6792017-05-15 15:34:25 -0700354 */
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700355 private void addToHashedNextObjective(Collection<PortLabel> portLabels,
356 MacAddress dstMac, Integer nextId) {
Saurav Das8a3022d2017-05-05 17:01:08 -0700357 // setup metadata to pass to nextObjective - indicate the vlan on egress
358 // if needed by the switch pipeline. Since hashed next-hops are always to
359 // other neighboring routers, there is no subnet assigned on those ports.
360 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
361 metabuilder.matchVlanId(INTERNAL_VLAN);
Saurav Das8a3022d2017-05-05 17:01:08 -0700362 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
363 .withId(nextId)
364 .withType(NextObjective.Type.HASHED)
Saurav Das8a3022d2017-05-05 17:01:08 -0700365 .withMeta(metabuilder.build())
366 .fromApp(appId);
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700367 // Create the new buckets to be updated
368 portLabels.forEach(pl -> {
369 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
370 tBuilder.setOutput(pl.port)
371 .setEthDst(dstMac)
372 .setEthSrc(nodeMacAddr);
373 if (pl.edgeLabel != DestinationSet.NO_EDGE_LABEL) {
374 tBuilder.pushMpls()
375 .copyTtlOut()
376 .setMpls(MplsLabel.mplsLabel(pl.edgeLabel));
377 }
378 nextObjBuilder.addTreatment(tBuilder.build());
379 });
380
381 log.debug("addToHash in device {}: Adding Bucket with port/label {} "
382 + "to nextId {}", deviceId, portLabels, nextId);
Saurav Das8a3022d2017-05-05 17:01:08 -0700383
384 ObjectiveContext context = new DefaultObjectiveContext(
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700385 (objective) -> log.debug("addToHash port/label {} addedTo "
386 + "NextObj {} on {}", portLabels, nextId, deviceId),
Saurav Das8a3022d2017-05-05 17:01:08 -0700387 (objective, error) ->
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700388 log.warn("addToHash failed to add port/label {} to"
389 + " NextObj {} on {}: {}", portLabels,
Saurav Das62ae6792017-05-15 15:34:25 -0700390 nextId, deviceId, error));
Saurav Das8a3022d2017-05-05 17:01:08 -0700391 NextObjective nextObjective = nextObjBuilder.addToExisting(context);
392 flowObjectiveService.next(deviceId, nextObjective);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700393 }
394
395 /**
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700396 * Makes a call to the FlowObjective service to remove buckets from
397 * a hash group. User must ensure that all the ports & labels are meant
398 * same neighbor (ie. dstMac).
Saurav Dasfbe74572017-08-03 18:30:35 -0700399 *
Pier Luigiecb09f42018-01-14 21:56:11 +0100400 * @param portLabels a collection of port & label combinations to remove
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700401 * from the hash group identified by the nextId
Saurav Dasfbe74572017-08-03 18:30:35 -0700402 * @param dstMac destination mac address of next-hop
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700403 * @param nextId id for next-objective from which buckets will be removed
Saurav Dasfbe74572017-08-03 18:30:35 -0700404 */
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700405 private void removeFromHashedNextObjective(Collection<PortLabel> portLabels,
406 MacAddress dstMac, Integer nextId) {
Saurav Dasfbe74572017-08-03 18:30:35 -0700407 NextObjective.Builder nextObjBuilder = DefaultNextObjective
408 .builder()
409 .withType(NextObjective.Type.HASHED) //same as original
410 .withId(nextId)
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700411 .fromApp(appId);
412 // Create the buckets to be removed
413 portLabels.forEach(pl -> {
414 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
415 tBuilder.setOutput(pl.port)
416 .setEthDst(dstMac)
417 .setEthSrc(nodeMacAddr);
418 if (pl.edgeLabel != DestinationSet.NO_EDGE_LABEL) {
419 tBuilder.pushMpls()
420 .copyTtlOut()
421 .setMpls(MplsLabel.mplsLabel(pl.edgeLabel));
422 }
423 nextObjBuilder.addTreatment(tBuilder.build());
424 });
425 log.debug("removeFromHash in device {}: Removing Bucket with port/label"
426 + " {} from nextId {}", deviceId, portLabels, nextId);
Saurav Das62ae6792017-05-15 15:34:25 -0700427
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700428 ObjectiveContext context = new DefaultObjectiveContext(
429 (objective) -> log.debug("port/label {} removedFrom NextObj"
430 + " {} on {}", portLabels, nextId, deviceId),
431 (objective, error) ->
432 log.warn("port/label {} failed to removeFrom NextObj {} on "
433 + "{}: {}", portLabels, nextId, deviceId, error));
434 NextObjective nextObjective = nextObjBuilder.removeFromExisting(context);
Saurav Dasfbe74572017-08-03 18:30:35 -0700435 flowObjectiveService.next(deviceId, nextObjective);
436 }
Saurav Das62ae6792017-05-15 15:34:25 -0700437
438 /**
439 * Checks all the hash-groups in the target-switch meant for the destination
440 * switch, and either adds or removes buckets to make the neighbor-set
441 * match the given next-hops. Typically called by the master instance of the
442 * destination switch, which may be different from the master instance of the
443 * target switch where hash-group changes are made.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700444 *
Saurav Das62ae6792017-05-15 15:34:25 -0700445 * @param targetSw the switch in which the hash groups will be edited
446 * @param nextHops the current next hops for the target switch to reach
447 * the dest sw
448 * @param destSw the destination switch
449 * @param revoke true if hash groups need to remove buckets from the
450 * the groups to match the current next hops
451 * @return true if calls are made to edit buckets, or if no edits are required
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700452 */
Saurav Das62ae6792017-05-15 15:34:25 -0700453 public boolean fixHashGroups(DeviceId targetSw, Set<DeviceId> nextHops,
454 DeviceId destSw, boolean revoke) {
455 // temporary storage of keys to be updated
Saurav Das261c3002017-06-13 15:35:54 -0700456 Map<DestinationSetNextObjectiveStoreKey, Set<DeviceId>> tempStore =
Saurav Das62ae6792017-05-15 15:34:25 -0700457 new HashMap<>();
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700458 boolean foundNextObjective = false, success = true;
Charles Chan319d1a22015-11-03 10:42:14 -0800459
Saurav Das261c3002017-06-13 15:35:54 -0700460 // retrieve hash-groups meant for destSw, which have destinationSets
Saurav Das62ae6792017-05-15 15:34:25 -0700461 // with different neighbors than the given next-hops
Saurav Das261c3002017-06-13 15:35:54 -0700462 for (DestinationSetNextObjectiveStoreKey dskey : dsNextObjStore.keySet()) {
463 if (!dskey.deviceId().equals(targetSw) ||
464 !dskey.destinationSet().getDestinationSwitches().contains(destSw)) {
Saurav Das62ae6792017-05-15 15:34:25 -0700465 continue;
466 }
467 foundNextObjective = true;
Saurav Das261c3002017-06-13 15:35:54 -0700468 NextNeighbors nhops = dsNextObjStore.get(dskey);
469 Set<DeviceId> currNeighbors = nhops.nextHops(destSw);
470 int edgeLabel = dskey.destinationSet().getEdgeLabel(destSw);
471 Integer nextId = nhops.nextId();
Saurav Dascea556f2018-03-05 14:37:16 -0800472 if (currNeighbors == null || nextHops == null) {
473 log.warn("fixing hash groups but found currNeighbors:{} or nextHops:{}"
474 + " in targetSw:{} for dstSw:{}", currNeighbors,
475 nextHops, targetSw, destSw);
476 success &= false;
477 continue;
478 }
Charles Chan319d1a22015-11-03 10:42:14 -0800479
Saurav Das97241862018-02-14 14:14:54 -0800480 // some store elements may not be hashed next-objectives - ignore them
481 if (isSimpleNextObjective(dskey)) {
482 log.debug("Ignoring {} of SIMPLE nextObj for targetSw:{}"
483 + " -> dstSw:{} with current nextHops:{} to new"
484 + " nextHops: {} in nextId:{}",
485 (revoke) ? "removal" : "addition", targetSw, destSw,
486 currNeighbors, nextHops, nextId);
Saurav Dascea556f2018-03-05 14:37:16 -0800487 if ((revoke && !nextHops.isEmpty())
488 || (!revoke && !nextHops.equals(currNeighbors))) {
Saurav Das68e1b6a2018-06-11 17:02:31 -0700489 log.debug("Simple next objective cannot be edited to "
Saurav Das97241862018-02-14 14:14:54 -0800490 + "move from {} to {}", currNeighbors, nextHops);
491 }
492 continue;
493 }
494
Saurav Das62ae6792017-05-15 15:34:25 -0700495 Set<DeviceId> diff;
496 if (revoke) {
497 diff = Sets.difference(currNeighbors, nextHops);
498 log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next "
499 + "hops:{} ..removing {}", targetSw, destSw, nextId,
500 currNeighbors, diff);
501 } else {
502 diff = Sets.difference(nextHops, currNeighbors);
503 log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next "
504 + "hops:{} ..adding {}", targetSw, destSw, nextId,
505 currNeighbors, diff);
506 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700507 boolean suc = updateAllPortsToNextHop(diff, edgeLabel, nextId,
508 revoke);
509 if (suc) {
510 // to update neighbor set with changes made
Saurav Das62ae6792017-05-15 15:34:25 -0700511 if (revoke) {
Saurav Das261c3002017-06-13 15:35:54 -0700512 tempStore.put(dskey, Sets.difference(currNeighbors, diff));
Saurav Das62ae6792017-05-15 15:34:25 -0700513 } else {
Saurav Das261c3002017-06-13 15:35:54 -0700514 tempStore.put(dskey, Sets.union(currNeighbors, diff));
Saurav Das62ae6792017-05-15 15:34:25 -0700515 }
sangho2165d222015-05-01 09:38:25 -0700516 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700517 success &= suc;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700518 }
519
Saurav Das62ae6792017-05-15 15:34:25 -0700520 if (!foundNextObjective) {
521 log.debug("Cannot find any nextObjectives for route targetSw:{} "
522 + "-> dstSw:{}", targetSw, destSw);
523 return true; // nothing to do, return true so ECMPspg is updated
524 }
525
Saurav Das261c3002017-06-13 15:35:54 -0700526 // update the dsNextObjectiveStore with new destinationSet to nextId mappings
527 for (DestinationSetNextObjectiveStoreKey key : tempStore.keySet()) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700528 NextNeighbors currentNextHops = dsNextObjStore.get(key);
529 if (currentNextHops == null) {
530 log.warn("fixHashGroups could not update global store in "
531 + "device {} .. missing nextNeighbors for key {}",
532 deviceId, key);
Saurav Das62ae6792017-05-15 15:34:25 -0700533 continue;
534 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700535 Set<DeviceId> newNeighbors = new HashSet<>();
536 newNeighbors.addAll(tempStore.get(key));
537 Map<DeviceId, Set<DeviceId>> oldDstNextHops =
538 ImmutableMap.copyOf(currentNextHops.dstNextHops());
539 currentNextHops.dstNextHops().put(destSw, newNeighbors); //local change
540 log.debug("Updating nsNextObjStore target:{} -> dst:{} in key:{} nextId:{}",
541 targetSw, destSw, key, currentNextHops.nextId());
542 log.debug("Old dstNextHops: {}", oldDstNextHops);
543 log.debug("New dstNextHops: {}", currentNextHops.dstNextHops());
544 // update global store
545 dsNextObjStore.put(key,
546 new NextNeighbors(currentNextHops.dstNextHops(),
547 currentNextHops.nextId()));
Saurav Das62ae6792017-05-15 15:34:25 -0700548 }
Saurav Das97241862018-02-14 14:14:54 -0800549
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700550 // even if one fails and others succeed, return false so ECMPspg not updated
551 return success;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700552 }
553
Saurav Dasfbe74572017-08-03 18:30:35 -0700554 /**
555 * Updates the DestinationSetNextObjectiveStore with any per-destination nexthops
556 * that are not already in the store for the given DestinationSet. Note that
557 * this method does not remove existing next hops for the destinations in the
558 * DestinationSet.
559 *
560 * @param ds the DestinationSet for which the next hops need to be updated
561 * @param newDstNextHops a map of per-destination next hops to update the
562 * destinationSet with
563 * @return true if successful in updating all next hops
564 */
565 private boolean updateNextHops(DestinationSet ds,
Saurav Das261c3002017-06-13 15:35:54 -0700566 Map<DeviceId, Set<DeviceId>> newDstNextHops) {
567 DestinationSetNextObjectiveStoreKey key =
568 new DestinationSetNextObjectiveStoreKey(deviceId, ds);
569 NextNeighbors currNext = dsNextObjStore.get(key);
570 Map<DeviceId, Set<DeviceId>> currDstNextHops = currNext.dstNextHops();
571
572 // add newDstNextHops to currDstNextHops for each dst
573 boolean success = true;
574 for (DeviceId dstSw : ds.getDestinationSwitches()) {
575 Set<DeviceId> currNhops = currDstNextHops.get(dstSw);
576 Set<DeviceId> newNhops = newDstNextHops.get(dstSw);
577 currNhops = (currNhops == null) ? Sets.newHashSet() : currNhops;
578 newNhops = (newNhops == null) ? Sets.newHashSet() : newNhops;
579 int edgeLabel = ds.getEdgeLabel(dstSw);
580 int nextId = currNext.nextId();
581
582 // new next hops should be added
583 boolean suc = updateAllPortsToNextHop(Sets.difference(newNhops, currNhops),
584 edgeLabel, nextId, false);
585 if (suc) {
586 currNhops.addAll(newNhops);
587 currDstNextHops.put(dstSw, currNhops); // this is only a local change
588 }
589 success &= suc;
590 }
591
592 if (success) {
593 // update global store
594 dsNextObjStore.put(key, new NextNeighbors(currDstNextHops,
595 currNext.nextId()));
596 log.debug("Updated device:{} ds:{} new next-hops: {}", deviceId, ds,
597 dsNextObjStore.get(key));
598 }
599 return success;
600 }
601
Saurav Dasfbe74572017-08-03 18:30:35 -0700602 /**
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700603 * Adds or removes buckets for all ports to a set of neighbor devices. Caller
604 * needs to ensure that the given neighbors are all next hops towards the
605 * same destination (represented by the given edgeLabel).
Saurav Dasfbe74572017-08-03 18:30:35 -0700606 *
607 * @param neighbors set of neighbor device ids
608 * @param edgeLabel MPLS label to use in buckets
609 * @param nextId the nextObjective to change
610 * @param revoke true if buckets need to be removed, false if they need to
611 * be added
612 * @return true if successful in adding or removing buckets for all ports
613 * to the neighbors
614 */
615 private boolean updateAllPortsToNextHop(Set<DeviceId> neighbors, int edgeLabel,
Saurav Das261c3002017-06-13 15:35:54 -0700616 int nextId, boolean revoke) {
Saurav Dasfbe74572017-08-03 18:30:35 -0700617 for (DeviceId neighbor : neighbors) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700618 MacAddress neighborMac;
Saurav Das261c3002017-06-13 15:35:54 -0700619 try {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700620 neighborMac = deviceConfig.getDeviceMac(neighbor);
Saurav Das261c3002017-06-13 15:35:54 -0700621 } catch (DeviceConfigNotFoundException e) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700622 log.warn(e.getMessage() + " Aborting updateAllPortsToNextHop"
623 + " for nextId:" + nextId);
Saurav Das261c3002017-06-13 15:35:54 -0700624 return false;
625 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700626 Collection<PortNumber> portsToNeighbor = devicePortMap.get(neighbor);
627 if (portsToNeighbor == null || portsToNeighbor.isEmpty()) {
Saurav Das261c3002017-06-13 15:35:54 -0700628 log.warn("No ports found in dev:{} for neighbor:{} .. cannot "
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700629 + "updateAllPortsToNextHop for nextId: {}",
Saurav Das261c3002017-06-13 15:35:54 -0700630 deviceId, neighbor, nextId);
631 return false;
632 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700633 List<PortLabel> pl = Lists.newArrayList();
634 portsToNeighbor.forEach(p -> pl.add(new PortLabel(p, edgeLabel)));
Saurav Das261c3002017-06-13 15:35:54 -0700635 if (revoke) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700636 log.debug("updateAllPortsToNextHops in device {}: Removing Bucket(s) "
637 + "with Port/Label:{} to next object id {}",
638 deviceId, pl, nextId);
639 removeFromHashedNextObjective(pl, neighborMac, nextId);
Saurav Das261c3002017-06-13 15:35:54 -0700640 } else {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700641 log.debug("fixHashGroup in device {}: Adding Bucket(s) "
642 + "with Port/Label: {} to next object id {}",
643 deviceId, pl, nextId);
644 addToHashedNextObjective(pl, neighborMac, nextId);
Saurav Das261c3002017-06-13 15:35:54 -0700645 }
646 }
647 return true;
648 }
649
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700650 /**
Saurav Das97241862018-02-14 14:14:54 -0800651 * Returns true if the destination set is meant for swap or multi-labeled
652 * packet transport, and MPLS ECMP is not supported.
653 *
654 * @param dskey the key representing the destination set
655 * @return true if destination set is meant for simple next objectives
656 */
657 boolean isSimpleNextObjective(DestinationSetNextObjectiveStoreKey dskey) {
658 return (dskey.destinationSet().notBos() || dskey.destinationSet().swap())
659 && !srManager.getMplsEcmp();
660 }
661
662 /**
Saurav Das3fb28272017-03-04 16:08:47 -0800663 * Adds or removes a port that has been configured with a vlan to a broadcast group
664 * for bridging. Should only be called by the master instance for this device.
Saurav Dasf0f592d2016-11-18 15:21:57 -0800665 *
666 * @param port the port on this device that needs to be added/removed to a bcast group
Saurav Das3fb28272017-03-04 16:08:47 -0800667 * @param vlanId the vlan id corresponding to the broadcast domain/group
668 * @param popVlan indicates if packets should be sent out untagged or not out
669 * of the port. If true, indicates an access (untagged) or native vlan
670 * configuration. If false, indicates a trunk (tagged) vlan config.
Saurav Dasf0f592d2016-11-18 15:21:57 -0800671 * @param portUp true if port is enabled, false if disabled
Saurav Dasf0f592d2016-11-18 15:21:57 -0800672 */
Saurav Das3fb28272017-03-04 16:08:47 -0800673 public void processEdgePort(PortNumber port, VlanId vlanId,
674 boolean popVlan, boolean portUp) {
Saurav Dasf0f592d2016-11-18 15:21:57 -0800675 //get the next id for the subnet and edit it.
Charles Chan10b0fb72017-02-02 16:20:42 -0800676 Integer nextId = getVlanNextObjectiveId(vlanId);
Saurav Dasf0f592d2016-11-18 15:21:57 -0800677 if (nextId == -1) {
678 if (portUp) {
679 log.debug("**Creating flooding group for first port enabled in"
Saurav Das2b6a00f2017-12-05 15:00:23 -0800680 + " vlan {} on dev {} port {}", vlanId, deviceId, port);
Charles Chan10b0fb72017-02-02 16:20:42 -0800681 createBcastGroupFromVlan(vlanId, Collections.singleton(port));
Saurav Dasf0f592d2016-11-18 15:21:57 -0800682 } else {
683 log.warn("Could not find flooding group for subnet {} on dev:{} when"
Charles Chan10b0fb72017-02-02 16:20:42 -0800684 + " removing port:{}", vlanId, deviceId, port);
Saurav Dasf0f592d2016-11-18 15:21:57 -0800685 }
686 return;
687 }
688
689 log.info("**port{} in device {}: {} Bucket with Port {} to"
690 + " next-id {}", (portUp) ? "UP" : "DOWN", deviceId,
691 (portUp) ? "Adding" : "Removing",
692 port, nextId);
693 // Create the bucket to be added or removed
694 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Saurav Das3fb28272017-03-04 16:08:47 -0800695 if (popVlan) {
696 tBuilder.popVlan();
697 }
Saurav Dasf0f592d2016-11-18 15:21:57 -0800698 tBuilder.setOutput(port);
699
Saurav Dasf0f592d2016-11-18 15:21:57 -0800700 TrafficSelector metadata =
Saurav Das3fb28272017-03-04 16:08:47 -0800701 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
Saurav Dasf0f592d2016-11-18 15:21:57 -0800702
703 NextObjective.Builder nextObjBuilder = DefaultNextObjective
704 .builder().withId(nextId)
705 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
706 .addTreatment(tBuilder.build())
707 .withMeta(metadata);
708
709 ObjectiveContext context = new DefaultObjectiveContext(
710 (objective) -> log.debug("port {} successfully {} NextObj {} on {}",
711 port, (portUp) ? "addedTo" : "removedFrom",
712 nextId, deviceId),
713 (objective, error) ->
714 log.warn("port {} failed to {} NextObj {} on {}: {}",
715 port, (portUp) ? "addTo" : "removeFrom",
716 nextId, deviceId, error));
717
718 NextObjective nextObj = (portUp) ? nextObjBuilder.addToExisting(context)
719 : nextObjBuilder.removeFromExisting(context);
720 log.debug("edgePort processed: Submited next objective {} in device {}",
721 nextId, deviceId);
722 flowObjectiveService.next(deviceId, nextObj);
723 }
724
725 /**
Saurav Das97241862018-02-14 14:14:54 -0800726 * Returns the next objective of type hashed (or simple) associated with the
727 * destination set. In addition, updates the existing next-objective if new
728 * route-paths found have resulted in the addition of new next-hops to a
729 * particular destination. If there is no existing next objective for this
730 * destination set, this method would create a next objective and return the
731 * nextId. Optionally metadata can be passed in for the creation of the next
732 * objective. If the parameter simple is true then a simple next objective
733 * is created instead of a hashed one.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700734 *
Saurav Das261c3002017-06-13 15:35:54 -0700735 * @param ds destination set
736 * @param nextHops a map of per destination next hops
Saurav Das4c35fc42015-11-20 15:27:53 -0800737 * @param meta metadata passed into the creation of a Next Objective
Saurav Das97241862018-02-14 14:14:54 -0800738 * @param simple if true, a simple next objective will be created instead of
739 * a hashed next objective
Saurav Das4c35fc42015-11-20 15:27:53 -0800740 * @return int if found or -1 if there are errors in the creation of the
Saurav Das97241862018-02-14 14:14:54 -0800741 * neighbor set.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700742 */
Saurav Das261c3002017-06-13 15:35:54 -0700743 public int getNextObjectiveId(DestinationSet ds,
744 Map<DeviceId, Set<DeviceId>> nextHops,
Saurav Das97241862018-02-14 14:14:54 -0800745 TrafficSelector meta, boolean simple) {
Saurav Das261c3002017-06-13 15:35:54 -0700746 NextNeighbors next = dsNextObjStore.
747 get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
748 if (next == null) {
749 log.debug("getNextObjectiveId in device{}: Next objective id "
750 + "not found for {} ... creating", deviceId, ds);
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700751 log.trace("getNextObjectiveId: nsNextObjStore contents for device {}: {}",
752 deviceId,
Saurav Das261c3002017-06-13 15:35:54 -0700753 dsNextObjStore.entrySet()
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700754 .stream()
755 .filter((nsStoreEntry) ->
756 (nsStoreEntry.getKey().deviceId().equals(deviceId)))
757 .collect(Collectors.toList()));
Saurav Das261c3002017-06-13 15:35:54 -0700758
Saurav Das97241862018-02-14 14:14:54 -0800759 createGroupFromDestinationSet(ds, nextHops, meta, simple);
Saurav Das261c3002017-06-13 15:35:54 -0700760 next = dsNextObjStore.
761 get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
762 if (next == null) {
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700763 log.warn("getNextObjectiveId: unable to create next objective");
Saurav Das261c3002017-06-13 15:35:54 -0700764 // failure in creating group
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700765 return -1;
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700766 } else {
767 log.debug("getNextObjectiveId in device{}: Next objective id {} "
Saurav Das261c3002017-06-13 15:35:54 -0700768 + "created for {}", deviceId, next.nextId(), ds);
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700769 }
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700770 } else {
771 log.trace("getNextObjectiveId in device{}: Next objective id {} "
Saurav Das261c3002017-06-13 15:35:54 -0700772 + "found for {}", deviceId, next.nextId(), ds);
773 // should fix hash groups too if next-hops have changed
774 if (!next.dstNextHops().equals(nextHops)) {
775 log.debug("Nexthops have changed for dev:{} nextId:{} ..updating",
776 deviceId, next.nextId());
777 if (!updateNextHops(ds, nextHops)) {
778 // failure in updating group
779 return -1;
780 }
781 }
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700782 }
Saurav Das261c3002017-06-13 15:35:54 -0700783 return next.nextId();
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700784 }
785
sangho4a5c42a2015-05-20 22:16:38 -0700786 /**
Charles Chan10b0fb72017-02-02 16:20:42 -0800787 * Returns the next objective of type broadcast associated with the vlan,
Saurav Das2d94d312015-11-24 23:21:05 -0800788 * or -1 if no such objective exists. Note that this method does NOT create
789 * the next objective as a side-effect. It is expected that is objective is
Saurav Dasf0f592d2016-11-18 15:21:57 -0800790 * created at startup from network configuration. Typically this is used
791 * for L2 flooding within the subnet configured on the switch.
Charles Chan77277672015-10-20 16:24:19 -0700792 *
Charles Chan10b0fb72017-02-02 16:20:42 -0800793 * @param vlanId vlan id
Charles Chan77277672015-10-20 16:24:19 -0700794 * @return int if found or -1
795 */
Charles Chan10b0fb72017-02-02 16:20:42 -0800796 public int getVlanNextObjectiveId(VlanId vlanId) {
797 Integer nextId = vlanNextObjStore.
798 get(new VlanNextObjectiveStoreKey(deviceId, vlanId));
Charles Chanc6ad7752015-10-29 14:58:10 -0700799
800 return (nextId != null) ? nextId : -1;
Charles Chan77277672015-10-20 16:24:19 -0700801 }
802
803 /**
Saurav Das2d94d312015-11-24 23:21:05 -0800804 * Returns the next objective of type simple associated with the port on the
805 * device, given the treatment. Different treatments to the same port result
806 * in different next objectives. If no such objective exists, this method
Saurav Das2cb38292017-03-29 19:09:17 -0700807 * creates one (if requested) and returns the id. Optionally metadata can be passed in for
Saurav Dasf0f592d2016-11-18 15:21:57 -0800808 * the creation of the objective. Typically this is used for L2 and L3 forwarding
809 * to compute nodes and containers/VMs on the compute nodes directly attached
810 * to the switch.
Saurav Das2d94d312015-11-24 23:21:05 -0800811 *
812 * @param portNum the port number for the simple next objective
813 * @param treatment the actions to apply on the packets (should include outport)
814 * @param meta optional metadata passed into the creation of the next objective
Saurav Das2cb38292017-03-29 19:09:17 -0700815 * @param createIfMissing true if a next object should be created if not found
Saurav Das2d94d312015-11-24 23:21:05 -0800816 * @return int if found or created, -1 if there are errors during the
817 * creation of the next objective.
818 */
819 public int getPortNextObjectiveId(PortNumber portNum, TrafficTreatment treatment,
Saurav Das2cb38292017-03-29 19:09:17 -0700820 TrafficSelector meta, boolean createIfMissing) {
Charles Chanb7f75ac2016-01-11 18:28:54 -0800821 Integer nextId = portNextObjStore
Saurav Das368cf212017-03-15 15:15:14 -0700822 .get(new PortNextObjectiveStoreKey(deviceId, portNum, treatment, meta));
Saurav Das2cb38292017-03-29 19:09:17 -0700823 if (nextId != null) {
824 return nextId;
825 }
826 log.debug("getPortNextObjectiveId in device {}: Next objective id "
827 + "not found for port: {} .. {}", deviceId, portNum,
828 (createIfMissing) ? "creating" : "aborting");
829 if (!createIfMissing) {
830 return -1;
831 }
832 // create missing next objective
833 createGroupFromPort(portNum, treatment, meta);
834 nextId = portNextObjStore.get(new PortNextObjectiveStoreKey(deviceId, portNum,
835 treatment, meta));
Saurav Das2d94d312015-11-24 23:21:05 -0800836 if (nextId == null) {
Saurav Das2cb38292017-03-29 19:09:17 -0700837 log.warn("getPortNextObjectiveId: unable to create next obj"
838 + "for dev:{} port:{}", deviceId, portNum);
839 return -1;
Charles Chanb7f75ac2016-01-11 18:28:54 -0800840 }
841 return nextId;
842 }
843
844 /**
sangho4a5c42a2015-05-20 22:16:38 -0700845 * Checks if the next objective ID (group) for the neighbor set exists or not.
846 *
847 * @param ns neighbor set to check
848 * @return true if it exists, false otherwise
849 */
Saurav Das261c3002017-06-13 15:35:54 -0700850 public boolean hasNextObjectiveId(DestinationSet ns) {
851 NextNeighbors nextHops = dsNextObjStore.
852 get(new DestinationSetNextObjectiveStoreKey(deviceId, ns));
853 if (nextHops == null) {
sangho4a5c42a2015-05-20 22:16:38 -0700854 return false;
855 }
856
857 return true;
858 }
859
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700860 private void populateNeighborMaps() {
861 Set<Link> outgoingLinks = linkService.getDeviceEgressLinks(deviceId);
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700862 for (Link link : outgoingLinks) {
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700863 if (link.type() != Link.Type.DIRECT) {
864 continue;
865 }
866 addNeighborAtPort(link.dst().deviceId(), link.src().port());
867 }
868 }
869
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700870 protected void addNeighborAtPort(DeviceId neighborId,
871 PortNumber portToNeighbor) {
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700872 // Update DeviceToPort database
873 log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
874 deviceId, neighborId, portToNeighbor);
Saurav Das4c35fc42015-11-20 15:27:53 -0800875 Set<PortNumber> ports = Collections
876 .newSetFromMap(new ConcurrentHashMap<PortNumber, Boolean>());
877 ports.add(portToNeighbor);
878 Set<PortNumber> portnums = devicePortMap.putIfAbsent(neighborId, ports);
879 if (portnums != null) {
880 portnums.add(portToNeighbor);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700881 }
882
883 // Update portToDevice database
Saurav Das97241862018-02-14 14:14:54 -0800884 // should always update as neighbor could have changed on this port
885 DeviceId prev = portDeviceMap.put(portToNeighbor, neighborId);
Saurav Das4c35fc42015-11-20 15:27:53 -0800886 if (prev != null) {
Saurav Dasdebcf882018-04-06 20:16:01 -0700887 log.warn("Device/port: {}/{} previous neighbor: {}, current neighbor: {} ",
Saurav Das62ae6792017-05-15 15:34:25 -0700888 deviceId, portToNeighbor, prev, neighborId);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700889 }
890 }
891
sangho27462c62015-05-14 00:39:53 -0700892 /**
Saurav Das261c3002017-06-13 15:35:54 -0700893 * Creates a NextObjective for a hash group in this device from a given
Saurav Das97241862018-02-14 14:14:54 -0800894 * DestinationSet. If the parameter simple is true, a simple next objective
895 * is created instead.
sangho27462c62015-05-14 00:39:53 -0700896 *
Saurav Das261c3002017-06-13 15:35:54 -0700897 * @param ds the DestinationSet
898 * @param neighbors a map for each destination and its next-hops
Saurav Das4c35fc42015-11-20 15:27:53 -0800899 * @param meta metadata passed into the creation of a Next Objective
Saurav Das97241862018-02-14 14:14:54 -0800900 * @param simple if true, a simple next objective will be created instead of
901 * a hashed next objective
sangho27462c62015-05-14 00:39:53 -0700902 */
Saurav Das261c3002017-06-13 15:35:54 -0700903 public void createGroupFromDestinationSet(DestinationSet ds,
904 Map<DeviceId, Set<DeviceId>> neighbors,
905 TrafficSelector meta,
Saurav Das97241862018-02-14 14:14:54 -0800906 boolean simple) {
Saurav Das261c3002017-06-13 15:35:54 -0700907 int nextId = flowObjectiveService.allocateNextId();
Saurav Das97241862018-02-14 14:14:54 -0800908 NextObjective.Type type = (simple) ? NextObjective.Type.SIMPLE
909 : NextObjective.Type.HASHED;
Saurav Das261c3002017-06-13 15:35:54 -0700910 if (neighbors == null || neighbors.isEmpty()) {
911 log.warn("createGroupsFromDestinationSet: needs at least one neighbor"
912 + "to create group in dev:{} for ds: {} with next-hops {}",
913 deviceId, ds, neighbors);
914 return;
915 }
Saurav Das261c3002017-06-13 15:35:54 -0700916
917 NextObjective.Builder nextObjBuilder = DefaultNextObjective
918 .builder()
919 .withId(nextId)
920 .withType(type)
921 .fromApp(appId);
922 if (meta != null) {
923 nextObjBuilder.withMeta(meta);
924 }
925
926 // create treatment buckets for each neighbor for each dst Device
927 // except in the special case where we only want to pick a single
Saurav Das97241862018-02-14 14:14:54 -0800928 // neighbor/port for a simple nextObj
Saurav Das261c3002017-06-13 15:35:54 -0700929 boolean foundSingleNeighbor = false;
930 boolean treatmentAdded = false;
931 Map<DeviceId, Set<DeviceId>> dstNextHops = new ConcurrentHashMap<>();
932 for (DeviceId dst : ds.getDestinationSwitches()) {
933 Set<DeviceId> nextHops = neighbors.get(dst);
934 if (nextHops == null || nextHops.isEmpty()) {
935 continue;
Pier Ventre229fd0b2016-10-31 16:49:19 -0700936 }
Saurav Das261c3002017-06-13 15:35:54 -0700937
938 if (foundSingleNeighbor) {
939 break;
940 }
941
942 for (DeviceId neighborId : nextHops) {
Saurav Das4c35fc42015-11-20 15:27:53 -0800943 if (devicePortMap.get(neighborId) == null) {
944 log.warn("Neighbor {} is not in the port map yet for dev:{}",
945 neighborId, deviceId);
sangho2165d222015-05-01 09:38:25 -0700946 return;
Jon Hall31d84782017-01-18 20:15:44 -0800947 } else if (devicePortMap.get(neighborId).isEmpty()) {
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700948 log.warn("There are no ports for "
Saurav Das4c35fc42015-11-20 15:27:53 -0800949 + "the Device {} in the port map yet", neighborId);
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700950 return;
sangho2165d222015-05-01 09:38:25 -0700951 }
952
Saurav Das4c35fc42015-11-20 15:27:53 -0800953 MacAddress neighborMac;
Charles Chan319d1a22015-11-03 10:42:14 -0800954 try {
Saurav Das4c35fc42015-11-20 15:27:53 -0800955 neighborMac = deviceConfig.getDeviceMac(neighborId);
Charles Chan319d1a22015-11-03 10:42:14 -0800956 } catch (DeviceConfigNotFoundException e) {
Saurav Das261c3002017-06-13 15:35:54 -0700957 log.warn(e.getMessage() + " Aborting createGroupsFromDestinationset.");
Charles Chan319d1a22015-11-03 10:42:14 -0800958 return;
959 }
Saurav Das261c3002017-06-13 15:35:54 -0700960 // For each port to the neighbor, we create a new treatment
Pier Ventre229fd0b2016-10-31 16:49:19 -0700961 Set<PortNumber> neighborPorts = devicePortMap.get(neighborId);
Saurav Das97241862018-02-14 14:14:54 -0800962 // In this case we need a SIMPLE nextObj. We randomly pick a port
963 if (simple) {
Pier Ventre229fd0b2016-10-31 16:49:19 -0700964 int size = devicePortMap.get(neighborId).size();
965 int index = RandomUtils.nextInt(0, size);
966 neighborPorts = Collections.singleton(
Saurav Das261c3002017-06-13 15:35:54 -0700967 Iterables.get(devicePortMap.get(neighborId),
968 index));
969 foundSingleNeighbor = true;
Pier Ventre229fd0b2016-10-31 16:49:19 -0700970 }
Andreas Pantelopoulos5e7be3d2017-10-23 12:18:25 -0700971
Pier Ventre229fd0b2016-10-31 16:49:19 -0700972 for (PortNumber sp : neighborPorts) {
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700973 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
974 .builder();
Saurav Das261c3002017-06-13 15:35:54 -0700975 tBuilder.setEthDst(neighborMac).setEthSrc(nodeMacAddr);
976 int edgeLabel = ds.getEdgeLabel(dst);
977 if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
Saurav Das97241862018-02-14 14:14:54 -0800978 if (simple) {
979 // swap label case
980 tBuilder.setMpls(MplsLabel.mplsLabel(edgeLabel));
981 } else {
982 // ecmp with label push case
983 tBuilder.pushMpls().copyTtlOut()
984 .setMpls(MplsLabel.mplsLabel(edgeLabel));
985 }
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700986 }
Charles Chan81ddf6f2018-08-12 15:19:01 -0700987
988 // Set VLAN ID for PW transport. Otherwise pop vlan
Andreas Pantelopoulosb281ae22018-05-01 14:56:05 -0700989 if ((ds.getTypeOfDstSet() == DestinationSet.DestinationSetType.SWAP_NOT_BOS) ||
990 (ds.getTypeOfDstSet() == DestinationSet.DestinationSetType.POP_NOT_BOS)) {
991 tBuilder.setVlanId(srManager.PSEUDOWIRE_VLAN);
Charles Chan81ddf6f2018-08-12 15:19:01 -0700992 } else {
993 tBuilder.popVlan();
Andreas Pantelopoulosb281ae22018-05-01 14:56:05 -0700994 }
Charles Chan81ddf6f2018-08-12 15:19:01 -0700995
Saurav Das4c35fc42015-11-20 15:27:53 -0800996 tBuilder.setOutput(sp);
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700997 nextObjBuilder.addTreatment(tBuilder.build());
Saurav Das261c3002017-06-13 15:35:54 -0700998 treatmentAdded = true;
999 //update store
1000 Set<DeviceId> existingNeighbors = dstNextHops.get(dst);
1001 if (existingNeighbors == null) {
1002 existingNeighbors = new HashSet<>();
1003 }
1004 existingNeighbors.add(neighborId);
1005 dstNextHops.put(dst, existingNeighbors);
1006 log.debug("creating treatment for port/label {}/{} in next:{}",
1007 sp, edgeLabel, nextId);
1008 }
1009
1010 if (foundSingleNeighbor) {
1011 break;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -07001012 }
1013 }
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -07001014 }
Saurav Das261c3002017-06-13 15:35:54 -07001015
1016 if (!treatmentAdded) {
1017 log.warn("Could not createGroup from DestinationSet {} without any"
1018 + "next hops {}", ds, neighbors);
1019 return;
1020 }
1021 ObjectiveContext context = new DefaultObjectiveContext(
1022 (objective) ->
1023 log.debug("createGroupsFromDestinationSet installed "
1024 + "NextObj {} on {}", nextId, deviceId),
1025 (objective, error) ->
1026 log.warn("createGroupsFromDestinationSet failed to install"
1027 + " NextObj {} on {}: {}", nextId, deviceId, error)
1028 );
1029 NextObjective nextObj = nextObjBuilder.add(context);
1030 log.debug(".. createGroupsFromDestinationSet: Submitted "
1031 + "next objective {} in device {}", nextId, deviceId);
1032 flowObjectiveService.next(deviceId, nextObj);
1033 //update store
1034 dsNextObjStore.put(new DestinationSetNextObjectiveStoreKey(deviceId, ds),
1035 new NextNeighbors(dstNextHops, nextId));
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -07001036 }
1037
Saurav Das2d94d312015-11-24 23:21:05 -08001038 /**
Saurav Dasf0f592d2016-11-18 15:21:57 -08001039 * Creates broadcast groups for all ports in the same subnet for
1040 * all configured subnets.
Saurav Das2d94d312015-11-24 23:21:05 -08001041 */
Charles Chan10b0fb72017-02-02 16:20:42 -08001042 public void createGroupsFromVlanConfig() {
Charles Chan90772a72017-02-08 15:52:08 -08001043 srManager.getVlanPortMap(deviceId).asMap().forEach((vlanId, ports) -> {
Charles Chan10b0fb72017-02-02 16:20:42 -08001044 createBcastGroupFromVlan(vlanId, ports);
Pier Ventreb6a7f342016-11-26 21:05:22 -08001045 });
Saurav Dasf0f592d2016-11-18 15:21:57 -08001046 }
Charles Chanc6ad7752015-10-29 14:58:10 -07001047
Saurav Dasf0f592d2016-11-18 15:21:57 -08001048 /**
Charles Chan10b0fb72017-02-02 16:20:42 -08001049 * Creates a single broadcast group from a given vlan id and list of ports.
Saurav Dasf0f592d2016-11-18 15:21:57 -08001050 *
Charles Chan10b0fb72017-02-02 16:20:42 -08001051 * @param vlanId vlan id
Saurav Dasf0f592d2016-11-18 15:21:57 -08001052 * @param ports list of ports in the subnet
1053 */
Charles Chan90772a72017-02-08 15:52:08 -08001054 public void createBcastGroupFromVlan(VlanId vlanId, Collection<PortNumber> ports) {
Charles Chan10b0fb72017-02-02 16:20:42 -08001055 VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
Charles Chanc6ad7752015-10-29 14:58:10 -07001056
Charles Chan10b0fb72017-02-02 16:20:42 -08001057 if (vlanNextObjStore.containsKey(key)) {
Saurav Dasf0f592d2016-11-18 15:21:57 -08001058 log.debug("Broadcast group for device {} and subnet {} exists",
Charles Chan10b0fb72017-02-02 16:20:42 -08001059 deviceId, vlanId);
Saurav Dasf0f592d2016-11-18 15:21:57 -08001060 return;
1061 }
Charles Chande6655c2015-12-23 00:15:11 -08001062
Saurav Dasf0f592d2016-11-18 15:21:57 -08001063 TrafficSelector metadata =
Charles Chan10b0fb72017-02-02 16:20:42 -08001064 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
Charles Chan77277672015-10-20 16:24:19 -07001065
Saurav Dasf0f592d2016-11-18 15:21:57 -08001066 int nextId = flowObjectiveService.allocateNextId();
Charles Chan77277672015-10-20 16:24:19 -07001067
Saurav Dasf0f592d2016-11-18 15:21:57 -08001068 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1069 .builder().withId(nextId)
1070 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1071 .withMeta(metadata);
Charles Chan77277672015-10-20 16:24:19 -07001072
Saurav Dasf0f592d2016-11-18 15:21:57 -08001073 ports.forEach(port -> {
1074 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Charles Chan90772a72017-02-08 15:52:08 -08001075 if (toPopVlan(port, vlanId)) {
1076 tBuilder.popVlan();
1077 }
Saurav Dasf0f592d2016-11-18 15:21:57 -08001078 tBuilder.setOutput(port);
1079 nextObjBuilder.addTreatment(tBuilder.build());
Charles Chan77277672015-10-20 16:24:19 -07001080 });
Saurav Dasf0f592d2016-11-18 15:21:57 -08001081
Saurav Das2cb38292017-03-29 19:09:17 -07001082 ObjectiveContext context = new DefaultObjectiveContext(
1083 (objective) ->
1084 log.debug("createBroadcastGroupFromVlan installed "
1085 + "NextObj {} on {}", nextId, deviceId),
1086 (objective, error) ->
1087 log.warn("createBroadcastGroupFromVlan failed to install"
1088 + " NextObj {} on {}: {}", nextId, deviceId, error)
1089 );
1090 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Dasf0f592d2016-11-18 15:21:57 -08001091 flowObjectiveService.next(deviceId, nextObj);
Saurav Das2b6a00f2017-12-05 15:00:23 -08001092 log.debug("createBcastGroupFromVlan: Submitted next objective {} "
1093 + "for vlan: {} in device {}", nextId, vlanId, deviceId);
Saurav Dasf0f592d2016-11-18 15:21:57 -08001094
Charles Chan10b0fb72017-02-02 16:20:42 -08001095 vlanNextObjStore.put(key, nextId);
Charles Chan77277672015-10-20 16:24:19 -07001096 }
1097
Charles Chanb7f75ac2016-01-11 18:28:54 -08001098 /**
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001099 * Removes a single broadcast group from a given vlan id.
1100 * The group should be empty.
1101 * @param deviceId device Id to remove the group
1102 * @param portNum port number related to the group
1103 * @param vlanId vlan id of the broadcast group to remove
1104 * @param popVlan true if the TrafficTreatment involves pop vlan tag action
1105 */
1106 public void removeBcastGroupFromVlan(DeviceId deviceId, PortNumber portNum,
1107 VlanId vlanId, boolean popVlan) {
1108 VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
1109
1110 if (!vlanNextObjStore.containsKey(key)) {
1111 log.debug("Broadcast group for device {} and subnet {} does not exist",
1112 deviceId, vlanId);
1113 return;
1114 }
1115
1116 TrafficSelector metadata =
1117 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
1118
1119 int nextId = vlanNextObjStore.get(key);
1120
1121 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1122 .builder().withId(nextId)
1123 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1124 .withMeta(metadata);
1125
1126 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1127 if (popVlan) {
1128 tBuilder.popVlan();
1129 }
1130 tBuilder.setOutput(portNum);
1131 nextObjBuilder.addTreatment(tBuilder.build());
1132
1133 ObjectiveContext context = new DefaultObjectiveContext(
1134 (objective) ->
1135 log.debug("removeBroadcastGroupFromVlan removed "
1136 + "NextObj {} on {}", nextId, deviceId),
1137 (objective, error) ->
1138 log.warn("removeBroadcastGroupFromVlan failed to remove "
1139 + " NextObj {} on {}: {}", nextId, deviceId, error)
1140 );
1141 NextObjective nextObj = nextObjBuilder.remove(context);
1142 flowObjectiveService.next(deviceId, nextObj);
1143 log.debug("removeBcastGroupFromVlan: Submited next objective {} in device {}",
1144 nextId, deviceId);
1145
1146 vlanNextObjStore.remove(key, nextId);
1147 }
1148
1149 /**
Charles Chan90772a72017-02-08 15:52:08 -08001150 * Determine if we should pop given vlan before sending packets to the given port.
1151 *
1152 * @param portNumber port number
1153 * @param vlanId vlan id
1154 * @return true if the vlan id is not contained in any vlanTagged config
1155 */
1156 private boolean toPopVlan(PortNumber portNumber, VlanId vlanId) {
Saurav Das261c3002017-06-13 15:35:54 -07001157 return srManager.interfaceService
1158 .getInterfacesByPort(new ConnectPoint(deviceId, portNumber))
Charles Chan90772a72017-02-08 15:52:08 -08001159 .stream().noneMatch(intf -> intf.vlanTagged().contains(vlanId));
1160 }
1161
1162 /**
Saurav Das2d94d312015-11-24 23:21:05 -08001163 * Create simple next objective for a single port. The treatments can include
1164 * all outgoing actions that need to happen on the packet.
1165 *
1166 * @param portNum the outgoing port on the device
1167 * @param treatment the actions to apply on the packets (should include outport)
1168 * @param meta optional data to pass to the driver
1169 */
1170 public void createGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
1171 TrafficSelector meta) {
1172 int nextId = flowObjectiveService.allocateNextId();
1173 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
Saurav Das368cf212017-03-15 15:15:14 -07001174 deviceId, portNum, treatment, meta);
Saurav Das2d94d312015-11-24 23:21:05 -08001175
1176 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1177 .builder().withId(nextId)
1178 .withType(NextObjective.Type.SIMPLE)
1179 .addTreatment(treatment)
1180 .fromApp(appId)
1181 .withMeta(meta);
1182
Saurav Das2cb38292017-03-29 19:09:17 -07001183 ObjectiveContext context = new DefaultObjectiveContext(
1184 (objective) ->
1185 log.debug("createGroupFromPort installed "
1186 + "NextObj {} on {}", nextId, deviceId),
1187 (objective, error) ->
1188 log.warn("createGroupFromPort failed to install"
1189 + " NextObj {} on {}: {}", nextId, deviceId, error)
1190 );
1191 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Das2d94d312015-11-24 23:21:05 -08001192 flowObjectiveService.next(deviceId, nextObj);
1193 log.debug("createGroupFromPort: Submited next objective {} in device {} "
1194 + "for port {}", nextId, deviceId, portNum);
1195
1196 portNextObjStore.put(key, nextId);
1197 }
1198
sangho27462c62015-05-14 00:39:53 -07001199 /**
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001200 * Removes simple next objective for a single port.
1201 *
1202 * @param deviceId device id that has the port to deal with
1203 * @param portNum the outgoing port on the device
1204 * @param vlanId vlan id associated with the port
1205 * @param popVlan true if POP_VLAN action is applied on the packets, false otherwise
1206 */
1207 public void removePortNextObjective(DeviceId deviceId, PortNumber portNum, VlanId vlanId, boolean popVlan) {
1208 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
1209 mbuilder.matchVlanId(vlanId);
1210
1211 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
1212 tbuilder.immediate().setOutput(portNum);
1213 if (popVlan) {
1214 tbuilder.immediate().popVlan();
1215 }
1216
1217 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, portNum,
1218 tbuilder.build(), mbuilder.build(), false);
1219
1220 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
1221 deviceId, portNum, tbuilder.build(), mbuilder.build());
1222 if (portNextObjId != -1 && portNextObjStore.containsKey(key)) {
1223 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1224 .builder().withId(portNextObjId)
1225 .withType(NextObjective.Type.SIMPLE).fromApp(appId);
1226 ObjectiveContext context = new DefaultObjectiveContext(
1227 (objective) -> log.debug("removePortNextObjective removes NextObj {} on {}",
1228 portNextObjId, deviceId),
1229 (objective, error) ->
1230 log.warn("removePortNextObjective failed to remove NextObj {} on {}: {}",
1231 portNextObjId, deviceId, error));
1232 NextObjective nextObjective = nextObjBuilder.remove(context);
1233 log.info("**removePortNextObjective: Submitted "
1234 + "next objective {} in device {}",
1235 portNextObjId, deviceId);
1236 flowObjectiveService.next(deviceId, nextObjective);
1237
1238 portNextObjStore.remove(key);
1239 }
1240 }
1241 /**
sangho27462c62015-05-14 00:39:53 -07001242 * Removes groups for the next objective ID given.
1243 *
1244 * @param objectiveId next objective ID to remove
1245 * @return true if succeeds, false otherwise
1246 */
1247 public boolean removeGroup(int objectiveId) {
Saurav Das261c3002017-06-13 15:35:54 -07001248 for (Map.Entry<DestinationSetNextObjectiveStoreKey, NextNeighbors> e :
1249 dsNextObjStore.entrySet()) {
1250 if (e.getValue().nextId() != objectiveId) {
1251 continue;
1252 }
Pier Luigiecb09f42018-01-14 21:56:11 +01001253 // Right now it is just used in TunnelHandler
1254 // remember in future that PW transit groups could
1255 // be Indirect groups
sangho27462c62015-05-14 00:39:53 -07001256 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1257 .builder().withId(objectiveId)
1258 .withType(NextObjective.Type.HASHED).fromApp(appId);
Charles Chana4ee4f92016-04-23 14:48:16 -07001259 ObjectiveContext context = new DefaultObjectiveContext(
1260 (objective) -> log.debug("RemoveGroup removes NextObj {} on {}",
1261 objectiveId, deviceId),
1262 (objective, error) ->
1263 log.warn("RemoveGroup failed to remove NextObj {} on {}: {}",
1264 objectiveId, deviceId, error));
1265 NextObjective nextObjective = nextObjBuilder.remove(context);
Saurav Das4c35fc42015-11-20 15:27:53 -08001266 log.info("**removeGroup: Submited "
1267 + "next objective {} in device {}",
1268 objectiveId, deviceId);
sangho27462c62015-05-14 00:39:53 -07001269 flowObjectiveService.next(deviceId, nextObjective);
1270
Saurav Das261c3002017-06-13 15:35:54 -07001271 dsNextObjStore.remove(e.getKey());
sangho4a5c42a2015-05-20 22:16:38 -07001272 return true;
sangho27462c62015-05-14 00:39:53 -07001273 }
1274
1275 return false;
1276 }
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001277 /**
1278 * Remove simple next objective for a single port. The treatments can include
1279 * all outgoing actions that need to happen on the packet.
1280 *
1281 * @param portNum the outgoing port on the device
1282 * @param treatment the actions applied on the packets (should include outport)
1283 * @param meta optional data to pass to the driver
1284 */
1285 public void removeGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
1286 TrafficSelector meta) {
1287 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
1288 deviceId, portNum, treatment, meta);
1289 Integer nextId = portNextObjStore.get(key);
1290
1291 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1292 .builder().withId(nextId)
1293 .withType(NextObjective.Type.SIMPLE)
1294 .addTreatment(treatment)
1295 .fromApp(appId)
1296 .withMeta(meta);
1297
1298 ObjectiveContext context = new DefaultObjectiveContext(
1299 (objective) ->
1300 log.info("removeGroupFromPort installed "
1301 + "NextObj {} on {}", nextId, deviceId),
1302 (objective, error) ->
1303 log.warn("removeGroupFromPort failed to install"
1304 + " NextObj {} on {}: {}", nextId, deviceId, error)
1305 );
1306 NextObjective nextObj = nextObjBuilder.remove(context);
1307 flowObjectiveService.next(deviceId, nextObj);
1308 log.info("removeGroupFromPort: Submitted next objective {} in device {} "
1309 + "for port {}", nextId, deviceId, portNum);
1310
1311 portNextObjStore.remove(key);
1312 }
Srikanth Vavilapalli8c83f1d2015-05-22 13:47:31 -07001313
Charles Chanb7f75ac2016-01-11 18:28:54 -08001314 /**
1315 * Removes all groups from all next objective stores.
1316 */
Saurav Das261c3002017-06-13 15:35:54 -07001317 /*public void removeAllGroups() {
1318 for (Map.Entry<NeighborSetNextObjectiveStoreKey, NextNeighbors> entry:
Saurav Das62af8802015-12-04 10:52:59 -08001319 nsNextObjStore.entrySet()) {
Saurav Das261c3002017-06-13 15:35:54 -07001320 removeGroup(entry.getValue().nextId());
Saurav Das62af8802015-12-04 10:52:59 -08001321 }
1322 for (Map.Entry<PortNextObjectiveStoreKey, Integer> entry:
1323 portNextObjStore.entrySet()) {
1324 removeGroup(entry.getValue());
1325 }
Charles Chan10b0fb72017-02-02 16:20:42 -08001326 for (Map.Entry<VlanNextObjectiveStoreKey, Integer> entry:
1327 vlanNextObjStore.entrySet()) {
Saurav Das62af8802015-12-04 10:52:59 -08001328 removeGroup(entry.getValue());
1329 }
Saurav Das261c3002017-06-13 15:35:54 -07001330 }*/ //XXX revisit
1331
Saurav Dasfbe74572017-08-03 18:30:35 -07001332 /**
1333 * Triggers a one time bucket verification operation on all hash groups
1334 * on this device.
1335 */
1336 public void triggerBucketCorrector() {
1337 BucketCorrector bc = new BucketCorrector();
1338 bc.run();
1339 }
1340
Jonghwan Hyuna4ce0aa2018-02-12 16:43:45 +09001341 /**
1342 * Modifies L2IG bucket when the interface configuration is updated, especially
1343 * when the interface has same VLAN ID but the VLAN type is changed (e.g., from
1344 * vlan-tagged [10] to vlan-untagged 10), which requires changes on
1345 * TrafficTreatment in turn.
1346 *
1347 * @param portNumber the port on this device that needs to be updated
1348 * @param vlanId the vlan id corresponding to this port
1349 * @param pushVlan indicates if packets should be sent out untagged or not out
1350 * from the port. If true, updated TrafficTreatment involves
1351 * pop vlan tag action. If false, updated TrafficTreatment
1352 * does not involve pop vlan tag action.
1353 */
1354 public void updateL2InterfaceGroupBucket(PortNumber portNumber, VlanId vlanId, boolean pushVlan) {
1355 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1356 if (pushVlan) {
1357 tBuilder.popVlan();
1358 }
1359 tBuilder.setOutput(portNumber);
1360
1361 TrafficSelector metadata =
1362 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
1363
1364 int nextId = getVlanNextObjectiveId(vlanId);
1365
1366 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1367 .builder().withId(nextId)
1368 .withType(NextObjective.Type.SIMPLE).fromApp(appId)
1369 .addTreatment(tBuilder.build())
1370 .withMeta(metadata);
1371
1372 ObjectiveContext context = new DefaultObjectiveContext(
1373 (objective) -> log.debug("port {} successfully updated NextObj {} on {}",
1374 portNumber, nextId, deviceId),
1375 (objective, error) ->
1376 log.warn("port {} failed to updated NextObj {} on {}: {}",
1377 portNumber, nextId, deviceId, error));
1378
1379 flowObjectiveService.next(deviceId, nextObjBuilder.modify(context));
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001380 }
1381
Jonghwan Hyuna4ce0aa2018-02-12 16:43:45 +09001382 /**
1383 * Adds a single port to the L2FG or removes it from the L2FG.
1384 *
1385 * @param vlanId the vlan id corresponding to this port
1386 * @param portNum the port on this device to be updated
1387 * @param nextId the next objective ID for the given vlan id
1388 * @param install if true, adds the port to L2FG. If false, removes it from L2FG.
1389 */
1390 public void updateGroupFromVlanConfiguration(VlanId vlanId, PortNumber portNum, int nextId, boolean install) {
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001391 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1392 if (toPopVlan(portNum, vlanId)) {
1393 tBuilder.popVlan();
1394 }
1395 tBuilder.setOutput(portNum);
1396
1397 TrafficSelector metadata =
1398 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
1399
1400 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1401 .builder().withId(nextId)
1402 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1403 .addTreatment(tBuilder.build())
1404 .withMeta(metadata);
1405
1406 ObjectiveContext context = new DefaultObjectiveContext(
1407 (objective) -> log.debug("port {} successfully removedFrom NextObj {} on {}",
1408 portNum, nextId, deviceId),
1409 (objective, error) ->
1410 log.warn("port {} failed to removedFrom NextObj {} on {}: {}",
1411 portNum, nextId, deviceId, error));
1412
1413 if (install) {
1414 flowObjectiveService.next(deviceId, nextObjBuilder.addToExisting(context));
1415 } else {
1416 flowObjectiveService.next(deviceId, nextObjBuilder.removeFromExisting(context));
1417 }
1418 }
Saurav Das8a3022d2017-05-05 17:01:08 -07001419
1420 /**
Saurav Dasfe0b05e2017-08-14 16:44:43 -07001421 * Performs bucket verification operation for all hash groups in this device.
1422 * Checks RouteHandler to ensure that routing is stable before attempting
1423 * verification. Verification involves creating a nextObjective with
1424 * operation VERIFY for existing next objectives in the store, and passing
1425 * it to the driver. It is the driver that actually performs the verification
1426 * by adding or removing buckets to match the verification next objective
1427 * created here.
Saurav Das8a3022d2017-05-05 17:01:08 -07001428 */
Saurav Dasfbe74572017-08-03 18:30:35 -07001429 protected final class BucketCorrector implements Runnable {
1430 Integer nextId;
Saurav Das8a3022d2017-05-05 17:01:08 -07001431
Saurav Dasfbe74572017-08-03 18:30:35 -07001432 BucketCorrector() {
1433 this.nextId = null;
1434 }
1435
1436 BucketCorrector(Integer nextId) {
1437 this.nextId = nextId;
Saurav Das8a3022d2017-05-05 17:01:08 -07001438 }
1439
1440 @Override
1441 public void run() {
Saurav Dasfbe74572017-08-03 18:30:35 -07001442 if (!srManager.mastershipService.isLocalMaster(deviceId)) {
1443 return;
Saurav Das8a3022d2017-05-05 17:01:08 -07001444 }
Saurav Dasfbe74572017-08-03 18:30:35 -07001445 DefaultRoutingHandler rh = srManager.getRoutingHandler();
1446 if (rh == null) {
1447 return;
1448 }
1449 if (!rh.isRoutingStable()) {
1450 return;
1451 }
1452 rh.acquireRoutingLock();
1453 try {
Saurav Dasfe0b05e2017-08-14 16:44:43 -07001454 log.trace("running bucket corrector for dev: {}", deviceId);
Saurav Dasfbe74572017-08-03 18:30:35 -07001455 Set<DestinationSetNextObjectiveStoreKey> dsKeySet = dsNextObjStore.entrySet()
1456 .stream()
1457 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
Pier Luigiecb09f42018-01-14 21:56:11 +01001458 // Filter out PW transit groups or include them if MPLS ECMP is supported
Saurav Das97241862018-02-14 14:14:54 -08001459 .filter(entry -> !entry.getKey().destinationSet().notBos() ||
1460 (entry.getKey().destinationSet().notBos() && srManager.getMplsEcmp()))
1461 // Filter out simple SWAP groups or include them if MPLS ECMP is supported
1462 .filter(entry -> !entry.getKey().destinationSet().swap() ||
1463 (entry.getKey().destinationSet().swap() && srManager.getMplsEcmp()))
Saurav Dasfbe74572017-08-03 18:30:35 -07001464 .map(entry -> entry.getKey())
1465 .collect(Collectors.toSet());
1466 for (DestinationSetNextObjectiveStoreKey dsKey : dsKeySet) {
1467 NextNeighbors next = dsNextObjStore.get(dsKey);
1468 if (next == null) {
1469 continue;
1470 }
1471 int nid = next.nextId();
1472 if (nextId != null && nextId != nid) {
1473 continue;
1474 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -07001475 log.trace("bkt-corr: dsNextObjStore for device {}: {}",
Saurav Dasfbe74572017-08-03 18:30:35 -07001476 deviceId, dsKey, next);
1477 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
1478 metabuilder.matchVlanId(INTERNAL_VLAN);
1479 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
1480 .withId(nid)
1481 .withType(NextObjective.Type.HASHED)
1482 .withMeta(metabuilder.build())
1483 .fromApp(appId);
1484
1485 next.dstNextHops().forEach((dstDev, nextHops) -> {
1486 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dstDev);
1487 nextHops.forEach(neighbor -> {
1488 MacAddress neighborMac;
1489 try {
1490 neighborMac = deviceConfig.getDeviceMac(neighbor);
1491 } catch (DeviceConfigNotFoundException e) {
1492 log.warn(e.getMessage() + " Aborting neighbor"
1493 + neighbor);
1494 return;
1495 }
1496 devicePortMap.get(neighbor).forEach(port -> {
Saurav Dasfe0b05e2017-08-14 16:44:43 -07001497 log.trace("verify in device {} nextId {}: bucket with"
Saurav Dasfbe74572017-08-03 18:30:35 -07001498 + " port/label {}/{} to dst {} via {}",
1499 deviceId, nid, port, edgeLabel,
1500 dstDev, neighbor);
Saurav Das97241862018-02-14 14:14:54 -08001501 nextObjBuilder
1502 .addTreatment(treatmentBuilder(port,
1503 neighborMac,
1504 dsKey.destinationSet().swap(),
1505 edgeLabel));
Saurav Dasfbe74572017-08-03 18:30:35 -07001506 });
1507 });
1508 });
1509
1510 NextObjective nextObjective = nextObjBuilder.verify();
1511 flowObjectiveService.next(deviceId, nextObjective);
1512 }
1513 } finally {
1514 rh.releaseRoutingLock();
1515 }
1516
1517 }
1518
1519 TrafficTreatment treatmentBuilder(PortNumber outport, MacAddress dstMac,
Saurav Das97241862018-02-14 14:14:54 -08001520 boolean swap, int edgeLabel) {
Saurav Dasfbe74572017-08-03 18:30:35 -07001521 TrafficTreatment.Builder tBuilder =
1522 DefaultTrafficTreatment.builder();
1523 tBuilder.setOutput(outport)
1524 .setEthDst(dstMac)
1525 .setEthSrc(nodeMacAddr);
1526 if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
Saurav Das97241862018-02-14 14:14:54 -08001527 if (swap) {
1528 // swap label case
1529 tBuilder.setMpls(MplsLabel.mplsLabel(edgeLabel));
1530 } else {
1531 // ecmp with label push case
1532 tBuilder.pushMpls()
1533 .copyTtlOut()
1534 .setMpls(MplsLabel.mplsLabel(edgeLabel));
1535 }
Saurav Dasfbe74572017-08-03 18:30:35 -07001536 }
1537 return tBuilder.build();
Saurav Das8a3022d2017-05-05 17:01:08 -07001538 }
1539 }
1540
Pier Luigiecb09f42018-01-14 21:56:11 +01001541}