blob: 58599baa3c29335d09db5c4eec252c0d7db041f3 [file] [log] [blame]
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Srikanth Vavilapalli4db76e32015-04-07 15:12:32 -070016package org.onosproject.segmentrouting.grouphandler;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080017
Saurav Das9df5b7c2017-08-14 16:44:43 -070018import com.google.common.collect.ImmutableMap;
Pier Ventre917127a2016-10-31 16:49:19 -070019import com.google.common.collect.Iterables;
Saurav Das9df5b7c2017-08-14 16:44:43 -070020import com.google.common.collect.Lists;
Saurav Dasc88d4662017-05-15 15:34:25 -070021import com.google.common.collect.Sets;
22
Pier Ventre917127a2016-10-31 16:49:19 -070023import org.apache.commons.lang3.RandomUtils;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080024import org.onlab.packet.MacAddress;
sangho32a59322015-02-17 12:07:41 -080025import org.onlab.packet.MplsLabel;
Saurav Das423fe2b2015-12-04 10:52:59 -080026import org.onlab.packet.VlanId;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070027import org.onlab.util.KryoNamespace;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080028import org.onosproject.core.ApplicationId;
Charles Chan59cc16d2017-02-02 16:20:42 -080029import org.onosproject.net.ConnectPoint;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080030import org.onosproject.net.DeviceId;
31import org.onosproject.net.Link;
32import org.onosproject.net.PortNumber;
Saurav Das423fe2b2015-12-04 10:52:59 -080033import org.onosproject.net.flow.DefaultTrafficSelector;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080034import org.onosproject.net.flow.DefaultTrafficTreatment;
Saurav Das8a0732e2015-11-20 15:27:53 -080035import org.onosproject.net.flow.TrafficSelector;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080036import org.onosproject.net.flow.TrafficTreatment;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070037import org.onosproject.net.flowobjective.DefaultNextObjective;
Charles Chan216e3c82016-04-23 14:48:16 -070038import org.onosproject.net.flowobjective.DefaultObjectiveContext;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070039import org.onosproject.net.flowobjective.FlowObjectiveService;
40import org.onosproject.net.flowobjective.NextObjective;
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -070041import org.onosproject.net.flowobjective.ObjectiveContext;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080042import org.onosproject.net.link.LinkService;
Saurav Dasceccf242017-08-03 18:30:35 -070043import org.onosproject.segmentrouting.DefaultRoutingHandler;
Saurav Das423fe2b2015-12-04 10:52:59 -080044import org.onosproject.segmentrouting.SegmentRoutingManager;
Charles Chan0b4e6182015-11-03 10:42:14 -080045import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
46import org.onosproject.segmentrouting.config.DeviceProperties;
Ruchi Sahota5d800282019-01-28 01:08:18 +000047import org.onosproject.segmentrouting.config.DeviceConfiguration;
Saurav Das7bcbe702017-06-13 15:35:54 -070048import org.onosproject.segmentrouting.storekey.DestinationSetNextObjectiveStoreKey;
Charles Chand2990362016-04-18 13:44:03 -070049import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
Charles Chan59cc16d2017-02-02 16:20:42 -080050import org.onosproject.segmentrouting.storekey.VlanNextObjectiveStoreKey;
Ruchi Sahota5d800282019-01-28 01:08:18 +000051import org.onosproject.segmentrouting.storekey.MacVlanNextObjectiveStoreKey;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070052import org.onosproject.store.service.EventuallyConsistentMap;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080053import org.slf4j.Logger;
54
Pier Ventre917127a2016-10-31 16:49:19 -070055import java.net.URI;
Charles Chan7ffd81f2017-02-08 15:52:08 -080056import java.util.Collection;
Pier Ventre917127a2016-10-31 16:49:19 -070057import java.util.Collections;
Saurav Dasc88d4662017-05-15 15:34:25 -070058import java.util.HashMap;
Pier Ventre917127a2016-10-31 16:49:19 -070059import java.util.HashSet;
60import java.util.List;
61import java.util.Map;
62import java.util.Set;
63import java.util.concurrent.ConcurrentHashMap;
Saurav Das1547b3f2017-05-05 17:01:08 -070064import java.util.concurrent.ScheduledExecutorService;
65import java.util.concurrent.TimeUnit;
Pier Ventre917127a2016-10-31 16:49:19 -070066import java.util.stream.Collectors;
67
68import static com.google.common.base.Preconditions.checkNotNull;
Saurav Das1547b3f2017-05-05 17:01:08 -070069import static java.util.concurrent.Executors.newScheduledThreadPool;
70import static org.onlab.util.Tools.groupedThreads;
Pier Ventre917127a2016-10-31 16:49:19 -070071import static org.slf4j.LoggerFactory.getLogger;
72
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080073/**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070074 * Default ECMP group handler creation module. This component creates a set of
75 * ECMP groups for every neighbor that this device is connected to based on
76 * whether the current device is an edge device or a transit device.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080077 */
78public class DefaultGroupHandler {
Ray Milkey9c9cde42018-01-12 14:22:06 -080079 private static final Logger log = getLogger(DefaultGroupHandler.class);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080080
Saurav Dasceccf242017-08-03 18:30:35 -070081 private static final long VERIFY_INTERVAL = 30; // secs
82
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080083 protected final DeviceId deviceId;
84 protected final ApplicationId appId;
85 protected final DeviceProperties deviceConfig;
86 protected final List<Integer> allSegmentIds;
Pier Ventree0ae7a32016-11-23 09:57:42 -080087 protected int ipv4NodeSegmentId = -1;
88 protected int ipv6NodeSegmentId = -1;
Charles Chan0b4e6182015-11-03 10:42:14 -080089 protected boolean isEdgeRouter = false;
90 protected MacAddress nodeMacAddr = null;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080091 protected LinkService linkService;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070092 protected FlowObjectiveService flowObjectiveService;
Ruchi Sahota5d800282019-01-28 01:08:18 +000093 private DeviceConfiguration config;
94
Saurav Dasc88d4662017-05-15 15:34:25 -070095 /**
96 * local store for neighbor-device-ids and the set of ports on this device
97 * that connect to the same neighbor.
98 */
Saurav Das8a0732e2015-11-20 15:27:53 -080099 protected ConcurrentHashMap<DeviceId, Set<PortNumber>> devicePortMap =
100 new ConcurrentHashMap<>();
Saurav Dasc88d4662017-05-15 15:34:25 -0700101 /**
102 * local store for ports on this device connected to neighbor-device-id.
103 */
Saurav Das8a0732e2015-11-20 15:27:53 -0800104 protected ConcurrentHashMap<PortNumber, DeviceId> portDeviceMap =
105 new ConcurrentHashMap<>();
Saurav Dasc88d4662017-05-15 15:34:25 -0700106
Saurav Das7bcbe702017-06-13 15:35:54 -0700107 // distributed store for (device+destination-set) mapped to next-id and neighbors
108 protected EventuallyConsistentMap<DestinationSetNextObjectiveStoreKey, NextNeighbors>
109 dsNextObjStore = null;
Saurav Das1a129a02016-11-18 15:21:57 -0800110 // distributed store for (device+subnet-ip-prefix) mapped to next-id
Charles Chan59cc16d2017-02-02 16:20:42 -0800111 protected EventuallyConsistentMap<VlanNextObjectiveStoreKey, Integer>
112 vlanNextObjStore = null;
Ruchi Sahota5d800282019-01-28 01:08:18 +0000113 // distributed store for (device+mac+vlan+treatment) mapped to next-id
114 protected EventuallyConsistentMap<MacVlanNextObjectiveStoreKey, Integer>
115 macVlanNextObjStore = null;
Saurav Das1a129a02016-11-18 15:21:57 -0800116 // distributed store for (device+port+treatment) mapped to next-id
Charles Chane849c192016-01-11 18:28:54 -0800117 protected EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
118 portNextObjStore = null;
Charles Chan188ebf52015-12-23 00:15:11 -0800119 private SegmentRoutingManager srManager;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800120
Saurav Das1547b3f2017-05-05 17:01:08 -0700121 private ScheduledExecutorService executorService
Saurav Dasceccf242017-08-03 18:30:35 -0700122 = newScheduledThreadPool(1, groupedThreads("bktCorrector", "bktC-%d", log));
Saurav Das1547b3f2017-05-05 17:01:08 -0700123
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700124 protected KryoNamespace.Builder kryo = new KryoNamespace.Builder()
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700125 .register(URI.class).register(HashSet.class)
Saurav Das7bcbe702017-06-13 15:35:54 -0700126 .register(DeviceId.class).register(PortNumber.class)
127 .register(DestinationSet.class).register(PolicyGroupIdentifier.class)
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700128 .register(PolicyGroupParams.class)
129 .register(GroupBucketIdentifier.class)
130 .register(GroupBucketIdentifier.BucketOutputType.class);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800131
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700132 protected DefaultGroupHandler(DeviceId deviceId, ApplicationId appId,
133 DeviceProperties config,
134 LinkService linkService,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700135 FlowObjectiveService flowObjService,
Charles Chan188ebf52015-12-23 00:15:11 -0800136 SegmentRoutingManager srManager) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800137 this.deviceId = checkNotNull(deviceId);
138 this.appId = checkNotNull(appId);
139 this.deviceConfig = checkNotNull(config);
140 this.linkService = checkNotNull(linkService);
Charles Chan0b4e6182015-11-03 10:42:14 -0800141 this.allSegmentIds = checkNotNull(config.getAllDeviceSegmentIds());
142 try {
Pier Ventree0ae7a32016-11-23 09:57:42 -0800143 this.ipv4NodeSegmentId = config.getIPv4SegmentId(deviceId);
144 this.ipv6NodeSegmentId = config.getIPv6SegmentId(deviceId);
Charles Chan0b4e6182015-11-03 10:42:14 -0800145 this.isEdgeRouter = config.isEdgeDevice(deviceId);
146 this.nodeMacAddr = checkNotNull(config.getDeviceMac(deviceId));
147 } catch (DeviceConfigNotFoundException e) {
148 log.warn(e.getMessage()
149 + " Skipping value assignment in DefaultGroupHandler");
150 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700151 this.flowObjectiveService = flowObjService;
Saurav Das7bcbe702017-06-13 15:35:54 -0700152 this.dsNextObjStore = srManager.dsNextObjStore();
Ray Milkeye4afdb52017-04-05 09:42:04 -0700153 this.vlanNextObjStore = srManager.vlanNextObjStore();
154 this.portNextObjStore = srManager.portNextObjStore();
Ruchi Sahota5d800282019-01-28 01:08:18 +0000155 this.macVlanNextObjStore = srManager.macVlanNextObjStore();
Charles Chan188ebf52015-12-23 00:15:11 -0800156 this.srManager = srManager;
Saurav Dasceccf242017-08-03 18:30:35 -0700157 executorService.scheduleWithFixedDelay(new BucketCorrector(), 10,
158 VERIFY_INTERVAL,
159 TimeUnit.SECONDS);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800160 populateNeighborMaps();
161 }
162
163 /**
Saurav Dasceccf242017-08-03 18:30:35 -0700164 * Gracefully shuts down a groupHandler. Typically called when the handler is
165 * no longer needed.
166 */
167 public void shutdown() {
168 executorService.shutdown();
169 }
170
171 /**
Saurav Dasc88d4662017-05-15 15:34:25 -0700172 * Creates a group handler object.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800173 *
174 * @param deviceId device identifier
175 * @param appId application identifier
176 * @param config interface to retrieve the device properties
177 * @param linkService link service object
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700178 * @param flowObjService flow objective service object
Charles Chane849c192016-01-11 18:28:54 -0800179 * @param srManager segment routing manager
Charles Chan0b4e6182015-11-03 10:42:14 -0800180 * @throws DeviceConfigNotFoundException if the device configuration is not found
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800181 * @return default group handler type
182 */
Saurav Das4ce45962015-11-24 23:21:05 -0800183 public static DefaultGroupHandler createGroupHandler(
Saurav Dasceccf242017-08-03 18:30:35 -0700184 DeviceId deviceId,
185 ApplicationId appId,
186 DeviceProperties config,
187 LinkService linkService,
188 FlowObjectiveService flowObjService,
189 SegmentRoutingManager srManager)
190 throws DeviceConfigNotFoundException {
Saurav Dasc88d4662017-05-15 15:34:25 -0700191 return new DefaultGroupHandler(deviceId, appId, config,
192 linkService,
193 flowObjService,
194 srManager);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800195 }
196
197 /**
Saurav Dasa4020382018-02-14 14:14:54 -0800198 * Updates local stores for link-src-device/port to neighbor (link-dst) for
199 * link that has come up.
Saurav Dasc88d4662017-05-15 15:34:25 -0700200 *
201 * @param link the infrastructure link
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800202 */
Saurav Dasc88d4662017-05-15 15:34:25 -0700203 public void portUpForLink(Link link) {
Saurav Dasceccf242017-08-03 18:30:35 -0700204 if (!link.src().deviceId().equals(deviceId)) {
205 log.warn("linkUp: deviceId{} doesn't match with link src {}",
206 deviceId, link.src().deviceId());
207 return;
208 }
Saurav Dasc88d4662017-05-15 15:34:25 -0700209
Saurav Dasceccf242017-08-03 18:30:35 -0700210 log.info("* portUpForLink: Device {} linkUp at local port {} to "
211 + "neighbor {}", deviceId, link.src().port(), link.dst().deviceId());
212 // ensure local state is updated even if linkup is aborted later on
213 addNeighborAtPort(link.dst().deviceId(),
214 link.src().port());
215 }
Saurav Dasc88d4662017-05-15 15:34:25 -0700216
Saurav Dasceccf242017-08-03 18:30:35 -0700217 /**
Saurav Dasa4020382018-02-14 14:14:54 -0800218 * Updates local stores for link-src-device/port to neighbor (link-dst) for
219 * link that has gone down.
Saurav Dasceccf242017-08-03 18:30:35 -0700220 *
Saurav Dasa4020382018-02-14 14:14:54 -0800221 * @param link the infrastructure link
Saurav Dasceccf242017-08-03 18:30:35 -0700222 */
Saurav Dasa4020382018-02-14 14:14:54 -0800223 public void portDownForLink(Link link) {
224 PortNumber port = link.src().port();
Saurav Dasceccf242017-08-03 18:30:35 -0700225 if (portDeviceMap.get(port) == null) {
226 log.warn("portDown: unknown port");
227 return;
228 }
Saurav Dasc88d4662017-05-15 15:34:25 -0700229
Saurav Dasceccf242017-08-03 18:30:35 -0700230 log.debug("Device {} portDown {} to neighbor {}", deviceId, port,
231 portDeviceMap.get(port));
232 devicePortMap.get(portDeviceMap.get(port)).remove(port);
233 portDeviceMap.remove(port);
234 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800235
236 /**
Saurav Dasa4020382018-02-14 14:14:54 -0800237 * Cleans up local stores for removed neighbor device.
238 *
239 * @param neighborId the device identifier for the neighbor device
240 */
241 public void cleanUpForNeighborDown(DeviceId neighborId) {
242 Set<PortNumber> ports = devicePortMap.remove(neighborId);
243 if (ports != null) {
244 ports.forEach(p -> portDeviceMap.remove(p));
245 }
246 }
247
248 /**
Saurav Dasc88d4662017-05-15 15:34:25 -0700249 * Checks all groups in the src-device of link for neighbor sets that include
250 * the dst-device of link, and edits the hash groups according to link up
251 * or down. Should only be called by the master instance of the src-switch
252 * of link. Typically used when there are no route-path changes due to the
253 * link up or down, as the ECMPspg does not change.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800254 *
Saurav Dasc88d4662017-05-15 15:34:25 -0700255 * @param link the infrastructure link that has gone down or come up
256 * @param linkDown true if link has gone down
257 * @param firstTime true if link has come up for the first time i.e a link
258 * not seen-before
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800259 */
Saurav Dasc88d4662017-05-15 15:34:25 -0700260 public void retryHash(Link link, boolean linkDown, boolean firstTime) {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700261 MacAddress neighborMac;
Charles Chan0b4e6182015-11-03 10:42:14 -0800262 try {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700263 neighborMac = deviceConfig.getDeviceMac(link.dst().deviceId());
Charles Chan0b4e6182015-11-03 10:42:14 -0800264 } catch (DeviceConfigNotFoundException e) {
Saurav Dasc88d4662017-05-15 15:34:25 -0700265 log.warn(e.getMessage() + " Aborting retryHash.");
Charles Chan0b4e6182015-11-03 10:42:14 -0800266 return;
267 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700268 // find all the destinationSets related to link
269 Set<DestinationSetNextObjectiveStoreKey> dsKeySet = dsNextObjStore.entrySet()
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700270 .stream()
Saurav Das7bcbe702017-06-13 15:35:54 -0700271 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
Pier Luigi63edd932018-01-14 21:56:11 +0100272 // Filter out PW transit groups or include them if MPLS ECMP is supported
Saurav Dasa4020382018-02-14 14:14:54 -0800273 .filter(entry -> !entry.getKey().destinationSet().notBos() ||
274 (entry.getKey().destinationSet().notBos() && srManager.getMplsEcmp()))
275 // Filter out simple SWAP groups or include them if MPLS ECMP is supported
276 .filter(entry -> !entry.getKey().destinationSet().swap() ||
277 (entry.getKey().destinationSet().swap() && srManager.getMplsEcmp()))
Saurav Das7bcbe702017-06-13 15:35:54 -0700278 .filter(entry -> entry.getValue().containsNextHop(link.dst().deviceId()))
279 .map(entry -> entry.getKey())
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700280 .collect(Collectors.toSet());
Saurav Dasc88d4662017-05-15 15:34:25 -0700281
Saurav Das7bcbe702017-06-13 15:35:54 -0700282 log.debug("retryHash: dsNextObjStore contents for linkSrc {} -> linkDst {}: {}",
283 deviceId, link.dst().deviceId(), dsKeySet);
284
285 for (DestinationSetNextObjectiveStoreKey dsKey : dsKeySet) {
286 NextNeighbors nextHops = dsNextObjStore.get(dsKey);
287 if (nextHops == null) {
Saurav Dasc88d4662017-05-15 15:34:25 -0700288 log.warn("retryHash in device {}, but global store has no record "
Saurav Das7bcbe702017-06-13 15:35:54 -0700289 + "for dsKey:{}", deviceId, dsKey);
Saurav Dasc88d4662017-05-15 15:34:25 -0700290 continue;
291 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700292 int nextId = nextHops.nextId();
293 Set<DeviceId> dstSet = nextHops.getDstForNextHop(link.dst().deviceId());
Saurav Dasc88d4662017-05-15 15:34:25 -0700294 if (!linkDown) {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700295 List<PortLabel> pl = Lists.newArrayList();
Saurav Dasc88d4662017-05-15 15:34:25 -0700296 if (firstTime) {
297 // some links may have come up before the next-objective was created
298 // we take this opportunity to ensure other ports to same next-hop-dst
299 // are part of the hash group (see CORD-1180). Duplicate additions
300 // to the same hash group are avoided by the driver.
301 for (PortNumber p : devicePortMap.get(link.dst().deviceId())) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700302 dstSet.forEach(dst -> {
303 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
Saurav Das9df5b7c2017-08-14 16:44:43 -0700304 pl.add(new PortLabel(p, edgeLabel));
Saurav Das7bcbe702017-06-13 15:35:54 -0700305 });
Saurav Dasc88d4662017-05-15 15:34:25 -0700306 }
Saurav Das9df5b7c2017-08-14 16:44:43 -0700307 addToHashedNextObjective(pl, neighborMac, nextId);
308 } else {
309 // handle only the port that came up
310 dstSet.forEach(dst -> {
311 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
312 pl.add(new PortLabel(link.src().port(), edgeLabel));
313 });
314 addToHashedNextObjective(pl, neighborMac, nextId);
Saurav Dasc88d4662017-05-15 15:34:25 -0700315 }
316 } else {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700317 // linkdown
318 List<PortLabel> pl = Lists.newArrayList();
Saurav Das7bcbe702017-06-13 15:35:54 -0700319 dstSet.forEach(dst -> {
320 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
Saurav Das9df5b7c2017-08-14 16:44:43 -0700321 pl.add(new PortLabel(link.src().port(), edgeLabel));
Saurav Das7bcbe702017-06-13 15:35:54 -0700322 });
Saurav Das9df5b7c2017-08-14 16:44:43 -0700323 removeFromHashedNextObjective(pl, neighborMac, nextId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700324 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800325 }
Saurav Das1547b3f2017-05-05 17:01:08 -0700326 }
327
Saurav Dasc88d4662017-05-15 15:34:25 -0700328 /**
Saurav Das9df5b7c2017-08-14 16:44:43 -0700329 * Utility class for associating output ports and the corresponding MPLS
330 * labels to push. In dual-homing, there are different labels to push
331 * corresponding to the destination switches in an edge-pair. If both
332 * destinations are reachable via the same spine, then the output-port to
333 * the spine will be associated with two labels i.e. there will be two
334 * PortLabel objects for the same port but with different labels.
335 */
336 private class PortLabel {
337 PortNumber port;
338 int edgeLabel;
339
340 PortLabel(PortNumber port, int edgeLabel) {
341 this.port = port;
342 this.edgeLabel = edgeLabel;
343 }
344
345 @Override
346 public String toString() {
347 return port.toString() + "/" + String.valueOf(edgeLabel);
348 }
349 }
350
351 /**
352 * Makes a call to the FlowObjective service to add buckets to
353 * a hashed group. User must ensure that all the ports & labels are meant
354 * same neighbor (ie. dstMac).
Saurav Dasc88d4662017-05-15 15:34:25 -0700355 *
Pier Luigi63edd932018-01-14 21:56:11 +0100356 * @param portLabels a collection of port & label combinations to add
Saurav Das9df5b7c2017-08-14 16:44:43 -0700357 * to the hash group identified by the nextId
Saurav Dasc88d4662017-05-15 15:34:25 -0700358 * @param dstMac destination mac address of next-hop
Saurav Das9df5b7c2017-08-14 16:44:43 -0700359 * @param nextId id for next-objective to which buckets will be added
Saurav Dasceccf242017-08-03 18:30:35 -0700360 *
Saurav Dasc88d4662017-05-15 15:34:25 -0700361 */
Saurav Das9df5b7c2017-08-14 16:44:43 -0700362 private void addToHashedNextObjective(Collection<PortLabel> portLabels,
363 MacAddress dstMac, Integer nextId) {
Saurav Das1547b3f2017-05-05 17:01:08 -0700364 // setup metadata to pass to nextObjective - indicate the vlan on egress
365 // if needed by the switch pipeline. Since hashed next-hops are always to
366 // other neighboring routers, there is no subnet assigned on those ports.
367 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
Saurav Das3c82f192018-08-13 15:34:26 -0700368 metabuilder.matchVlanId(srManager.getDefaultInternalVlan());
Saurav Das1547b3f2017-05-05 17:01:08 -0700369 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
370 .withId(nextId)
371 .withType(NextObjective.Type.HASHED)
Saurav Das1547b3f2017-05-05 17:01:08 -0700372 .withMeta(metabuilder.build())
373 .fromApp(appId);
Saurav Das9df5b7c2017-08-14 16:44:43 -0700374 // Create the new buckets to be updated
375 portLabels.forEach(pl -> {
376 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
377 tBuilder.setOutput(pl.port)
378 .setEthDst(dstMac)
379 .setEthSrc(nodeMacAddr);
380 if (pl.edgeLabel != DestinationSet.NO_EDGE_LABEL) {
381 tBuilder.pushMpls()
382 .copyTtlOut()
383 .setMpls(MplsLabel.mplsLabel(pl.edgeLabel));
384 }
385 nextObjBuilder.addTreatment(tBuilder.build());
386 });
387
388 log.debug("addToHash in device {}: Adding Bucket with port/label {} "
389 + "to nextId {}", deviceId, portLabels, nextId);
Saurav Das1547b3f2017-05-05 17:01:08 -0700390
391 ObjectiveContext context = new DefaultObjectiveContext(
Saurav Das9df5b7c2017-08-14 16:44:43 -0700392 (objective) -> log.debug("addToHash port/label {} addedTo "
393 + "NextObj {} on {}", portLabels, nextId, deviceId),
Charles Chan1dbadbd2018-08-23 14:30:33 -0700394 (objective, error) -> {
395 log.warn("addToHash failed to add port/label {} to NextObj {} on {}: {}",
396 portLabels, nextId, deviceId, error);
397 srManager.invalidateNextObj(objective.id());
398 });
Saurav Das1547b3f2017-05-05 17:01:08 -0700399 NextObjective nextObjective = nextObjBuilder.addToExisting(context);
400 flowObjectiveService.next(deviceId, nextObjective);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800401 }
402
403 /**
Saurav Das9df5b7c2017-08-14 16:44:43 -0700404 * Makes a call to the FlowObjective service to remove buckets from
405 * a hash group. User must ensure that all the ports & labels are meant
406 * same neighbor (ie. dstMac).
Saurav Dasceccf242017-08-03 18:30:35 -0700407 *
Pier Luigi63edd932018-01-14 21:56:11 +0100408 * @param portLabels a collection of port & label combinations to remove
Saurav Das9df5b7c2017-08-14 16:44:43 -0700409 * from the hash group identified by the nextId
Saurav Dasceccf242017-08-03 18:30:35 -0700410 * @param dstMac destination mac address of next-hop
Saurav Das9df5b7c2017-08-14 16:44:43 -0700411 * @param nextId id for next-objective from which buckets will be removed
Saurav Dasceccf242017-08-03 18:30:35 -0700412 */
Saurav Das9df5b7c2017-08-14 16:44:43 -0700413 private void removeFromHashedNextObjective(Collection<PortLabel> portLabels,
414 MacAddress dstMac, Integer nextId) {
Saurav Dasceccf242017-08-03 18:30:35 -0700415 NextObjective.Builder nextObjBuilder = DefaultNextObjective
416 .builder()
417 .withType(NextObjective.Type.HASHED) //same as original
418 .withId(nextId)
Saurav Das9df5b7c2017-08-14 16:44:43 -0700419 .fromApp(appId);
420 // Create the buckets to be removed
421 portLabels.forEach(pl -> {
422 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
423 tBuilder.setOutput(pl.port)
424 .setEthDst(dstMac)
425 .setEthSrc(nodeMacAddr);
426 if (pl.edgeLabel != DestinationSet.NO_EDGE_LABEL) {
427 tBuilder.pushMpls()
428 .copyTtlOut()
429 .setMpls(MplsLabel.mplsLabel(pl.edgeLabel));
430 }
431 nextObjBuilder.addTreatment(tBuilder.build());
432 });
433 log.debug("removeFromHash in device {}: Removing Bucket with port/label"
434 + " {} from nextId {}", deviceId, portLabels, nextId);
Saurav Dasc88d4662017-05-15 15:34:25 -0700435
Saurav Das9df5b7c2017-08-14 16:44:43 -0700436 ObjectiveContext context = new DefaultObjectiveContext(
437 (objective) -> log.debug("port/label {} removedFrom NextObj"
438 + " {} on {}", portLabels, nextId, deviceId),
Charles Chan1dbadbd2018-08-23 14:30:33 -0700439 (objective, error) -> {
440 log.warn("port/label {} failed to removeFrom NextObj {} on {}: {}",
441 portLabels, nextId, deviceId, error);
442 srManager.invalidateNextObj(objective.id());
443 });
Saurav Das9df5b7c2017-08-14 16:44:43 -0700444 NextObjective nextObjective = nextObjBuilder.removeFromExisting(context);
Saurav Dasceccf242017-08-03 18:30:35 -0700445 flowObjectiveService.next(deviceId, nextObjective);
446 }
Saurav Dasc88d4662017-05-15 15:34:25 -0700447
448 /**
449 * Checks all the hash-groups in the target-switch meant for the destination
450 * switch, and either adds or removes buckets to make the neighbor-set
451 * match the given next-hops. Typically called by the master instance of the
452 * destination switch, which may be different from the master instance of the
453 * target switch where hash-group changes are made.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800454 *
Saurav Dasc88d4662017-05-15 15:34:25 -0700455 * @param targetSw the switch in which the hash groups will be edited
456 * @param nextHops the current next hops for the target switch to reach
457 * the dest sw
458 * @param destSw the destination switch
459 * @param revoke true if hash groups need to remove buckets from the
460 * the groups to match the current next hops
461 * @return true if calls are made to edit buckets, or if no edits are required
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800462 */
Saurav Dasc88d4662017-05-15 15:34:25 -0700463 public boolean fixHashGroups(DeviceId targetSw, Set<DeviceId> nextHops,
464 DeviceId destSw, boolean revoke) {
465 // temporary storage of keys to be updated
Saurav Das7bcbe702017-06-13 15:35:54 -0700466 Map<DestinationSetNextObjectiveStoreKey, Set<DeviceId>> tempStore =
Saurav Dasc88d4662017-05-15 15:34:25 -0700467 new HashMap<>();
Saurav Das9df5b7c2017-08-14 16:44:43 -0700468 boolean foundNextObjective = false, success = true;
Charles Chan0b4e6182015-11-03 10:42:14 -0800469
Saurav Das7bcbe702017-06-13 15:35:54 -0700470 // retrieve hash-groups meant for destSw, which have destinationSets
Saurav Dasc88d4662017-05-15 15:34:25 -0700471 // with different neighbors than the given next-hops
Saurav Das7bcbe702017-06-13 15:35:54 -0700472 for (DestinationSetNextObjectiveStoreKey dskey : dsNextObjStore.keySet()) {
473 if (!dskey.deviceId().equals(targetSw) ||
474 !dskey.destinationSet().getDestinationSwitches().contains(destSw)) {
Saurav Dasc88d4662017-05-15 15:34:25 -0700475 continue;
476 }
477 foundNextObjective = true;
Saurav Das7bcbe702017-06-13 15:35:54 -0700478 NextNeighbors nhops = dsNextObjStore.get(dskey);
479 Set<DeviceId> currNeighbors = nhops.nextHops(destSw);
480 int edgeLabel = dskey.destinationSet().getEdgeLabel(destSw);
481 Integer nextId = nhops.nextId();
Saurav Dase0d4c872018-03-05 14:37:16 -0800482 if (currNeighbors == null || nextHops == null) {
483 log.warn("fixing hash groups but found currNeighbors:{} or nextHops:{}"
484 + " in targetSw:{} for dstSw:{}", currNeighbors,
485 nextHops, targetSw, destSw);
486 success &= false;
487 continue;
488 }
Charles Chan0b4e6182015-11-03 10:42:14 -0800489
Saurav Dasa4020382018-02-14 14:14:54 -0800490 // some store elements may not be hashed next-objectives - ignore them
491 if (isSimpleNextObjective(dskey)) {
492 log.debug("Ignoring {} of SIMPLE nextObj for targetSw:{}"
493 + " -> dstSw:{} with current nextHops:{} to new"
494 + " nextHops: {} in nextId:{}",
495 (revoke) ? "removal" : "addition", targetSw, destSw,
496 currNeighbors, nextHops, nextId);
Saurav Dase0d4c872018-03-05 14:37:16 -0800497 if ((revoke && !nextHops.isEmpty())
498 || (!revoke && !nextHops.equals(currNeighbors))) {
Saurav Dasf1027d42018-06-11 17:02:31 -0700499 log.debug("Simple next objective cannot be edited to "
Saurav Dasa4020382018-02-14 14:14:54 -0800500 + "move from {} to {}", currNeighbors, nextHops);
501 }
502 continue;
503 }
504
Saurav Dasc88d4662017-05-15 15:34:25 -0700505 Set<DeviceId> diff;
506 if (revoke) {
507 diff = Sets.difference(currNeighbors, nextHops);
508 log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next "
509 + "hops:{} ..removing {}", targetSw, destSw, nextId,
510 currNeighbors, diff);
511 } else {
512 diff = Sets.difference(nextHops, currNeighbors);
513 log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next "
514 + "hops:{} ..adding {}", targetSw, destSw, nextId,
515 currNeighbors, diff);
516 }
Saurav Das9df5b7c2017-08-14 16:44:43 -0700517 boolean suc = updateAllPortsToNextHop(diff, edgeLabel, nextId,
518 revoke);
519 if (suc) {
520 // to update neighbor set with changes made
Saurav Dasc88d4662017-05-15 15:34:25 -0700521 if (revoke) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700522 tempStore.put(dskey, Sets.difference(currNeighbors, diff));
Saurav Dasc88d4662017-05-15 15:34:25 -0700523 } else {
Saurav Das7bcbe702017-06-13 15:35:54 -0700524 tempStore.put(dskey, Sets.union(currNeighbors, diff));
Saurav Dasc88d4662017-05-15 15:34:25 -0700525 }
sangho834e4b02015-05-01 09:38:25 -0700526 }
Saurav Das9df5b7c2017-08-14 16:44:43 -0700527 success &= suc;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800528 }
529
Saurav Dasc88d4662017-05-15 15:34:25 -0700530 if (!foundNextObjective) {
531 log.debug("Cannot find any nextObjectives for route targetSw:{} "
532 + "-> dstSw:{}", targetSw, destSw);
533 return true; // nothing to do, return true so ECMPspg is updated
534 }
535
Saurav Das7bcbe702017-06-13 15:35:54 -0700536 // update the dsNextObjectiveStore with new destinationSet to nextId mappings
537 for (DestinationSetNextObjectiveStoreKey key : tempStore.keySet()) {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700538 NextNeighbors currentNextHops = dsNextObjStore.get(key);
539 if (currentNextHops == null) {
540 log.warn("fixHashGroups could not update global store in "
541 + "device {} .. missing nextNeighbors for key {}",
542 deviceId, key);
Saurav Dasc88d4662017-05-15 15:34:25 -0700543 continue;
544 }
Saurav Das9df5b7c2017-08-14 16:44:43 -0700545 Set<DeviceId> newNeighbors = new HashSet<>();
546 newNeighbors.addAll(tempStore.get(key));
547 Map<DeviceId, Set<DeviceId>> oldDstNextHops =
548 ImmutableMap.copyOf(currentNextHops.dstNextHops());
549 currentNextHops.dstNextHops().put(destSw, newNeighbors); //local change
550 log.debug("Updating nsNextObjStore target:{} -> dst:{} in key:{} nextId:{}",
551 targetSw, destSw, key, currentNextHops.nextId());
552 log.debug("Old dstNextHops: {}", oldDstNextHops);
553 log.debug("New dstNextHops: {}", currentNextHops.dstNextHops());
554 // update global store
555 dsNextObjStore.put(key,
556 new NextNeighbors(currentNextHops.dstNextHops(),
557 currentNextHops.nextId()));
Saurav Dasc88d4662017-05-15 15:34:25 -0700558 }
Saurav Dasa4020382018-02-14 14:14:54 -0800559
Saurav Das9df5b7c2017-08-14 16:44:43 -0700560 // even if one fails and others succeed, return false so ECMPspg not updated
561 return success;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800562 }
563
Saurav Dasceccf242017-08-03 18:30:35 -0700564 /**
565 * Updates the DestinationSetNextObjectiveStore with any per-destination nexthops
566 * that are not already in the store for the given DestinationSet. Note that
567 * this method does not remove existing next hops for the destinations in the
568 * DestinationSet.
569 *
570 * @param ds the DestinationSet for which the next hops need to be updated
571 * @param newDstNextHops a map of per-destination next hops to update the
572 * destinationSet with
573 * @return true if successful in updating all next hops
574 */
575 private boolean updateNextHops(DestinationSet ds,
Saurav Das7bcbe702017-06-13 15:35:54 -0700576 Map<DeviceId, Set<DeviceId>> newDstNextHops) {
577 DestinationSetNextObjectiveStoreKey key =
578 new DestinationSetNextObjectiveStoreKey(deviceId, ds);
579 NextNeighbors currNext = dsNextObjStore.get(key);
580 Map<DeviceId, Set<DeviceId>> currDstNextHops = currNext.dstNextHops();
581
582 // add newDstNextHops to currDstNextHops for each dst
583 boolean success = true;
584 for (DeviceId dstSw : ds.getDestinationSwitches()) {
585 Set<DeviceId> currNhops = currDstNextHops.get(dstSw);
586 Set<DeviceId> newNhops = newDstNextHops.get(dstSw);
587 currNhops = (currNhops == null) ? Sets.newHashSet() : currNhops;
588 newNhops = (newNhops == null) ? Sets.newHashSet() : newNhops;
589 int edgeLabel = ds.getEdgeLabel(dstSw);
590 int nextId = currNext.nextId();
591
592 // new next hops should be added
593 boolean suc = updateAllPortsToNextHop(Sets.difference(newNhops, currNhops),
594 edgeLabel, nextId, false);
595 if (suc) {
596 currNhops.addAll(newNhops);
597 currDstNextHops.put(dstSw, currNhops); // this is only a local change
598 }
599 success &= suc;
600 }
601
602 if (success) {
603 // update global store
604 dsNextObjStore.put(key, new NextNeighbors(currDstNextHops,
605 currNext.nextId()));
606 log.debug("Updated device:{} ds:{} new next-hops: {}", deviceId, ds,
607 dsNextObjStore.get(key));
608 }
609 return success;
610 }
611
Saurav Dasceccf242017-08-03 18:30:35 -0700612 /**
Saurav Das9df5b7c2017-08-14 16:44:43 -0700613 * Adds or removes buckets for all ports to a set of neighbor devices. Caller
614 * needs to ensure that the given neighbors are all next hops towards the
615 * same destination (represented by the given edgeLabel).
Saurav Dasceccf242017-08-03 18:30:35 -0700616 *
617 * @param neighbors set of neighbor device ids
618 * @param edgeLabel MPLS label to use in buckets
619 * @param nextId the nextObjective to change
620 * @param revoke true if buckets need to be removed, false if they need to
621 * be added
622 * @return true if successful in adding or removing buckets for all ports
623 * to the neighbors
624 */
625 private boolean updateAllPortsToNextHop(Set<DeviceId> neighbors, int edgeLabel,
Saurav Das7bcbe702017-06-13 15:35:54 -0700626 int nextId, boolean revoke) {
Saurav Dasceccf242017-08-03 18:30:35 -0700627 for (DeviceId neighbor : neighbors) {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700628 MacAddress neighborMac;
Saurav Das7bcbe702017-06-13 15:35:54 -0700629 try {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700630 neighborMac = deviceConfig.getDeviceMac(neighbor);
Saurav Das7bcbe702017-06-13 15:35:54 -0700631 } catch (DeviceConfigNotFoundException e) {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700632 log.warn(e.getMessage() + " Aborting updateAllPortsToNextHop"
633 + " for nextId:" + nextId);
Saurav Das7bcbe702017-06-13 15:35:54 -0700634 return false;
635 }
Saurav Das9df5b7c2017-08-14 16:44:43 -0700636 Collection<PortNumber> portsToNeighbor = devicePortMap.get(neighbor);
637 if (portsToNeighbor == null || portsToNeighbor.isEmpty()) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700638 log.warn("No ports found in dev:{} for neighbor:{} .. cannot "
Saurav Das9df5b7c2017-08-14 16:44:43 -0700639 + "updateAllPortsToNextHop for nextId: {}",
Saurav Das7bcbe702017-06-13 15:35:54 -0700640 deviceId, neighbor, nextId);
641 return false;
642 }
Saurav Das9df5b7c2017-08-14 16:44:43 -0700643 List<PortLabel> pl = Lists.newArrayList();
644 portsToNeighbor.forEach(p -> pl.add(new PortLabel(p, edgeLabel)));
Saurav Das7bcbe702017-06-13 15:35:54 -0700645 if (revoke) {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700646 log.debug("updateAllPortsToNextHops in device {}: Removing Bucket(s) "
647 + "with Port/Label:{} to next object id {}",
648 deviceId, pl, nextId);
649 removeFromHashedNextObjective(pl, neighborMac, nextId);
Saurav Das7bcbe702017-06-13 15:35:54 -0700650 } else {
Saurav Das9df5b7c2017-08-14 16:44:43 -0700651 log.debug("fixHashGroup in device {}: Adding Bucket(s) "
652 + "with Port/Label: {} to next object id {}",
653 deviceId, pl, nextId);
654 addToHashedNextObjective(pl, neighborMac, nextId);
Saurav Das7bcbe702017-06-13 15:35:54 -0700655 }
656 }
657 return true;
658 }
659
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800660 /**
Saurav Dasa4020382018-02-14 14:14:54 -0800661 * Returns true if the destination set is meant for swap or multi-labeled
662 * packet transport, and MPLS ECMP is not supported.
663 *
664 * @param dskey the key representing the destination set
665 * @return true if destination set is meant for simple next objectives
666 */
667 boolean isSimpleNextObjective(DestinationSetNextObjectiveStoreKey dskey) {
668 return (dskey.destinationSet().notBos() || dskey.destinationSet().swap())
669 && !srManager.getMplsEcmp();
670 }
671
672 /**
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800673 * Adds or removes a port that has been configured with a vlan to a broadcast group
674 * for bridging. Should only be called by the master instance for this device.
Saurav Das1a129a02016-11-18 15:21:57 -0800675 *
676 * @param port the port on this device that needs to be added/removed to a bcast group
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800677 * @param vlanId the vlan id corresponding to the broadcast domain/group
678 * @param popVlan indicates if packets should be sent out untagged or not out
679 * of the port. If true, indicates an access (untagged) or native vlan
680 * configuration. If false, indicates a trunk (tagged) vlan config.
Saurav Das1a129a02016-11-18 15:21:57 -0800681 * @param portUp true if port is enabled, false if disabled
Saurav Das1a129a02016-11-18 15:21:57 -0800682 */
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800683 public void processEdgePort(PortNumber port, VlanId vlanId,
684 boolean popVlan, boolean portUp) {
Saurav Das1a129a02016-11-18 15:21:57 -0800685 //get the next id for the subnet and edit it.
Charles Chan59cc16d2017-02-02 16:20:42 -0800686 Integer nextId = getVlanNextObjectiveId(vlanId);
Saurav Das1a129a02016-11-18 15:21:57 -0800687 if (nextId == -1) {
688 if (portUp) {
689 log.debug("**Creating flooding group for first port enabled in"
Saurav Dasf14d9ef2017-12-05 15:00:23 -0800690 + " vlan {} on dev {} port {}", vlanId, deviceId, port);
Charles Chan59cc16d2017-02-02 16:20:42 -0800691 createBcastGroupFromVlan(vlanId, Collections.singleton(port));
Saurav Das1a129a02016-11-18 15:21:57 -0800692 } else {
693 log.warn("Could not find flooding group for subnet {} on dev:{} when"
Charles Chan59cc16d2017-02-02 16:20:42 -0800694 + " removing port:{}", vlanId, deviceId, port);
Saurav Das1a129a02016-11-18 15:21:57 -0800695 }
696 return;
697 }
698
699 log.info("**port{} in device {}: {} Bucket with Port {} to"
700 + " next-id {}", (portUp) ? "UP" : "DOWN", deviceId,
701 (portUp) ? "Adding" : "Removing",
702 port, nextId);
703 // Create the bucket to be added or removed
704 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800705 if (popVlan) {
706 tBuilder.popVlan();
707 }
Saurav Das1a129a02016-11-18 15:21:57 -0800708 tBuilder.setOutput(port);
709
Saurav Das1a129a02016-11-18 15:21:57 -0800710 TrafficSelector metadata =
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800711 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
Saurav Das1a129a02016-11-18 15:21:57 -0800712
713 NextObjective.Builder nextObjBuilder = DefaultNextObjective
714 .builder().withId(nextId)
715 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
716 .addTreatment(tBuilder.build())
717 .withMeta(metadata);
718
719 ObjectiveContext context = new DefaultObjectiveContext(
720 (objective) -> log.debug("port {} successfully {} NextObj {} on {}",
721 port, (portUp) ? "addedTo" : "removedFrom",
722 nextId, deviceId),
Charles Chan1dbadbd2018-08-23 14:30:33 -0700723 (objective, error) -> {
724 log.warn("port {} failed to {} NextObj {} on {}: {}",
725 port, (portUp) ? "addTo" : "removeFrom", nextId, deviceId, error);
726 srManager.invalidateNextObj(objective.id());
727 });
Saurav Das1a129a02016-11-18 15:21:57 -0800728
729 NextObjective nextObj = (portUp) ? nextObjBuilder.addToExisting(context)
730 : nextObjBuilder.removeFromExisting(context);
731 log.debug("edgePort processed: Submited next objective {} in device {}",
732 nextId, deviceId);
733 flowObjectiveService.next(deviceId, nextObj);
734 }
735
736 /**
Saurav Dasa4020382018-02-14 14:14:54 -0800737 * Returns the next objective of type hashed (or simple) associated with the
738 * destination set. In addition, updates the existing next-objective if new
739 * route-paths found have resulted in the addition of new next-hops to a
740 * particular destination. If there is no existing next objective for this
741 * destination set, this method would create a next objective and return the
742 * nextId. Optionally metadata can be passed in for the creation of the next
743 * objective. If the parameter simple is true then a simple next objective
744 * is created instead of a hashed one.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800745 *
Saurav Das7bcbe702017-06-13 15:35:54 -0700746 * @param ds destination set
747 * @param nextHops a map of per destination next hops
Saurav Das8a0732e2015-11-20 15:27:53 -0800748 * @param meta metadata passed into the creation of a Next Objective
Saurav Dasa4020382018-02-14 14:14:54 -0800749 * @param simple if true, a simple next objective will be created instead of
750 * a hashed next objective
Saurav Das8a0732e2015-11-20 15:27:53 -0800751 * @return int if found or -1 if there are errors in the creation of the
Saurav Dasa4020382018-02-14 14:14:54 -0800752 * neighbor set.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800753 */
Saurav Das7bcbe702017-06-13 15:35:54 -0700754 public int getNextObjectiveId(DestinationSet ds,
755 Map<DeviceId, Set<DeviceId>> nextHops,
Saurav Dasa4020382018-02-14 14:14:54 -0800756 TrafficSelector meta, boolean simple) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700757 NextNeighbors next = dsNextObjStore.
758 get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
759 if (next == null) {
760 log.debug("getNextObjectiveId in device{}: Next objective id "
761 + "not found for {} ... creating", deviceId, ds);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700762 log.trace("getNextObjectiveId: nsNextObjStore contents for device {}: {}",
763 deviceId,
Saurav Das7bcbe702017-06-13 15:35:54 -0700764 dsNextObjStore.entrySet()
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700765 .stream()
766 .filter((nsStoreEntry) ->
767 (nsStoreEntry.getKey().deviceId().equals(deviceId)))
768 .collect(Collectors.toList()));
Saurav Das7bcbe702017-06-13 15:35:54 -0700769
Saurav Dasa4020382018-02-14 14:14:54 -0800770 createGroupFromDestinationSet(ds, nextHops, meta, simple);
Saurav Das7bcbe702017-06-13 15:35:54 -0700771 next = dsNextObjStore.
772 get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
773 if (next == null) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700774 log.warn("getNextObjectiveId: unable to create next objective");
Saurav Das7bcbe702017-06-13 15:35:54 -0700775 // failure in creating group
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700776 return -1;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700777 } else {
778 log.debug("getNextObjectiveId in device{}: Next objective id {} "
Saurav Das7bcbe702017-06-13 15:35:54 -0700779 + "created for {}", deviceId, next.nextId(), ds);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700780 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700781 } else {
782 log.trace("getNextObjectiveId in device{}: Next objective id {} "
Saurav Das7bcbe702017-06-13 15:35:54 -0700783 + "found for {}", deviceId, next.nextId(), ds);
784 // should fix hash groups too if next-hops have changed
785 if (!next.dstNextHops().equals(nextHops)) {
786 log.debug("Nexthops have changed for dev:{} nextId:{} ..updating",
787 deviceId, next.nextId());
788 if (!updateNextHops(ds, nextHops)) {
789 // failure in updating group
790 return -1;
791 }
792 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700793 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700794 return next.nextId();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800795 }
796
sangho0b2b6d12015-05-20 22:16:38 -0700797 /**
Charles Chan59cc16d2017-02-02 16:20:42 -0800798 * Returns the next objective of type broadcast associated with the vlan,
Saurav Das4ce45962015-11-24 23:21:05 -0800799 * or -1 if no such objective exists. Note that this method does NOT create
800 * the next objective as a side-effect. It is expected that is objective is
Saurav Das1a129a02016-11-18 15:21:57 -0800801 * created at startup from network configuration. Typically this is used
802 * for L2 flooding within the subnet configured on the switch.
Charles Chanc42e84e2015-10-20 16:24:19 -0700803 *
Charles Chan59cc16d2017-02-02 16:20:42 -0800804 * @param vlanId vlan id
Charles Chanc42e84e2015-10-20 16:24:19 -0700805 * @return int if found or -1
806 */
Charles Chan59cc16d2017-02-02 16:20:42 -0800807 public int getVlanNextObjectiveId(VlanId vlanId) {
808 Integer nextId = vlanNextObjStore.
809 get(new VlanNextObjectiveStoreKey(deviceId, vlanId));
Charles Chan9f676b62015-10-29 14:58:10 -0700810
811 return (nextId != null) ? nextId : -1;
Charles Chanc42e84e2015-10-20 16:24:19 -0700812 }
813
814 /**
Ruchi Sahota5d800282019-01-28 01:08:18 +0000815 * Returns the next objective of type simple associated with the mac/vlan on the
816 * device, given the treatment. Different treatments to the same mac/vlan result
817 * in different next objectives. If no such objective exists, this method
818 * creates one (if requested) and returns the id. Optionally metadata can be passed in for
819 * the creation of the objective. Typically this is used for L2 and L3 forwarding
820 * to compute nodes and containers/VMs on the compute nodes directly attached
821 * to the switch.
822 *
823 * @param macAddr the mac addr for the simple next objective
824 * @param vlanId the vlan for the simple next objective
Ruchi Sahotab555e592019-05-09 17:26:14 -0400825 * @param port port with which to create the Next Obj.
Ruchi Sahota5d800282019-01-28 01:08:18 +0000826 * @param createIfMissing true if a next object should be created if not found
827 * @return int if found or created, -1 if there are errors during the
828 * creation of the next objective.
829 */
Ruchi Sahotab555e592019-05-09 17:26:14 -0400830 public int getMacVlanNextObjectiveId(MacAddress macAddr, VlanId vlanId, PortNumber port,
831 boolean createIfMissing) {
Ruchi Sahota5d800282019-01-28 01:08:18 +0000832
833 Integer nextId = macVlanNextObjStore
834 .get(new MacVlanNextObjectiveStoreKey(deviceId, macAddr, vlanId));
835
836 if (nextId != null) {
837 return nextId;
838 }
839
840 log.debug("getMacVlanNextObjectiveId in device {}: Next objective id "
841 + "not found for host : {}/{} .. {}", deviceId, macAddr, vlanId,
842 (createIfMissing) ? "creating" : "aborting");
843
844 if (!createIfMissing) {
845 return -1;
846 }
847
Ruchi Sahotab555e592019-05-09 17:26:14 -0400848 MacAddress deviceMac;
849 try {
850 deviceMac = deviceConfig.getDeviceMac(deviceId);
851 } catch (DeviceConfigNotFoundException e) {
852 log.warn(e.getMessage() + " in getMacVlanNextObjectiveId");
853 return -1;
854 }
855
856 // since we are creating now, port cannot be null
857 if (port == null) {
858 log.debug("getMacVlanNextObjectiveId : port information cannot be null "
859 + "for device {}, host {}/{}", deviceId, macAddr, vlanId);
860 return -1;
861 }
862
863 TrafficSelector.Builder meta = DefaultTrafficSelector.builder();
864
865 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
866 treatment.deferred()
867 .setEthDst(macAddr)
868 .setEthSrc(deviceMac)
869 .setOutput(port);
870
871 ConnectPoint connectPoint = new ConnectPoint(deviceId, port);
872 VlanId untaggedVlan = srManager.interfaceService.getUntaggedVlanId(connectPoint);
873 Set<VlanId> taggedVlans = srManager.interfaceService.getTaggedVlanId(connectPoint);
874 VlanId nativeVlan = srManager.interfaceService.getNativeVlanId(connectPoint);
875
876 // Adjust the meta according to VLAN configuration
877 if (taggedVlans.contains(vlanId)) {
878 treatment.setVlanId(vlanId);
879 } else if (vlanId.equals(VlanId.NONE)) {
880 if (untaggedVlan != null) {
881 meta.matchVlanId(untaggedVlan);
882 } else if (nativeVlan != null) {
883 meta.matchVlanId(nativeVlan);
884 } else {
885 log.warn("Untagged nexthop {}/{} is not allowed on {} without untagged or native vlan",
886 macAddr, vlanId, connectPoint);
887 return -1;
888 }
889 } else {
890 log.warn("Tagged nexthop {}/{} is not allowed on {} without VLAN listed"
891 + " in tagged vlan", macAddr, vlanId, connectPoint);
892 return -1;
893 }
894
Ruchi Sahota5d800282019-01-28 01:08:18 +0000895 /* create missing next objective */
Ruchi Sahotab555e592019-05-09 17:26:14 -0400896 nextId = createGroupFromMacVlan(macAddr, vlanId, treatment.build(), meta.build());
Ruchi Sahota5d800282019-01-28 01:08:18 +0000897 if (nextId == null) {
898 log.warn("getMacVlanNextObjectiveId: unable to create next obj"
899 + "for dev:{} host:{}/{}", deviceId, macAddr, vlanId);
900 return -1;
901 }
902 return nextId;
903 }
904
905
906 /**
Saurav Das4ce45962015-11-24 23:21:05 -0800907 * Returns the next objective of type simple associated with the port on the
908 * device, given the treatment. Different treatments to the same port result
909 * in different next objectives. If no such objective exists, this method
Saurav Das961beb22017-03-29 19:09:17 -0700910 * creates one (if requested) and returns the id. Optionally metadata can be passed in for
Saurav Das1a129a02016-11-18 15:21:57 -0800911 * the creation of the objective. Typically this is used for L2 and L3 forwarding
912 * to compute nodes and containers/VMs on the compute nodes directly attached
913 * to the switch.
Saurav Das4ce45962015-11-24 23:21:05 -0800914 *
915 * @param portNum the port number for the simple next objective
916 * @param treatment the actions to apply on the packets (should include outport)
917 * @param meta optional metadata passed into the creation of the next objective
Saurav Das961beb22017-03-29 19:09:17 -0700918 * @param createIfMissing true if a next object should be created if not found
Saurav Das4ce45962015-11-24 23:21:05 -0800919 * @return int if found or created, -1 if there are errors during the
920 * creation of the next objective.
921 */
922 public int getPortNextObjectiveId(PortNumber portNum, TrafficTreatment treatment,
Saurav Das961beb22017-03-29 19:09:17 -0700923 TrafficSelector meta, boolean createIfMissing) {
Charles Chane849c192016-01-11 18:28:54 -0800924 Integer nextId = portNextObjStore
Saurav Das76ae6812017-03-15 15:15:14 -0700925 .get(new PortNextObjectiveStoreKey(deviceId, portNum, treatment, meta));
Saurav Das961beb22017-03-29 19:09:17 -0700926 if (nextId != null) {
927 return nextId;
928 }
929 log.debug("getPortNextObjectiveId in device {}: Next objective id "
930 + "not found for port: {} .. {}", deviceId, portNum,
931 (createIfMissing) ? "creating" : "aborting");
932 if (!createIfMissing) {
933 return -1;
934 }
935 // create missing next objective
936 createGroupFromPort(portNum, treatment, meta);
937 nextId = portNextObjStore.get(new PortNextObjectiveStoreKey(deviceId, portNum,
938 treatment, meta));
Saurav Das4ce45962015-11-24 23:21:05 -0800939 if (nextId == null) {
Saurav Das961beb22017-03-29 19:09:17 -0700940 log.warn("getPortNextObjectiveId: unable to create next obj"
941 + "for dev:{} port:{}", deviceId, portNum);
942 return -1;
Charles Chane849c192016-01-11 18:28:54 -0800943 }
944 return nextId;
945 }
946
947 /**
sangho0b2b6d12015-05-20 22:16:38 -0700948 * Checks if the next objective ID (group) for the neighbor set exists or not.
949 *
950 * @param ns neighbor set to check
951 * @return true if it exists, false otherwise
952 */
Saurav Das7bcbe702017-06-13 15:35:54 -0700953 public boolean hasNextObjectiveId(DestinationSet ns) {
954 NextNeighbors nextHops = dsNextObjStore.
955 get(new DestinationSetNextObjectiveStoreKey(deviceId, ns));
956 if (nextHops == null) {
sangho0b2b6d12015-05-20 22:16:38 -0700957 return false;
958 }
959
960 return true;
961 }
962
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800963 private void populateNeighborMaps() {
964 Set<Link> outgoingLinks = linkService.getDeviceEgressLinks(deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700965 for (Link link : outgoingLinks) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800966 if (link.type() != Link.Type.DIRECT) {
967 continue;
968 }
969 addNeighborAtPort(link.dst().deviceId(), link.src().port());
970 }
971 }
972
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700973 protected void addNeighborAtPort(DeviceId neighborId,
974 PortNumber portToNeighbor) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800975 // Update DeviceToPort database
976 log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
977 deviceId, neighborId, portToNeighbor);
Saurav Das8a0732e2015-11-20 15:27:53 -0800978 Set<PortNumber> ports = Collections
979 .newSetFromMap(new ConcurrentHashMap<PortNumber, Boolean>());
980 ports.add(portToNeighbor);
981 Set<PortNumber> portnums = devicePortMap.putIfAbsent(neighborId, ports);
982 if (portnums != null) {
983 portnums.add(portToNeighbor);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800984 }
985
986 // Update portToDevice database
Saurav Dasa4020382018-02-14 14:14:54 -0800987 // should always update as neighbor could have changed on this port
988 DeviceId prev = portDeviceMap.put(portToNeighbor, neighborId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800989 if (prev != null) {
Saurav Das5a356042018-04-06 20:16:01 -0700990 log.warn("Device/port: {}/{} previous neighbor: {}, current neighbor: {} ",
Saurav Dasc88d4662017-05-15 15:34:25 -0700991 deviceId, portToNeighbor, prev, neighborId);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800992 }
993 }
994
sangho1e575652015-05-14 00:39:53 -0700995 /**
Saurav Das7bcbe702017-06-13 15:35:54 -0700996 * Creates a NextObjective for a hash group in this device from a given
Saurav Dasa4020382018-02-14 14:14:54 -0800997 * DestinationSet. If the parameter simple is true, a simple next objective
998 * is created instead.
sangho1e575652015-05-14 00:39:53 -0700999 *
Saurav Das7bcbe702017-06-13 15:35:54 -07001000 * @param ds the DestinationSet
1001 * @param neighbors a map for each destination and its next-hops
Saurav Das8a0732e2015-11-20 15:27:53 -08001002 * @param meta metadata passed into the creation of a Next Objective
Saurav Dasa4020382018-02-14 14:14:54 -08001003 * @param simple if true, a simple next objective will be created instead of
1004 * a hashed next objective
sangho1e575652015-05-14 00:39:53 -07001005 */
Saurav Das7bcbe702017-06-13 15:35:54 -07001006 public void createGroupFromDestinationSet(DestinationSet ds,
1007 Map<DeviceId, Set<DeviceId>> neighbors,
1008 TrafficSelector meta,
Saurav Dasa4020382018-02-14 14:14:54 -08001009 boolean simple) {
Saurav Das7bcbe702017-06-13 15:35:54 -07001010 int nextId = flowObjectiveService.allocateNextId();
Saurav Dasa4020382018-02-14 14:14:54 -08001011 NextObjective.Type type = (simple) ? NextObjective.Type.SIMPLE
1012 : NextObjective.Type.HASHED;
Saurav Das7bcbe702017-06-13 15:35:54 -07001013 if (neighbors == null || neighbors.isEmpty()) {
1014 log.warn("createGroupsFromDestinationSet: needs at least one neighbor"
1015 + "to create group in dev:{} for ds: {} with next-hops {}",
1016 deviceId, ds, neighbors);
1017 return;
1018 }
Saurav Das7bcbe702017-06-13 15:35:54 -07001019
1020 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1021 .builder()
1022 .withId(nextId)
1023 .withType(type)
1024 .fromApp(appId);
1025 if (meta != null) {
1026 nextObjBuilder.withMeta(meta);
1027 }
1028
1029 // create treatment buckets for each neighbor for each dst Device
1030 // except in the special case where we only want to pick a single
Saurav Dasa4020382018-02-14 14:14:54 -08001031 // neighbor/port for a simple nextObj
Saurav Das7bcbe702017-06-13 15:35:54 -07001032 boolean foundSingleNeighbor = false;
1033 boolean treatmentAdded = false;
1034 Map<DeviceId, Set<DeviceId>> dstNextHops = new ConcurrentHashMap<>();
1035 for (DeviceId dst : ds.getDestinationSwitches()) {
1036 Set<DeviceId> nextHops = neighbors.get(dst);
1037 if (nextHops == null || nextHops.isEmpty()) {
1038 continue;
Pier Ventre917127a2016-10-31 16:49:19 -07001039 }
Saurav Das7bcbe702017-06-13 15:35:54 -07001040
1041 if (foundSingleNeighbor) {
1042 break;
1043 }
1044
1045 for (DeviceId neighborId : nextHops) {
Saurav Das8a0732e2015-11-20 15:27:53 -08001046 if (devicePortMap.get(neighborId) == null) {
1047 log.warn("Neighbor {} is not in the port map yet for dev:{}",
1048 neighborId, deviceId);
sangho834e4b02015-05-01 09:38:25 -07001049 return;
Jon Hallcbd1b392017-01-18 20:15:44 -08001050 } else if (devicePortMap.get(neighborId).isEmpty()) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001051 log.warn("There are no ports for "
Saurav Das8a0732e2015-11-20 15:27:53 -08001052 + "the Device {} in the port map yet", neighborId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001053 return;
sangho834e4b02015-05-01 09:38:25 -07001054 }
1055
Saurav Das8a0732e2015-11-20 15:27:53 -08001056 MacAddress neighborMac;
Charles Chan0b4e6182015-11-03 10:42:14 -08001057 try {
Saurav Das8a0732e2015-11-20 15:27:53 -08001058 neighborMac = deviceConfig.getDeviceMac(neighborId);
Charles Chan0b4e6182015-11-03 10:42:14 -08001059 } catch (DeviceConfigNotFoundException e) {
Saurav Das7bcbe702017-06-13 15:35:54 -07001060 log.warn(e.getMessage() + " Aborting createGroupsFromDestinationset.");
Charles Chan0b4e6182015-11-03 10:42:14 -08001061 return;
1062 }
Saurav Das7bcbe702017-06-13 15:35:54 -07001063 // For each port to the neighbor, we create a new treatment
Pier Ventre917127a2016-10-31 16:49:19 -07001064 Set<PortNumber> neighborPorts = devicePortMap.get(neighborId);
Saurav Dasa4020382018-02-14 14:14:54 -08001065 // In this case we need a SIMPLE nextObj. We randomly pick a port
1066 if (simple) {
Pier Ventre917127a2016-10-31 16:49:19 -07001067 int size = devicePortMap.get(neighborId).size();
1068 int index = RandomUtils.nextInt(0, size);
1069 neighborPorts = Collections.singleton(
Saurav Das7bcbe702017-06-13 15:35:54 -07001070 Iterables.get(devicePortMap.get(neighborId),
1071 index));
1072 foundSingleNeighbor = true;
Pier Ventre917127a2016-10-31 16:49:19 -07001073 }
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001074
Pier Ventre917127a2016-10-31 16:49:19 -07001075 for (PortNumber sp : neighborPorts) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001076 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
1077 .builder();
Saurav Das7bcbe702017-06-13 15:35:54 -07001078 tBuilder.setEthDst(neighborMac).setEthSrc(nodeMacAddr);
1079 int edgeLabel = ds.getEdgeLabel(dst);
1080 if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
Saurav Dasa4020382018-02-14 14:14:54 -08001081 if (simple) {
1082 // swap label case
1083 tBuilder.setMpls(MplsLabel.mplsLabel(edgeLabel));
1084 } else {
1085 // ecmp with label push case
1086 tBuilder.pushMpls().copyTtlOut()
1087 .setMpls(MplsLabel.mplsLabel(edgeLabel));
1088 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001089 }
Andreas Pantelopoulose8d5f412018-05-01 14:56:05 -07001090 if ((ds.getTypeOfDstSet() == DestinationSet.DestinationSetType.SWAP_NOT_BOS) ||
1091 (ds.getTypeOfDstSet() == DestinationSet.DestinationSetType.POP_NOT_BOS)) {
Saurav Das3c82f192018-08-13 15:34:26 -07001092 tBuilder.setVlanId(srManager.getPwTransportVlan());
Andreas Pantelopoulose8d5f412018-05-01 14:56:05 -07001093 }
Saurav Das8a0732e2015-11-20 15:27:53 -08001094 tBuilder.setOutput(sp);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001095 nextObjBuilder.addTreatment(tBuilder.build());
Saurav Das7bcbe702017-06-13 15:35:54 -07001096 treatmentAdded = true;
1097 //update store
1098 Set<DeviceId> existingNeighbors = dstNextHops.get(dst);
1099 if (existingNeighbors == null) {
1100 existingNeighbors = new HashSet<>();
1101 }
1102 existingNeighbors.add(neighborId);
1103 dstNextHops.put(dst, existingNeighbors);
1104 log.debug("creating treatment for port/label {}/{} in next:{}",
1105 sp, edgeLabel, nextId);
1106 }
1107
1108 if (foundSingleNeighbor) {
1109 break;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -08001110 }
1111 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -08001112 }
Saurav Das7bcbe702017-06-13 15:35:54 -07001113
1114 if (!treatmentAdded) {
1115 log.warn("Could not createGroup from DestinationSet {} without any"
1116 + "next hops {}", ds, neighbors);
1117 return;
1118 }
1119 ObjectiveContext context = new DefaultObjectiveContext(
1120 (objective) ->
1121 log.debug("createGroupsFromDestinationSet installed "
1122 + "NextObj {} on {}", nextId, deviceId),
Charles Chan1dbadbd2018-08-23 14:30:33 -07001123 (objective, error) -> {
1124 log.warn("createGroupsFromDestinationSet failed to install NextObj {} on {}: {}",
1125 nextId, deviceId, error);
1126 srManager.invalidateNextObj(objective.id());
1127 });
Saurav Das7bcbe702017-06-13 15:35:54 -07001128 NextObjective nextObj = nextObjBuilder.add(context);
1129 log.debug(".. createGroupsFromDestinationSet: Submitted "
1130 + "next objective {} in device {}", nextId, deviceId);
1131 flowObjectiveService.next(deviceId, nextObj);
1132 //update store
1133 dsNextObjStore.put(new DestinationSetNextObjectiveStoreKey(deviceId, ds),
1134 new NextNeighbors(dstNextHops, nextId));
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -08001135 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001136
Saurav Das4ce45962015-11-24 23:21:05 -08001137 /**
Saurav Das1a129a02016-11-18 15:21:57 -08001138 * Creates broadcast groups for all ports in the same subnet for
1139 * all configured subnets.
Saurav Das4ce45962015-11-24 23:21:05 -08001140 */
Charles Chan59cc16d2017-02-02 16:20:42 -08001141 public void createGroupsFromVlanConfig() {
Charles Chan7ffd81f2017-02-08 15:52:08 -08001142 srManager.getVlanPortMap(deviceId).asMap().forEach((vlanId, ports) -> {
Charles Chan59cc16d2017-02-02 16:20:42 -08001143 createBcastGroupFromVlan(vlanId, ports);
Pier Ventre10bd8d12016-11-26 21:05:22 -08001144 });
Saurav Das1a129a02016-11-18 15:21:57 -08001145 }
Charles Chan9f676b62015-10-29 14:58:10 -07001146
Saurav Das1a129a02016-11-18 15:21:57 -08001147 /**
Charles Chan59cc16d2017-02-02 16:20:42 -08001148 * Creates a single broadcast group from a given vlan id and list of ports.
Saurav Das1a129a02016-11-18 15:21:57 -08001149 *
Charles Chan59cc16d2017-02-02 16:20:42 -08001150 * @param vlanId vlan id
Saurav Das1a129a02016-11-18 15:21:57 -08001151 * @param ports list of ports in the subnet
1152 */
Charles Chan7ffd81f2017-02-08 15:52:08 -08001153 public void createBcastGroupFromVlan(VlanId vlanId, Collection<PortNumber> ports) {
Charles Chan59cc16d2017-02-02 16:20:42 -08001154 VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
Charles Chan9f676b62015-10-29 14:58:10 -07001155
Charles Chan59cc16d2017-02-02 16:20:42 -08001156 if (vlanNextObjStore.containsKey(key)) {
Saurav Das1a129a02016-11-18 15:21:57 -08001157 log.debug("Broadcast group for device {} and subnet {} exists",
Charles Chan59cc16d2017-02-02 16:20:42 -08001158 deviceId, vlanId);
Saurav Das1a129a02016-11-18 15:21:57 -08001159 return;
1160 }
Charles Chan188ebf52015-12-23 00:15:11 -08001161
Saurav Das1a129a02016-11-18 15:21:57 -08001162 TrafficSelector metadata =
Charles Chan59cc16d2017-02-02 16:20:42 -08001163 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
Charles Chanc42e84e2015-10-20 16:24:19 -07001164
Saurav Das1a129a02016-11-18 15:21:57 -08001165 int nextId = flowObjectiveService.allocateNextId();
Charles Chanc42e84e2015-10-20 16:24:19 -07001166
Saurav Das1a129a02016-11-18 15:21:57 -08001167 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1168 .builder().withId(nextId)
1169 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1170 .withMeta(metadata);
Charles Chanc42e84e2015-10-20 16:24:19 -07001171
Saurav Das1a129a02016-11-18 15:21:57 -08001172 ports.forEach(port -> {
1173 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Charles Chan7ffd81f2017-02-08 15:52:08 -08001174 if (toPopVlan(port, vlanId)) {
1175 tBuilder.popVlan();
1176 }
Saurav Das1a129a02016-11-18 15:21:57 -08001177 tBuilder.setOutput(port);
1178 nextObjBuilder.addTreatment(tBuilder.build());
Charles Chanc42e84e2015-10-20 16:24:19 -07001179 });
Saurav Das1a129a02016-11-18 15:21:57 -08001180
Saurav Das961beb22017-03-29 19:09:17 -07001181 ObjectiveContext context = new DefaultObjectiveContext(
1182 (objective) ->
1183 log.debug("createBroadcastGroupFromVlan installed "
1184 + "NextObj {} on {}", nextId, deviceId),
Charles Chan1dbadbd2018-08-23 14:30:33 -07001185 (objective, error) -> {
1186 log.warn("createBroadcastGroupFromVlan failed to install NextObj {} on {}: {}",
1187 nextId, deviceId, error);
1188 srManager.invalidateNextObj(objective.id());
1189 });
Saurav Das961beb22017-03-29 19:09:17 -07001190 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Das1a129a02016-11-18 15:21:57 -08001191 flowObjectiveService.next(deviceId, nextObj);
Saurav Dasf14d9ef2017-12-05 15:00:23 -08001192 log.debug("createBcastGroupFromVlan: Submitted next objective {} "
1193 + "for vlan: {} in device {}", nextId, vlanId, deviceId);
Saurav Das1a129a02016-11-18 15:21:57 -08001194
Charles Chan59cc16d2017-02-02 16:20:42 -08001195 vlanNextObjStore.put(key, nextId);
Charles Chanc42e84e2015-10-20 16:24:19 -07001196 }
1197
Charles Chane849c192016-01-11 18:28:54 -08001198 /**
Jonghwan Hyun42fe1052017-08-25 17:48:36 -07001199 * Removes a single broadcast group from a given vlan id.
1200 * The group should be empty.
1201 * @param deviceId device Id to remove the group
1202 * @param portNum port number related to the group
1203 * @param vlanId vlan id of the broadcast group to remove
1204 * @param popVlan true if the TrafficTreatment involves pop vlan tag action
1205 */
1206 public void removeBcastGroupFromVlan(DeviceId deviceId, PortNumber portNum,
1207 VlanId vlanId, boolean popVlan) {
1208 VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
1209
1210 if (!vlanNextObjStore.containsKey(key)) {
1211 log.debug("Broadcast group for device {} and subnet {} does not exist",
1212 deviceId, vlanId);
1213 return;
1214 }
1215
1216 TrafficSelector metadata =
1217 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
1218
1219 int nextId = vlanNextObjStore.get(key);
1220
1221 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1222 .builder().withId(nextId)
1223 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1224 .withMeta(metadata);
1225
1226 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1227 if (popVlan) {
1228 tBuilder.popVlan();
1229 }
1230 tBuilder.setOutput(portNum);
1231 nextObjBuilder.addTreatment(tBuilder.build());
1232
1233 ObjectiveContext context = new DefaultObjectiveContext(
1234 (objective) ->
1235 log.debug("removeBroadcastGroupFromVlan removed "
1236 + "NextObj {} on {}", nextId, deviceId),
Charles Chan1dbadbd2018-08-23 14:30:33 -07001237 (objective, error) -> {
1238 log.warn("removeBroadcastGroupFromVlan failed to remove NextObj {} on {}: {}",
1239 nextId, deviceId, error);
1240 srManager.invalidateNextObj(objective.id());
1241 });
Jonghwan Hyun42fe1052017-08-25 17:48:36 -07001242 NextObjective nextObj = nextObjBuilder.remove(context);
1243 flowObjectiveService.next(deviceId, nextObj);
1244 log.debug("removeBcastGroupFromVlan: Submited next objective {} in device {}",
1245 nextId, deviceId);
1246
1247 vlanNextObjStore.remove(key, nextId);
1248 }
1249
1250 /**
Charles Chan7ffd81f2017-02-08 15:52:08 -08001251 * Determine if we should pop given vlan before sending packets to the given port.
1252 *
1253 * @param portNumber port number
1254 * @param vlanId vlan id
1255 * @return true if the vlan id is not contained in any vlanTagged config
1256 */
1257 private boolean toPopVlan(PortNumber portNumber, VlanId vlanId) {
Saurav Das7bcbe702017-06-13 15:35:54 -07001258 return srManager.interfaceService
1259 .getInterfacesByPort(new ConnectPoint(deviceId, portNumber))
Charles Chan7ffd81f2017-02-08 15:52:08 -08001260 .stream().noneMatch(intf -> intf.vlanTagged().contains(vlanId));
1261 }
1262
1263 /**
Ruchi Sahota5d800282019-01-28 01:08:18 +00001264 * Create simple next objective for an indirect host mac/vlan. The treatments can include
1265 * all outgoing actions that need to happen on the packet.
1266 *
1267 * @param macAddr the mac address of the host
1268 * @param vlanId the vlan of the host
1269 * @param treatment the actions to apply on the packets (should include outport)
1270 * @param meta optional data to pass to the driver
1271 * @return next objective ID
1272 */
1273 public int createGroupFromMacVlan(MacAddress macAddr, VlanId vlanId, TrafficTreatment treatment,
1274 TrafficSelector meta) {
1275 int nextId = flowObjectiveService.allocateNextId();
1276 MacVlanNextObjectiveStoreKey key = new MacVlanNextObjectiveStoreKey(deviceId, macAddr, vlanId);
1277
1278 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1279 .builder().withId(nextId)
1280 .withType(NextObjective.Type.SIMPLE)
1281 .addTreatment(treatment)
1282 .fromApp(appId)
1283 .withMeta(meta);
1284
1285 ObjectiveContext context = new DefaultObjectiveContext(
1286 (objective) ->
1287 log.debug("createGroupFromMacVlan installed "
1288 + "NextObj {} on {}", nextId, deviceId),
1289 (objective, error) -> {
1290 log.warn("createGroupFromMacVlan failed to install NextObj {} on {}: {}", nextId, deviceId, error);
1291 srManager.invalidateNextObj(objective.id());
1292 });
1293 NextObjective nextObj = nextObjBuilder.add(context);
1294 flowObjectiveService.next(deviceId, nextObj);
1295 log.debug("createGroupFromMacVlan: Submited next objective {} in device {} "
1296 + "for host {}/{}", nextId, deviceId, macAddr, vlanId);
1297
1298 macVlanNextObjStore.put(key, nextId);
1299 return nextId;
1300 }
1301
1302 /**
Saurav Das4ce45962015-11-24 23:21:05 -08001303 * Create simple next objective for a single port. The treatments can include
1304 * all outgoing actions that need to happen on the packet.
1305 *
1306 * @param portNum the outgoing port on the device
1307 * @param treatment the actions to apply on the packets (should include outport)
1308 * @param meta optional data to pass to the driver
1309 */
1310 public void createGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
1311 TrafficSelector meta) {
1312 int nextId = flowObjectiveService.allocateNextId();
1313 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
Saurav Das76ae6812017-03-15 15:15:14 -07001314 deviceId, portNum, treatment, meta);
Saurav Das4ce45962015-11-24 23:21:05 -08001315
1316 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1317 .builder().withId(nextId)
1318 .withType(NextObjective.Type.SIMPLE)
1319 .addTreatment(treatment)
1320 .fromApp(appId)
1321 .withMeta(meta);
1322
Saurav Das961beb22017-03-29 19:09:17 -07001323 ObjectiveContext context = new DefaultObjectiveContext(
1324 (objective) ->
1325 log.debug("createGroupFromPort installed "
1326 + "NextObj {} on {}", nextId, deviceId),
Charles Chan1dbadbd2018-08-23 14:30:33 -07001327 (objective, error) -> {
1328 log.warn("createGroupFromPort failed to install NextObj {} on {}: {}", nextId, deviceId, error);
1329 srManager.invalidateNextObj(objective.id());
1330 });
Saurav Das961beb22017-03-29 19:09:17 -07001331 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Das4ce45962015-11-24 23:21:05 -08001332 flowObjectiveService.next(deviceId, nextObj);
1333 log.debug("createGroupFromPort: Submited next objective {} in device {} "
1334 + "for port {}", nextId, deviceId, portNum);
1335
1336 portNextObjStore.put(key, nextId);
1337 }
1338
sangho1e575652015-05-14 00:39:53 -07001339 /**
Jonghwan Hyun42fe1052017-08-25 17:48:36 -07001340 * Removes simple next objective for a single port.
1341 *
1342 * @param deviceId device id that has the port to deal with
1343 * @param portNum the outgoing port on the device
1344 * @param vlanId vlan id associated with the port
1345 * @param popVlan true if POP_VLAN action is applied on the packets, false otherwise
1346 */
1347 public void removePortNextObjective(DeviceId deviceId, PortNumber portNum, VlanId vlanId, boolean popVlan) {
1348 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
1349 mbuilder.matchVlanId(vlanId);
1350
1351 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
1352 tbuilder.immediate().setOutput(portNum);
1353 if (popVlan) {
1354 tbuilder.immediate().popVlan();
1355 }
1356
1357 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, portNum,
1358 tbuilder.build(), mbuilder.build(), false);
1359
1360 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
1361 deviceId, portNum, tbuilder.build(), mbuilder.build());
1362 if (portNextObjId != -1 && portNextObjStore.containsKey(key)) {
1363 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1364 .builder().withId(portNextObjId)
1365 .withType(NextObjective.Type.SIMPLE).fromApp(appId);
1366 ObjectiveContext context = new DefaultObjectiveContext(
1367 (objective) -> log.debug("removePortNextObjective removes NextObj {} on {}",
1368 portNextObjId, deviceId),
Charles Chan1dbadbd2018-08-23 14:30:33 -07001369 (objective, error) -> {
1370 log.warn("removePortNextObjective failed to remove NextObj {} on {}: {}",
1371 portNextObjId, deviceId, error);
1372 srManager.invalidateNextObj(objective.id());
1373 });
Jonghwan Hyun42fe1052017-08-25 17:48:36 -07001374 NextObjective nextObjective = nextObjBuilder.remove(context);
1375 log.info("**removePortNextObjective: Submitted "
1376 + "next objective {} in device {}",
1377 portNextObjId, deviceId);
1378 flowObjectiveService.next(deviceId, nextObjective);
1379
1380 portNextObjStore.remove(key);
1381 }
1382 }
Ruchi Sahota5d800282019-01-28 01:08:18 +00001383
Jonghwan Hyun42fe1052017-08-25 17:48:36 -07001384 /**
sangho1e575652015-05-14 00:39:53 -07001385 * Removes groups for the next objective ID given.
1386 *
1387 * @param objectiveId next objective ID to remove
1388 * @return true if succeeds, false otherwise
1389 */
1390 public boolean removeGroup(int objectiveId) {
Saurav Das7bcbe702017-06-13 15:35:54 -07001391 for (Map.Entry<DestinationSetNextObjectiveStoreKey, NextNeighbors> e :
1392 dsNextObjStore.entrySet()) {
1393 if (e.getValue().nextId() != objectiveId) {
1394 continue;
1395 }
Pier Luigi63edd932018-01-14 21:56:11 +01001396 // Right now it is just used in TunnelHandler
1397 // remember in future that PW transit groups could
1398 // be Indirect groups
sangho1e575652015-05-14 00:39:53 -07001399 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1400 .builder().withId(objectiveId)
1401 .withType(NextObjective.Type.HASHED).fromApp(appId);
Charles Chan216e3c82016-04-23 14:48:16 -07001402 ObjectiveContext context = new DefaultObjectiveContext(
1403 (objective) -> log.debug("RemoveGroup removes NextObj {} on {}",
1404 objectiveId, deviceId),
Charles Chan1dbadbd2018-08-23 14:30:33 -07001405 (objective, error) -> {
1406 log.warn("RemoveGroup failed to remove NextObj {} on {}: {}", objectiveId, deviceId, error);
1407 srManager.invalidateNextObj(objective.id());
1408 });
Charles Chan216e3c82016-04-23 14:48:16 -07001409 NextObjective nextObjective = nextObjBuilder.remove(context);
Saurav Das8a0732e2015-11-20 15:27:53 -08001410 log.info("**removeGroup: Submited "
1411 + "next objective {} in device {}",
1412 objectiveId, deviceId);
sangho1e575652015-05-14 00:39:53 -07001413 flowObjectiveService.next(deviceId, nextObjective);
1414
Saurav Das7bcbe702017-06-13 15:35:54 -07001415 dsNextObjStore.remove(e.getKey());
sangho0b2b6d12015-05-20 22:16:38 -07001416 return true;
sangho1e575652015-05-14 00:39:53 -07001417 }
1418
1419 return false;
1420 }
Jonghwan Hyun42fe1052017-08-25 17:48:36 -07001421 /**
1422 * Remove simple next objective for a single port. The treatments can include
1423 * all outgoing actions that need to happen on the packet.
1424 *
1425 * @param portNum the outgoing port on the device
1426 * @param treatment the actions applied on the packets (should include outport)
1427 * @param meta optional data to pass to the driver
1428 */
1429 public void removeGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
1430 TrafficSelector meta) {
1431 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
1432 deviceId, portNum, treatment, meta);
1433 Integer nextId = portNextObjStore.get(key);
1434
1435 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1436 .builder().withId(nextId)
1437 .withType(NextObjective.Type.SIMPLE)
1438 .addTreatment(treatment)
1439 .fromApp(appId)
1440 .withMeta(meta);
1441
1442 ObjectiveContext context = new DefaultObjectiveContext(
1443 (objective) ->
1444 log.info("removeGroupFromPort installed "
1445 + "NextObj {} on {}", nextId, deviceId),
Charles Chan1dbadbd2018-08-23 14:30:33 -07001446 (objective, error) -> {
1447 log.warn("removeGroupFromPort failed to install NextObj {} on {}: {}", nextId, deviceId, error);
1448 srManager.invalidateNextObj(objective.id());
1449 }
Jonghwan Hyun42fe1052017-08-25 17:48:36 -07001450 );
1451 NextObjective nextObj = nextObjBuilder.remove(context);
1452 flowObjectiveService.next(deviceId, nextObj);
1453 log.info("removeGroupFromPort: Submitted next objective {} in device {} "
1454 + "for port {}", nextId, deviceId, portNum);
1455
1456 portNextObjStore.remove(key);
1457 }
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -07001458
Charles Chane849c192016-01-11 18:28:54 -08001459 /**
1460 * Removes all groups from all next objective stores.
1461 */
Saurav Das7bcbe702017-06-13 15:35:54 -07001462 /*public void removeAllGroups() {
1463 for (Map.Entry<NeighborSetNextObjectiveStoreKey, NextNeighbors> entry:
Saurav Das423fe2b2015-12-04 10:52:59 -08001464 nsNextObjStore.entrySet()) {
Saurav Das7bcbe702017-06-13 15:35:54 -07001465 removeGroup(entry.getValue().nextId());
Saurav Das423fe2b2015-12-04 10:52:59 -08001466 }
1467 for (Map.Entry<PortNextObjectiveStoreKey, Integer> entry:
1468 portNextObjStore.entrySet()) {
1469 removeGroup(entry.getValue());
1470 }
Charles Chan59cc16d2017-02-02 16:20:42 -08001471 for (Map.Entry<VlanNextObjectiveStoreKey, Integer> entry:
1472 vlanNextObjStore.entrySet()) {
Saurav Das423fe2b2015-12-04 10:52:59 -08001473 removeGroup(entry.getValue());
1474 }
Saurav Das7bcbe702017-06-13 15:35:54 -07001475 }*/ //XXX revisit
1476
Saurav Dasceccf242017-08-03 18:30:35 -07001477 /**
1478 * Triggers a one time bucket verification operation on all hash groups
1479 * on this device.
1480 */
1481 public void triggerBucketCorrector() {
1482 BucketCorrector bc = new BucketCorrector();
1483 bc.run();
1484 }
1485
Jonghwan Hyunf810a7a2018-02-12 16:43:45 +09001486 /**
1487 * Modifies L2IG bucket when the interface configuration is updated, especially
1488 * when the interface has same VLAN ID but the VLAN type is changed (e.g., from
1489 * vlan-tagged [10] to vlan-untagged 10), which requires changes on
1490 * TrafficTreatment in turn.
1491 *
1492 * @param portNumber the port on this device that needs to be updated
1493 * @param vlanId the vlan id corresponding to this port
1494 * @param pushVlan indicates if packets should be sent out untagged or not out
1495 * from the port. If true, updated TrafficTreatment involves
1496 * pop vlan tag action. If false, updated TrafficTreatment
1497 * does not involve pop vlan tag action.
1498 */
1499 public void updateL2InterfaceGroupBucket(PortNumber portNumber, VlanId vlanId, boolean pushVlan) {
1500 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1501 if (pushVlan) {
1502 tBuilder.popVlan();
1503 }
1504 tBuilder.setOutput(portNumber);
1505
1506 TrafficSelector metadata =
1507 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
1508
1509 int nextId = getVlanNextObjectiveId(vlanId);
1510
1511 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1512 .builder().withId(nextId)
1513 .withType(NextObjective.Type.SIMPLE).fromApp(appId)
1514 .addTreatment(tBuilder.build())
1515 .withMeta(metadata);
1516
1517 ObjectiveContext context = new DefaultObjectiveContext(
1518 (objective) -> log.debug("port {} successfully updated NextObj {} on {}",
1519 portNumber, nextId, deviceId),
Charles Chan1dbadbd2018-08-23 14:30:33 -07001520 (objective, error) -> {
1521 log.warn("port {} failed to updated NextObj {} on {}: {}", portNumber, nextId, deviceId, error);
1522 srManager.invalidateNextObj(objective.id());
1523 });
Jonghwan Hyunf810a7a2018-02-12 16:43:45 +09001524
1525 flowObjectiveService.next(deviceId, nextObjBuilder.modify(context));
Jonghwan Hyun42fe1052017-08-25 17:48:36 -07001526 }
1527
Jonghwan Hyunf810a7a2018-02-12 16:43:45 +09001528 /**
Ruchi Sahota5d800282019-01-28 01:08:18 +00001529 * Updates the next objective for the given nextId .
1530 *
1531 * @param hostMac mac of host for which Next obj is to be updated.
1532 * @param hostVlanId vlan of host for which Next obj is to be updated.
1533 * @param port port with which to update the Next Obj.
1534 * @param nextId of Next Obj which needs to be updated.
1535 */
1536 public void updateL3UcastGroupBucket(MacAddress hostMac, VlanId hostVlanId, PortNumber port, int nextId) {
1537
1538 MacAddress deviceMac;
1539 try {
1540 deviceMac = deviceConfig.getDeviceMac(deviceId);
1541 } catch (DeviceConfigNotFoundException e) {
1542 log.warn(e.getMessage() + " in updateL3UcastGroupBucket");
1543 return;
1544 }
1545
1546 TrafficSelector metadata =
1547 DefaultTrafficSelector.builder().matchVlanId(hostVlanId).build();
1548
1549 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
1550 tbuilder.deferred()
1551 .setEthDst(hostMac)
1552 .setEthSrc(deviceMac)
1553 .setVlanId(hostVlanId)
1554 .setOutput(port);
1555
1556 log.debug(" update L3Ucast : deviceMac {}, port {}, host {}/{}, nextid {}, Treatment {} Meta {}",
1557 deviceMac, port, hostMac, hostVlanId, nextId, tbuilder.build(), metadata);
1558
1559 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1560 .builder().withId(nextId)
1561 .withType(NextObjective.Type.SIMPLE).fromApp(appId)
1562 .addTreatment(tbuilder.build())
1563 .withMeta(metadata);
1564
1565 ObjectiveContext context = new DefaultObjectiveContext(
1566 (objective) -> log.debug(" NextId {} successfully updated host {} vlan {} with port {}",
1567 nextId, hostMac, hostVlanId, port),
1568 (objective, error) -> {
1569 log.warn(" NextId {} failed to update host {} vlan {} with port {}, error : {}",
1570 nextId, hostMac, hostVlanId, port, error);
1571 srManager.invalidateNextObj(objective.id());
1572 });
1573
1574 NextObjective nextObj = nextObjBuilder.modify(context);
1575 flowObjectiveService.next(deviceId, nextObj);
1576
1577 }
1578
1579 /**
Jonghwan Hyunf810a7a2018-02-12 16:43:45 +09001580 * Adds a single port to the L2FG or removes it from the L2FG.
1581 *
1582 * @param vlanId the vlan id corresponding to this port
1583 * @param portNum the port on this device to be updated
1584 * @param nextId the next objective ID for the given vlan id
1585 * @param install if true, adds the port to L2FG. If false, removes it from L2FG.
1586 */
1587 public void updateGroupFromVlanConfiguration(VlanId vlanId, PortNumber portNum, int nextId, boolean install) {
Jonghwan Hyun42fe1052017-08-25 17:48:36 -07001588 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1589 if (toPopVlan(portNum, vlanId)) {
1590 tBuilder.popVlan();
1591 }
1592 tBuilder.setOutput(portNum);
1593
1594 TrafficSelector metadata =
1595 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
1596
1597 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1598 .builder().withId(nextId)
1599 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1600 .addTreatment(tBuilder.build())
1601 .withMeta(metadata);
1602
1603 ObjectiveContext context = new DefaultObjectiveContext(
1604 (objective) -> log.debug("port {} successfully removedFrom NextObj {} on {}",
1605 portNum, nextId, deviceId),
Charles Chan1dbadbd2018-08-23 14:30:33 -07001606 (objective, error) -> {
1607 log.warn("port {} failed to removedFrom NextObj {} on {}: {}", portNum, nextId, deviceId, error);
1608 srManager.invalidateNextObj(objective.id());
1609 });
Jonghwan Hyun42fe1052017-08-25 17:48:36 -07001610
1611 if (install) {
1612 flowObjectiveService.next(deviceId, nextObjBuilder.addToExisting(context));
1613 } else {
1614 flowObjectiveService.next(deviceId, nextObjBuilder.removeFromExisting(context));
1615 }
1616 }
Saurav Das1547b3f2017-05-05 17:01:08 -07001617
1618 /**
Saurav Das9df5b7c2017-08-14 16:44:43 -07001619 * Performs bucket verification operation for all hash groups in this device.
1620 * Checks RouteHandler to ensure that routing is stable before attempting
1621 * verification. Verification involves creating a nextObjective with
1622 * operation VERIFY for existing next objectives in the store, and passing
1623 * it to the driver. It is the driver that actually performs the verification
1624 * by adding or removing buckets to match the verification next objective
1625 * created here.
Saurav Das1547b3f2017-05-05 17:01:08 -07001626 */
Saurav Dasceccf242017-08-03 18:30:35 -07001627 protected final class BucketCorrector implements Runnable {
1628 Integer nextId;
Saurav Das1547b3f2017-05-05 17:01:08 -07001629
Saurav Dasceccf242017-08-03 18:30:35 -07001630 BucketCorrector() {
1631 this.nextId = null;
1632 }
1633
1634 BucketCorrector(Integer nextId) {
1635 this.nextId = nextId;
Saurav Das1547b3f2017-05-05 17:01:08 -07001636 }
1637
1638 @Override
1639 public void run() {
Saurav Dasceccf242017-08-03 18:30:35 -07001640 if (!srManager.mastershipService.isLocalMaster(deviceId)) {
1641 return;
Saurav Das1547b3f2017-05-05 17:01:08 -07001642 }
Saurav Dasceccf242017-08-03 18:30:35 -07001643 DefaultRoutingHandler rh = srManager.getRoutingHandler();
1644 if (rh == null) {
1645 return;
1646 }
1647 if (!rh.isRoutingStable()) {
1648 return;
1649 }
1650 rh.acquireRoutingLock();
1651 try {
Saurav Das9df5b7c2017-08-14 16:44:43 -07001652 log.trace("running bucket corrector for dev: {}", deviceId);
Saurav Dasceccf242017-08-03 18:30:35 -07001653 Set<DestinationSetNextObjectiveStoreKey> dsKeySet = dsNextObjStore.entrySet()
1654 .stream()
1655 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
Pier Luigi63edd932018-01-14 21:56:11 +01001656 // Filter out PW transit groups or include them if MPLS ECMP is supported
Saurav Dasa4020382018-02-14 14:14:54 -08001657 .filter(entry -> !entry.getKey().destinationSet().notBos() ||
1658 (entry.getKey().destinationSet().notBos() && srManager.getMplsEcmp()))
1659 // Filter out simple SWAP groups or include them if MPLS ECMP is supported
1660 .filter(entry -> !entry.getKey().destinationSet().swap() ||
1661 (entry.getKey().destinationSet().swap() && srManager.getMplsEcmp()))
Saurav Dasceccf242017-08-03 18:30:35 -07001662 .map(entry -> entry.getKey())
1663 .collect(Collectors.toSet());
1664 for (DestinationSetNextObjectiveStoreKey dsKey : dsKeySet) {
1665 NextNeighbors next = dsNextObjStore.get(dsKey);
1666 if (next == null) {
1667 continue;
1668 }
1669 int nid = next.nextId();
1670 if (nextId != null && nextId != nid) {
1671 continue;
1672 }
Saurav Das9df5b7c2017-08-14 16:44:43 -07001673 log.trace("bkt-corr: dsNextObjStore for device {}: {}",
Saurav Dasceccf242017-08-03 18:30:35 -07001674 deviceId, dsKey, next);
1675 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
Saurav Das3c82f192018-08-13 15:34:26 -07001676 metabuilder.matchVlanId(srManager.getDefaultInternalVlan());
Saurav Dasceccf242017-08-03 18:30:35 -07001677 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
1678 .withId(nid)
1679 .withType(NextObjective.Type.HASHED)
1680 .withMeta(metabuilder.build())
1681 .fromApp(appId);
1682
1683 next.dstNextHops().forEach((dstDev, nextHops) -> {
1684 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dstDev);
1685 nextHops.forEach(neighbor -> {
1686 MacAddress neighborMac;
1687 try {
1688 neighborMac = deviceConfig.getDeviceMac(neighbor);
1689 } catch (DeviceConfigNotFoundException e) {
1690 log.warn(e.getMessage() + " Aborting neighbor"
1691 + neighbor);
1692 return;
1693 }
1694 devicePortMap.get(neighbor).forEach(port -> {
Saurav Das9df5b7c2017-08-14 16:44:43 -07001695 log.trace("verify in device {} nextId {}: bucket with"
Saurav Dasceccf242017-08-03 18:30:35 -07001696 + " port/label {}/{} to dst {} via {}",
1697 deviceId, nid, port, edgeLabel,
1698 dstDev, neighbor);
Saurav Dasa4020382018-02-14 14:14:54 -08001699 nextObjBuilder
1700 .addTreatment(treatmentBuilder(port,
1701 neighborMac,
1702 dsKey.destinationSet().swap(),
1703 edgeLabel));
Saurav Dasceccf242017-08-03 18:30:35 -07001704 });
1705 });
1706 });
1707
1708 NextObjective nextObjective = nextObjBuilder.verify();
1709 flowObjectiveService.next(deviceId, nextObjective);
1710 }
1711 } finally {
1712 rh.releaseRoutingLock();
1713 }
1714
1715 }
1716
1717 TrafficTreatment treatmentBuilder(PortNumber outport, MacAddress dstMac,
Saurav Dasa4020382018-02-14 14:14:54 -08001718 boolean swap, int edgeLabel) {
Saurav Dasceccf242017-08-03 18:30:35 -07001719 TrafficTreatment.Builder tBuilder =
1720 DefaultTrafficTreatment.builder();
1721 tBuilder.setOutput(outport)
1722 .setEthDst(dstMac)
1723 .setEthSrc(nodeMacAddr);
1724 if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
Saurav Dasa4020382018-02-14 14:14:54 -08001725 if (swap) {
1726 // swap label case
1727 tBuilder.setMpls(MplsLabel.mplsLabel(edgeLabel));
1728 } else {
1729 // ecmp with label push case
1730 tBuilder.pushMpls()
1731 .copyTtlOut()
1732 .setMpls(MplsLabel.mplsLabel(edgeLabel));
1733 }
Saurav Dasceccf242017-08-03 18:30:35 -07001734 }
1735 return tBuilder.build();
Saurav Das1547b3f2017-05-05 17:01:08 -07001736 }
1737 }
1738
Pier Luigi63edd932018-01-14 21:56:11 +01001739}