blob: 85b0c76b21e69ab805b882e80e83a9a34c3fe1e9 [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
Charles Chan59cc16d2017-02-02 16:20:42 -080018
Saurav Das7bcbe702017-06-13 15:35:54 -070019import com.google.common.collect.ImmutableSet;
Pier Ventre917127a2016-10-31 16:49:19 -070020import com.google.common.collect.Iterables;
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 Das423fe2b2015-12-04 10:52:59 -080043import org.onosproject.segmentrouting.SegmentRoutingManager;
Charles Chan0b4e6182015-11-03 10:42:14 -080044import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
45import org.onosproject.segmentrouting.config.DeviceProperties;
Saurav Das7bcbe702017-06-13 15:35:54 -070046import org.onosproject.segmentrouting.storekey.DestinationSetNextObjectiveStoreKey;
Charles Chand2990362016-04-18 13:44:03 -070047import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
Charles Chan59cc16d2017-02-02 16:20:42 -080048import org.onosproject.segmentrouting.storekey.VlanNextObjectiveStoreKey;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070049import org.onosproject.store.service.EventuallyConsistentMap;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080050import org.slf4j.Logger;
51
Pier Ventre917127a2016-10-31 16:49:19 -070052import java.net.URI;
Charles Chan7ffd81f2017-02-08 15:52:08 -080053import java.util.Collection;
Pier Ventre917127a2016-10-31 16:49:19 -070054import java.util.Collections;
Saurav Dasc88d4662017-05-15 15:34:25 -070055import java.util.HashMap;
Pier Ventre917127a2016-10-31 16:49:19 -070056import java.util.HashSet;
57import java.util.List;
58import java.util.Map;
59import java.util.Set;
60import java.util.concurrent.ConcurrentHashMap;
Saurav Das1547b3f2017-05-05 17:01:08 -070061import java.util.concurrent.ScheduledExecutorService;
62import java.util.concurrent.TimeUnit;
Pier Ventre917127a2016-10-31 16:49:19 -070063import java.util.stream.Collectors;
64
65import static com.google.common.base.Preconditions.checkNotNull;
Saurav Das1547b3f2017-05-05 17:01:08 -070066import static java.util.concurrent.Executors.newScheduledThreadPool;
67import static org.onlab.util.Tools.groupedThreads;
Charles Chan59cc16d2017-02-02 16:20:42 -080068import static org.onosproject.segmentrouting.SegmentRoutingManager.INTERNAL_VLAN;
Pier Ventre917127a2016-10-31 16:49:19 -070069import static org.slf4j.LoggerFactory.getLogger;
70
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080071/**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070072 * Default ECMP group handler creation module. This component creates a set of
73 * ECMP groups for every neighbor that this device is connected to based on
74 * whether the current device is an edge device or a transit device.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080075 */
76public class DefaultGroupHandler {
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -070077 protected static final Logger log = getLogger(DefaultGroupHandler.class);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080078
79 protected final DeviceId deviceId;
80 protected final ApplicationId appId;
81 protected final DeviceProperties deviceConfig;
82 protected final List<Integer> allSegmentIds;
Pier Ventree0ae7a32016-11-23 09:57:42 -080083 protected int ipv4NodeSegmentId = -1;
84 protected int ipv6NodeSegmentId = -1;
Charles Chan0b4e6182015-11-03 10:42:14 -080085 protected boolean isEdgeRouter = false;
86 protected MacAddress nodeMacAddr = null;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080087 protected LinkService linkService;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070088 protected FlowObjectiveService flowObjectiveService;
Saurav Dasc88d4662017-05-15 15:34:25 -070089 /**
90 * local store for neighbor-device-ids and the set of ports on this device
91 * that connect to the same neighbor.
92 */
Saurav Das8a0732e2015-11-20 15:27:53 -080093 protected ConcurrentHashMap<DeviceId, Set<PortNumber>> devicePortMap =
94 new ConcurrentHashMap<>();
Saurav Dasc88d4662017-05-15 15:34:25 -070095 /**
96 * local store for ports on this device connected to neighbor-device-id.
97 */
Saurav Das8a0732e2015-11-20 15:27:53 -080098 protected ConcurrentHashMap<PortNumber, DeviceId> portDeviceMap =
99 new ConcurrentHashMap<>();
Saurav Dasc88d4662017-05-15 15:34:25 -0700100
Saurav Das7bcbe702017-06-13 15:35:54 -0700101 // distributed store for (device+destination-set) mapped to next-id and neighbors
102 protected EventuallyConsistentMap<DestinationSetNextObjectiveStoreKey, NextNeighbors>
103 dsNextObjStore = null;
Saurav Das1a129a02016-11-18 15:21:57 -0800104 // distributed store for (device+subnet-ip-prefix) mapped to next-id
Charles Chan59cc16d2017-02-02 16:20:42 -0800105 protected EventuallyConsistentMap<VlanNextObjectiveStoreKey, Integer>
106 vlanNextObjStore = null;
Saurav Das1a129a02016-11-18 15:21:57 -0800107 // distributed store for (device+port+treatment) mapped to next-id
Charles Chane849c192016-01-11 18:28:54 -0800108 protected EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
109 portNextObjStore = null;
Charles Chan188ebf52015-12-23 00:15:11 -0800110 private SegmentRoutingManager srManager;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800111
Saurav Das1547b3f2017-05-05 17:01:08 -0700112 private static final long RETRY_INTERVAL_SEC = 30;
113 private ScheduledExecutorService executorService
114 = newScheduledThreadPool(1, groupedThreads("retryhashbkts", "retry-%d", log));
115
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700116 protected KryoNamespace.Builder kryo = new KryoNamespace.Builder()
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700117 .register(URI.class).register(HashSet.class)
Saurav Das7bcbe702017-06-13 15:35:54 -0700118 .register(DeviceId.class).register(PortNumber.class)
119 .register(DestinationSet.class).register(PolicyGroupIdentifier.class)
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700120 .register(PolicyGroupParams.class)
121 .register(GroupBucketIdentifier.class)
122 .register(GroupBucketIdentifier.BucketOutputType.class);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800123
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700124 protected DefaultGroupHandler(DeviceId deviceId, ApplicationId appId,
125 DeviceProperties config,
126 LinkService linkService,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700127 FlowObjectiveService flowObjService,
Charles Chan188ebf52015-12-23 00:15:11 -0800128 SegmentRoutingManager srManager) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800129 this.deviceId = checkNotNull(deviceId);
130 this.appId = checkNotNull(appId);
131 this.deviceConfig = checkNotNull(config);
132 this.linkService = checkNotNull(linkService);
Charles Chan0b4e6182015-11-03 10:42:14 -0800133 this.allSegmentIds = checkNotNull(config.getAllDeviceSegmentIds());
134 try {
Pier Ventree0ae7a32016-11-23 09:57:42 -0800135 this.ipv4NodeSegmentId = config.getIPv4SegmentId(deviceId);
136 this.ipv6NodeSegmentId = config.getIPv6SegmentId(deviceId);
Charles Chan0b4e6182015-11-03 10:42:14 -0800137 this.isEdgeRouter = config.isEdgeDevice(deviceId);
138 this.nodeMacAddr = checkNotNull(config.getDeviceMac(deviceId));
139 } catch (DeviceConfigNotFoundException e) {
140 log.warn(e.getMessage()
141 + " Skipping value assignment in DefaultGroupHandler");
142 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700143 this.flowObjectiveService = flowObjService;
Saurav Das7bcbe702017-06-13 15:35:54 -0700144 this.dsNextObjStore = srManager.dsNextObjStore();
Ray Milkeye4afdb52017-04-05 09:42:04 -0700145 this.vlanNextObjStore = srManager.vlanNextObjStore();
146 this.portNextObjStore = srManager.portNextObjStore();
Charles Chan188ebf52015-12-23 00:15:11 -0800147 this.srManager = srManager;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800148
149 populateNeighborMaps();
150 }
151
152 /**
Saurav Dasc88d4662017-05-15 15:34:25 -0700153 * Creates a group handler object.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800154 *
155 * @param deviceId device identifier
156 * @param appId application identifier
157 * @param config interface to retrieve the device properties
158 * @param linkService link service object
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700159 * @param flowObjService flow objective service object
Charles Chane849c192016-01-11 18:28:54 -0800160 * @param srManager segment routing manager
Charles Chan0b4e6182015-11-03 10:42:14 -0800161 * @throws DeviceConfigNotFoundException if the device configuration is not found
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800162 * @return default group handler type
163 */
Saurav Das4ce45962015-11-24 23:21:05 -0800164 public static DefaultGroupHandler createGroupHandler(
165 DeviceId deviceId,
166 ApplicationId appId,
167 DeviceProperties config,
168 LinkService linkService,
169 FlowObjectiveService flowObjService,
Charles Chan188ebf52015-12-23 00:15:11 -0800170 SegmentRoutingManager srManager)
Saurav Das4ce45962015-11-24 23:21:05 -0800171 throws DeviceConfigNotFoundException {
Saurav Dasc88d4662017-05-15 15:34:25 -0700172 return new DefaultGroupHandler(deviceId, appId, config,
173 linkService,
174 flowObjService,
175 srManager);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800176 }
177
178 /**
Saurav Dasc88d4662017-05-15 15:34:25 -0700179 * Updates local stores for link-src device/port to neighbor (link-dst).
180 *
181 * @param link the infrastructure link
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800182 */
Saurav Dasc88d4662017-05-15 15:34:25 -0700183 public void portUpForLink(Link link) {
184 if (!link.src().deviceId().equals(deviceId)) {
185 log.warn("linkUp: deviceId{} doesn't match with link src {}",
186 deviceId, link.src().deviceId());
187 return;
188 }
189
190 log.info("* portUpForLink: Device {} linkUp at local port {} to "
191 + "neighbor {}", deviceId, link.src().port(), link.dst().deviceId());
192 // ensure local state is updated even if linkup is aborted later on
193 addNeighborAtPort(link.dst().deviceId(),
194 link.src().port());
195 }
196
197 /**
198 * Updates local stores for port that has gone down.
199 *
200 * @param port port number that has gone down
201 */
202 public void portDown(PortNumber port) {
203 if (portDeviceMap.get(port) == null) {
204 log.warn("portDown: unknown port");
205 return;
206 }
207
208 log.debug("Device {} portDown {} to neighbor {}", deviceId, port,
209 portDeviceMap.get(port));
210 devicePortMap.get(portDeviceMap.get(port)).remove(port);
211 portDeviceMap.remove(port);
212 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800213
214 /**
Saurav Dasc88d4662017-05-15 15:34:25 -0700215 * Checks all groups in the src-device of link for neighbor sets that include
216 * the dst-device of link, and edits the hash groups according to link up
217 * or down. Should only be called by the master instance of the src-switch
218 * of link. Typically used when there are no route-path changes due to the
219 * link up or down, as the ECMPspg does not change.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800220 *
Saurav Dasc88d4662017-05-15 15:34:25 -0700221 * @param link the infrastructure link that has gone down or come up
222 * @param linkDown true if link has gone down
223 * @param firstTime true if link has come up for the first time i.e a link
224 * not seen-before
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800225 */
Saurav Dasc88d4662017-05-15 15:34:25 -0700226 public void retryHash(Link link, boolean linkDown, boolean firstTime) {
Charles Chan0b4e6182015-11-03 10:42:14 -0800227 MacAddress dstMac;
228 try {
Saurav Dasc88d4662017-05-15 15:34:25 -0700229 dstMac = deviceConfig.getDeviceMac(link.dst().deviceId());
Charles Chan0b4e6182015-11-03 10:42:14 -0800230 } catch (DeviceConfigNotFoundException e) {
Saurav Dasc88d4662017-05-15 15:34:25 -0700231 log.warn(e.getMessage() + " Aborting retryHash.");
Charles Chan0b4e6182015-11-03 10:42:14 -0800232 return;
233 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700234 // find all the destinationSets related to link
235 Set<DestinationSetNextObjectiveStoreKey> dsKeySet = dsNextObjStore.entrySet()
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700236 .stream()
Saurav Das7bcbe702017-06-13 15:35:54 -0700237 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
238 .filter(entry -> entry.getValue().containsNextHop(link.dst().deviceId()))
239 .map(entry -> entry.getKey())
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700240 .collect(Collectors.toSet());
Saurav Dasc88d4662017-05-15 15:34:25 -0700241
Saurav Das7bcbe702017-06-13 15:35:54 -0700242 log.debug("retryHash: dsNextObjStore contents for linkSrc {} -> linkDst {}: {}",
243 deviceId, link.dst().deviceId(), dsKeySet);
244
245 for (DestinationSetNextObjectiveStoreKey dsKey : dsKeySet) {
246 NextNeighbors nextHops = dsNextObjStore.get(dsKey);
247 if (nextHops == null) {
Saurav Dasc88d4662017-05-15 15:34:25 -0700248 log.warn("retryHash in device {}, but global store has no record "
Saurav Das7bcbe702017-06-13 15:35:54 -0700249 + "for dsKey:{}", deviceId, dsKey);
Saurav Dasc88d4662017-05-15 15:34:25 -0700250 continue;
251 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700252 int nextId = nextHops.nextId();
253 Set<DeviceId> dstSet = nextHops.getDstForNextHop(link.dst().deviceId());
Saurav Dasc88d4662017-05-15 15:34:25 -0700254 if (!linkDown) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700255 dstSet.forEach(dst -> {
256 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
257 addToHashedNextObjective(link.src().port(), dstMac,
258 edgeLabel, nextId, false);
259 });
260
Saurav Dasc88d4662017-05-15 15:34:25 -0700261 if (firstTime) {
262 // some links may have come up before the next-objective was created
263 // we take this opportunity to ensure other ports to same next-hop-dst
264 // are part of the hash group (see CORD-1180). Duplicate additions
265 // to the same hash group are avoided by the driver.
266 for (PortNumber p : devicePortMap.get(link.dst().deviceId())) {
267 if (p.equals(link.src().port())) {
268 continue;
269 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700270 dstSet.forEach(dst -> {
271 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
272 addToHashedNextObjective(p, dstMac, edgeLabel,
273 nextId, false);
274 });
Saurav Dasc88d4662017-05-15 15:34:25 -0700275 }
276 }
277 } else {
Saurav Das7bcbe702017-06-13 15:35:54 -0700278 dstSet.forEach(dst -> {
279 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
280 removeFromHashedNextObjective(link.src().port(), dstMac,
281 edgeLabel, nextId);
282 });
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700283 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800284 }
Saurav Das1547b3f2017-05-05 17:01:08 -0700285
286 // It's possible that at the time of linkup, some hash-groups have
287 // not been created yet by the instance responsible for creating them, or
288 // due to the eventually-consistent nature of the nsNextObjStore it has
289 // not synced up with this instance yet. Thus we perform this check again
290 // after a delay (see CORD-1180). Duplicate additions to the same hash group
291 // are avoided by the driver.
Saurav Dasc88d4662017-05-15 15:34:25 -0700292 if (!linkDown && firstTime) {
293 executorService.schedule(new RetryHashBkts(link, dstMac),
Saurav Das1547b3f2017-05-05 17:01:08 -0700294 RETRY_INTERVAL_SEC, TimeUnit.SECONDS);
295 }
296 }
297
Saurav Dasc88d4662017-05-15 15:34:25 -0700298 /**
299 * Makes a call to the FlowObjective service to add a single bucket to
300 * a hashed group.
301 *
302 * @param outport port to add to hash group
303 * @param dstMac destination mac address of next-hop
Saurav Das7bcbe702017-06-13 15:35:54 -0700304 * @param edgeLabel the label to use in the bucket
Saurav Dasc88d4662017-05-15 15:34:25 -0700305 * @param nextId id for next-objective to which the bucket will be added
306 * @param retry indicates if this method is being called on a retry attempt
307 * at adding a bucket to the group
308 */
Saurav Das1547b3f2017-05-05 17:01:08 -0700309 private void addToHashedNextObjective(PortNumber outport, MacAddress dstMac,
Saurav Das7bcbe702017-06-13 15:35:54 -0700310 int edgeLabel, Integer nextId, boolean retry) {
Saurav Das1547b3f2017-05-05 17:01:08 -0700311 // Create the new bucket to be updated
312 TrafficTreatment.Builder tBuilder =
313 DefaultTrafficTreatment.builder();
314 tBuilder.setOutput(outport)
315 .setEthDst(dstMac)
316 .setEthSrc(nodeMacAddr);
Saurav Das7bcbe702017-06-13 15:35:54 -0700317 if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
Saurav Das1547b3f2017-05-05 17:01:08 -0700318 tBuilder.pushMpls()
319 .copyTtlOut()
Saurav Das7bcbe702017-06-13 15:35:54 -0700320 .setMpls(MplsLabel.mplsLabel(edgeLabel));
Saurav Das1547b3f2017-05-05 17:01:08 -0700321 }
322 // setup metadata to pass to nextObjective - indicate the vlan on egress
323 // if needed by the switch pipeline. Since hashed next-hops are always to
324 // other neighboring routers, there is no subnet assigned on those ports.
325 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
326 metabuilder.matchVlanId(INTERNAL_VLAN);
327
328 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
329 .withId(nextId)
330 .withType(NextObjective.Type.HASHED)
331 .addTreatment(tBuilder.build())
332 .withMeta(metabuilder.build())
333 .fromApp(appId);
Saurav Das7bcbe702017-06-13 15:35:54 -0700334 log.debug("{} in device {}: Adding Bucket with port/label {}/{} to nextId {}",
Saurav Dasc88d4662017-05-15 15:34:25 -0700335 (retry) ? "retry-addToHash" : "addToHash",
Saurav Das7bcbe702017-06-13 15:35:54 -0700336 deviceId, outport, edgeLabel, nextId);
Saurav Das1547b3f2017-05-05 17:01:08 -0700337
338 ObjectiveContext context = new DefaultObjectiveContext(
Saurav Dasc88d4662017-05-15 15:34:25 -0700339 (objective) -> log.debug("{} addedTo NextObj {} on {}",
340 (retry) ? "retry-addToHash" : "addToHash",
341 nextId, deviceId),
Saurav Das1547b3f2017-05-05 17:01:08 -0700342 (objective, error) ->
Saurav Dasc88d4662017-05-15 15:34:25 -0700343 log.warn("{} failed to addTo NextObj {} on {}: {}",
344 (retry) ? "retry-addToHash" : "addToHash",
345 nextId, deviceId, error));
Saurav Das1547b3f2017-05-05 17:01:08 -0700346 NextObjective nextObjective = nextObjBuilder.addToExisting(context);
347 flowObjectiveService.next(deviceId, nextObjective);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800348 }
349
350 /**
Saurav Dasc88d4662017-05-15 15:34:25 -0700351 * Makes a call to the FlowObjective service to remove a single bucket from
352 * a hashed group.
353 *
354 * @param port port to remove from hash group
355 * @param dstMac destination mac address of next-hop
Saurav Das7bcbe702017-06-13 15:35:54 -0700356 * @param edgeLabel the label to use in the bucket
Saurav Dasc88d4662017-05-15 15:34:25 -0700357 * @param nextId id for next-objective from which the bucket will be removed
358 */
359 private void removeFromHashedNextObjective(PortNumber port, MacAddress dstMac,
Saurav Das7bcbe702017-06-13 15:35:54 -0700360 int edgeLabel, Integer nextId) {
Saurav Dasc88d4662017-05-15 15:34:25 -0700361 // Create the bucket to be removed
362 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
363 .builder();
364 tBuilder.setOutput(port)
365 .setEthDst(dstMac)
366 .setEthSrc(nodeMacAddr);
Saurav Das7bcbe702017-06-13 15:35:54 -0700367 if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
Saurav Dasc88d4662017-05-15 15:34:25 -0700368 tBuilder.pushMpls()
369 .copyTtlOut()
Saurav Das7bcbe702017-06-13 15:35:54 -0700370 .setMpls(MplsLabel.mplsLabel(edgeLabel));
Saurav Dasc88d4662017-05-15 15:34:25 -0700371 }
372 log.info("{} in device {}: Removing Bucket with Port {} to next object id {}",
373 "removeFromHash", deviceId, port, nextId);
374 NextObjective.Builder nextObjBuilder = DefaultNextObjective
375 .builder()
376 .withType(NextObjective.Type.HASHED) //same as original
377 .withId(nextId)
378 .fromApp(appId)
379 .addTreatment(tBuilder.build());
380 ObjectiveContext context = new DefaultObjectiveContext(
381 (objective) -> log.debug("port {} removedFrom NextObj {} on {}",
382 port, nextId, deviceId),
383 (objective, error) ->
384 log.warn("port {} failed to removeFrom NextObj {} on {}: {}",
385 port, nextId, deviceId, error));
386 NextObjective nextObjective = nextObjBuilder.
387 removeFromExisting(context);
388
389 flowObjectiveService.next(deviceId, nextObjective);
390 }
391
392 /**
393 * Checks all the hash-groups in the target-switch meant for the destination
394 * switch, and either adds or removes buckets to make the neighbor-set
395 * match the given next-hops. Typically called by the master instance of the
396 * destination switch, which may be different from the master instance of the
397 * target switch where hash-group changes are made.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800398 *
Saurav Dasc88d4662017-05-15 15:34:25 -0700399 * @param targetSw the switch in which the hash groups will be edited
400 * @param nextHops the current next hops for the target switch to reach
401 * the dest sw
402 * @param destSw the destination switch
403 * @param revoke true if hash groups need to remove buckets from the
404 * the groups to match the current next hops
405 * @return true if calls are made to edit buckets, or if no edits are required
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800406 */
Saurav Dasc88d4662017-05-15 15:34:25 -0700407 public boolean fixHashGroups(DeviceId targetSw, Set<DeviceId> nextHops,
408 DeviceId destSw, boolean revoke) {
409 // temporary storage of keys to be updated
Saurav Das7bcbe702017-06-13 15:35:54 -0700410 Map<DestinationSetNextObjectiveStoreKey, Set<DeviceId>> tempStore =
Saurav Dasc88d4662017-05-15 15:34:25 -0700411 new HashMap<>();
412 boolean foundNextObjective = false;
Charles Chan0b4e6182015-11-03 10:42:14 -0800413
Saurav Das7bcbe702017-06-13 15:35:54 -0700414 // retrieve hash-groups meant for destSw, which have destinationSets
Saurav Dasc88d4662017-05-15 15:34:25 -0700415 // with different neighbors than the given next-hops
Saurav Das7bcbe702017-06-13 15:35:54 -0700416 for (DestinationSetNextObjectiveStoreKey dskey : dsNextObjStore.keySet()) {
417 if (!dskey.deviceId().equals(targetSw) ||
418 !dskey.destinationSet().getDestinationSwitches().contains(destSw)) {
Saurav Dasc88d4662017-05-15 15:34:25 -0700419 continue;
420 }
421 foundNextObjective = true;
Saurav Das7bcbe702017-06-13 15:35:54 -0700422 NextNeighbors nhops = dsNextObjStore.get(dskey);
423 Set<DeviceId> currNeighbors = nhops.nextHops(destSw);
424 int edgeLabel = dskey.destinationSet().getEdgeLabel(destSw);
425 Integer nextId = nhops.nextId();
Charles Chan0b4e6182015-11-03 10:42:14 -0800426
Saurav Dasc88d4662017-05-15 15:34:25 -0700427 Set<DeviceId> diff;
428 if (revoke) {
429 diff = Sets.difference(currNeighbors, nextHops);
430 log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next "
431 + "hops:{} ..removing {}", targetSw, destSw, nextId,
432 currNeighbors, diff);
433 } else {
434 diff = Sets.difference(nextHops, currNeighbors);
435 log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next "
436 + "hops:{} ..adding {}", targetSw, destSw, nextId,
437 currNeighbors, diff);
438 }
439 for (DeviceId neighbor : diff) {
440 MacAddress dstMac;
441 try {
442 dstMac = deviceConfig.getDeviceMac(neighbor);
443 } catch (DeviceConfigNotFoundException e) {
444 log.warn(e.getMessage() + " Aborting fixHashGroup for nextId:"
Saurav Das7bcbe702017-06-13 15:35:54 -0700445 + nextId);
Saurav Dasc88d4662017-05-15 15:34:25 -0700446 return false;
Saurav Das423fe2b2015-12-04 10:52:59 -0800447 }
Saurav Dasc88d4662017-05-15 15:34:25 -0700448 if (devicePortMap.get(neighbor) == null ||
449 devicePortMap.get(neighbor).isEmpty()) {
450 log.warn("No ports found in dev:{} for neighbor:{} .. cannot "
451 + "fix hash group for nextId: {}",
452 deviceId, neighbor, nextId);
453 return false;
454 }
455 if (revoke) {
456 for (PortNumber port : devicePortMap.get(neighbor)) {
457 log.info("fixHashGroup in device {}: Removing Bucket "
458 + "with Port {} to next object id {}",
459 deviceId, port, nextId);
460 removeFromHashedNextObjective(port, dstMac,
Saurav Das7bcbe702017-06-13 15:35:54 -0700461 edgeLabel,
Saurav Dasc88d4662017-05-15 15:34:25 -0700462 nextId);
463 }
464 // to update neighbor set with changes made
Saurav Das7bcbe702017-06-13 15:35:54 -0700465 tempStore.put(dskey, Sets.difference(currNeighbors, diff));
Saurav Dasc88d4662017-05-15 15:34:25 -0700466 } else {
467 for (PortNumber port : devicePortMap.get(neighbor)) {
468 log.info("fixHashGroup in device {}: Adding Bucket "
469 + "with Port {} to next object id {}",
470 deviceId, port, nextId);
471 addToHashedNextObjective(port, dstMac,
Saurav Das7bcbe702017-06-13 15:35:54 -0700472 edgeLabel,
Saurav Dasc88d4662017-05-15 15:34:25 -0700473 nextId, false);
474 }
475 // to update neighbor set with changes made
Saurav Das7bcbe702017-06-13 15:35:54 -0700476 tempStore.put(dskey, Sets.union(currNeighbors, diff));
Saurav Dasc88d4662017-05-15 15:34:25 -0700477 }
sangho834e4b02015-05-01 09:38:25 -0700478 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800479 }
480
Saurav Dasc88d4662017-05-15 15:34:25 -0700481 if (!foundNextObjective) {
482 log.debug("Cannot find any nextObjectives for route targetSw:{} "
483 + "-> dstSw:{}", targetSw, destSw);
484 return true; // nothing to do, return true so ECMPspg is updated
485 }
486
Saurav Das7bcbe702017-06-13 15:35:54 -0700487 // update the dsNextObjectiveStore with new destinationSet to nextId mappings
488 for (DestinationSetNextObjectiveStoreKey key : tempStore.keySet()) {
489 NextNeighbors oldHops = dsNextObjStore.get(key);
490 if (oldHops == null) {
Saurav Dasc88d4662017-05-15 15:34:25 -0700491 continue;
492 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700493 Set<DeviceId> newNeighbors = tempStore.get(key);
494 Set<DeviceId> oldNeighbors = ImmutableSet.copyOf(oldHops.nextHops(destSw));
495 oldHops.dstNextHops().put(destSw, newNeighbors);
496 log.debug("Updating nsNextObjStore: oldHops:{} -> newHops:{} :: nextId:{}",
497 oldNeighbors, newNeighbors, oldHops.nextId());
Saurav Dasc88d4662017-05-15 15:34:25 -0700498 }
499
500 return true;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800501 }
502
Saurav Das7bcbe702017-06-13 15:35:54 -0700503
504 public boolean updateNextHops(DestinationSet ds,
505 Map<DeviceId, Set<DeviceId>> newDstNextHops) {
506 DestinationSetNextObjectiveStoreKey key =
507 new DestinationSetNextObjectiveStoreKey(deviceId, ds);
508 NextNeighbors currNext = dsNextObjStore.get(key);
509 Map<DeviceId, Set<DeviceId>> currDstNextHops = currNext.dstNextHops();
510
511 // add newDstNextHops to currDstNextHops for each dst
512 boolean success = true;
513 for (DeviceId dstSw : ds.getDestinationSwitches()) {
514 Set<DeviceId> currNhops = currDstNextHops.get(dstSw);
515 Set<DeviceId> newNhops = newDstNextHops.get(dstSw);
516 currNhops = (currNhops == null) ? Sets.newHashSet() : currNhops;
517 newNhops = (newNhops == null) ? Sets.newHashSet() : newNhops;
518 int edgeLabel = ds.getEdgeLabel(dstSw);
519 int nextId = currNext.nextId();
520
521 // new next hops should be added
522 boolean suc = updateAllPortsToNextHop(Sets.difference(newNhops, currNhops),
523 edgeLabel, nextId, false);
524 if (suc) {
525 currNhops.addAll(newNhops);
526 currDstNextHops.put(dstSw, currNhops); // this is only a local change
527 }
528 success &= suc;
529 }
530
531 if (success) {
532 // update global store
533 dsNextObjStore.put(key, new NextNeighbors(currDstNextHops,
534 currNext.nextId()));
535 log.debug("Updated device:{} ds:{} new next-hops: {}", deviceId, ds,
536 dsNextObjStore.get(key));
537 }
538 return success;
539 }
540
541 private boolean updateAllPortsToNextHop(Set<DeviceId> diff, int edgeLabel,
542 int nextId, boolean revoke) {
543 for (DeviceId neighbor : diff) {
544 MacAddress dstMac;
545 try {
546 dstMac = deviceConfig.getDeviceMac(neighbor);
547 } catch (DeviceConfigNotFoundException e) {
548 log.warn(e.getMessage() + " Aborting fixHashGroup for nextId:"
549 + nextId);
550 return false;
551 }
552 if (devicePortMap.get(neighbor) == null ||
553 devicePortMap.get(neighbor).isEmpty()) {
554 log.warn("No ports found in dev:{} for neighbor:{} .. cannot "
555 + "fix hash group for nextId: {}",
556 deviceId, neighbor, nextId);
557 return false;
558 }
559 if (revoke) {
560 for (PortNumber port : devicePortMap.get(neighbor)) {
561 log.debug("fixHashGroup in device {}: Removing Bucket "
562 + "with Port {} edgeLabel:{} to next object id {}",
563 deviceId, port, edgeLabel, nextId);
564 removeFromHashedNextObjective(port, dstMac,
565 edgeLabel,
566 nextId);
567 }
568 } else {
569 for (PortNumber port : devicePortMap.get(neighbor)) {
570 log.debug("fixHashGroup in device {}: Adding Bucket "
571 + "with Port {} edgeLabel: {} to next object id {}",
572 deviceId, port, edgeLabel, nextId);
573 addToHashedNextObjective(port, dstMac,
574 edgeLabel,
575 nextId, false);
576 }
577 }
578 }
579 return true;
580 }
581
582
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800583 /**
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800584 * Adds or removes a port that has been configured with a vlan to a broadcast group
585 * for bridging. Should only be called by the master instance for this device.
Saurav Das1a129a02016-11-18 15:21:57 -0800586 *
587 * @param port the port on this device that needs to be added/removed to a bcast group
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800588 * @param vlanId the vlan id corresponding to the broadcast domain/group
589 * @param popVlan indicates if packets should be sent out untagged or not out
590 * of the port. If true, indicates an access (untagged) or native vlan
591 * configuration. If false, indicates a trunk (tagged) vlan config.
Saurav Das1a129a02016-11-18 15:21:57 -0800592 * @param portUp true if port is enabled, false if disabled
Saurav Das1a129a02016-11-18 15:21:57 -0800593 */
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800594 public void processEdgePort(PortNumber port, VlanId vlanId,
595 boolean popVlan, boolean portUp) {
Saurav Das1a129a02016-11-18 15:21:57 -0800596 //get the next id for the subnet and edit it.
Charles Chan59cc16d2017-02-02 16:20:42 -0800597 Integer nextId = getVlanNextObjectiveId(vlanId);
Saurav Das1a129a02016-11-18 15:21:57 -0800598 if (nextId == -1) {
599 if (portUp) {
600 log.debug("**Creating flooding group for first port enabled in"
Charles Chan59cc16d2017-02-02 16:20:42 -0800601 + " subnet {} on dev {} port {}", vlanId, deviceId, port);
602 createBcastGroupFromVlan(vlanId, Collections.singleton(port));
Saurav Das1a129a02016-11-18 15:21:57 -0800603 } else {
604 log.warn("Could not find flooding group for subnet {} on dev:{} when"
Charles Chan59cc16d2017-02-02 16:20:42 -0800605 + " removing port:{}", vlanId, deviceId, port);
Saurav Das1a129a02016-11-18 15:21:57 -0800606 }
607 return;
608 }
609
610 log.info("**port{} in device {}: {} Bucket with Port {} to"
611 + " next-id {}", (portUp) ? "UP" : "DOWN", deviceId,
612 (portUp) ? "Adding" : "Removing",
613 port, nextId);
614 // Create the bucket to be added or removed
615 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800616 if (popVlan) {
617 tBuilder.popVlan();
618 }
Saurav Das1a129a02016-11-18 15:21:57 -0800619 tBuilder.setOutput(port);
620
Saurav Das1a129a02016-11-18 15:21:57 -0800621 TrafficSelector metadata =
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800622 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
Saurav Das1a129a02016-11-18 15:21:57 -0800623
624 NextObjective.Builder nextObjBuilder = DefaultNextObjective
625 .builder().withId(nextId)
626 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
627 .addTreatment(tBuilder.build())
628 .withMeta(metadata);
629
630 ObjectiveContext context = new DefaultObjectiveContext(
631 (objective) -> log.debug("port {} successfully {} NextObj {} on {}",
632 port, (portUp) ? "addedTo" : "removedFrom",
633 nextId, deviceId),
634 (objective, error) ->
635 log.warn("port {} failed to {} NextObj {} on {}: {}",
636 port, (portUp) ? "addTo" : "removeFrom",
637 nextId, deviceId, error));
638
639 NextObjective nextObj = (portUp) ? nextObjBuilder.addToExisting(context)
640 : nextObjBuilder.removeFromExisting(context);
641 log.debug("edgePort processed: Submited next objective {} in device {}",
642 nextId, deviceId);
643 flowObjectiveService.next(deviceId, nextObj);
644 }
645
646 /**
Saurav Das7bcbe702017-06-13 15:35:54 -0700647 * Returns the next objective of type hashed associated with the destination set.
648 * In addition, updates the existing next-objective if new route-route paths found
649 * have resulted in the addition of new next-hops to a particular destination.
650 * If there is no existing next objective for this destination set, this method
651 * would create a next objective and return the nextId. Optionally metadata can be
Saurav Das8a0732e2015-11-20 15:27:53 -0800652 * passed in for the creation of the next objective.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800653 *
Saurav Das7bcbe702017-06-13 15:35:54 -0700654 * @param ds destination set
655 * @param nextHops a map of per destination next hops
Saurav Das8a0732e2015-11-20 15:27:53 -0800656 * @param meta metadata passed into the creation of a Next Objective
Pier Ventre917127a2016-10-31 16:49:19 -0700657 * @param isBos if Bos is set
Saurav Das8a0732e2015-11-20 15:27:53 -0800658 * @return int if found or -1 if there are errors in the creation of the
659 * neighbor set.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800660 */
Saurav Das7bcbe702017-06-13 15:35:54 -0700661 public int getNextObjectiveId(DestinationSet ds,
662 Map<DeviceId, Set<DeviceId>> nextHops,
663 TrafficSelector meta, boolean isBos) {
664 NextNeighbors next = dsNextObjStore.
665 get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
666 if (next == null) {
667 log.debug("getNextObjectiveId in device{}: Next objective id "
668 + "not found for {} ... creating", deviceId, ds);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700669 log.trace("getNextObjectiveId: nsNextObjStore contents for device {}: {}",
670 deviceId,
Saurav Das7bcbe702017-06-13 15:35:54 -0700671 dsNextObjStore.entrySet()
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700672 .stream()
673 .filter((nsStoreEntry) ->
674 (nsStoreEntry.getKey().deviceId().equals(deviceId)))
675 .collect(Collectors.toList()));
Saurav Das7bcbe702017-06-13 15:35:54 -0700676
677 createGroupFromDestinationSet(ds, nextHops, meta, isBos);
678 next = dsNextObjStore.
679 get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
680 if (next == null) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700681 log.warn("getNextObjectiveId: unable to create next objective");
Saurav Das7bcbe702017-06-13 15:35:54 -0700682 // failure in creating group
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700683 return -1;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700684 } else {
685 log.debug("getNextObjectiveId in device{}: Next objective id {} "
Saurav Das7bcbe702017-06-13 15:35:54 -0700686 + "created for {}", deviceId, next.nextId(), ds);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700687 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700688 } else {
689 log.trace("getNextObjectiveId in device{}: Next objective id {} "
Saurav Das7bcbe702017-06-13 15:35:54 -0700690 + "found for {}", deviceId, next.nextId(), ds);
691 // should fix hash groups too if next-hops have changed
692 if (!next.dstNextHops().equals(nextHops)) {
693 log.debug("Nexthops have changed for dev:{} nextId:{} ..updating",
694 deviceId, next.nextId());
695 if (!updateNextHops(ds, nextHops)) {
696 // failure in updating group
697 return -1;
698 }
699 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700700 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700701 return next.nextId();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800702 }
703
sangho0b2b6d12015-05-20 22:16:38 -0700704 /**
Charles Chan59cc16d2017-02-02 16:20:42 -0800705 * Returns the next objective of type broadcast associated with the vlan,
Saurav Das4ce45962015-11-24 23:21:05 -0800706 * or -1 if no such objective exists. Note that this method does NOT create
707 * the next objective as a side-effect. It is expected that is objective is
Saurav Das1a129a02016-11-18 15:21:57 -0800708 * created at startup from network configuration. Typically this is used
709 * for L2 flooding within the subnet configured on the switch.
Charles Chanc42e84e2015-10-20 16:24:19 -0700710 *
Charles Chan59cc16d2017-02-02 16:20:42 -0800711 * @param vlanId vlan id
Charles Chanc42e84e2015-10-20 16:24:19 -0700712 * @return int if found or -1
713 */
Charles Chan59cc16d2017-02-02 16:20:42 -0800714 public int getVlanNextObjectiveId(VlanId vlanId) {
715 Integer nextId = vlanNextObjStore.
716 get(new VlanNextObjectiveStoreKey(deviceId, vlanId));
Charles Chan9f676b62015-10-29 14:58:10 -0700717
718 return (nextId != null) ? nextId : -1;
Charles Chanc42e84e2015-10-20 16:24:19 -0700719 }
720
721 /**
Saurav Das4ce45962015-11-24 23:21:05 -0800722 * Returns the next objective of type simple associated with the port on the
723 * device, given the treatment. Different treatments to the same port result
724 * in different next objectives. If no such objective exists, this method
Saurav Das961beb22017-03-29 19:09:17 -0700725 * creates one (if requested) and returns the id. Optionally metadata can be passed in for
Saurav Das1a129a02016-11-18 15:21:57 -0800726 * the creation of the objective. Typically this is used for L2 and L3 forwarding
727 * to compute nodes and containers/VMs on the compute nodes directly attached
728 * to the switch.
Saurav Das4ce45962015-11-24 23:21:05 -0800729 *
730 * @param portNum the port number for the simple next objective
731 * @param treatment the actions to apply on the packets (should include outport)
732 * @param meta optional metadata passed into the creation of the next objective
Saurav Das961beb22017-03-29 19:09:17 -0700733 * @param createIfMissing true if a next object should be created if not found
Saurav Das4ce45962015-11-24 23:21:05 -0800734 * @return int if found or created, -1 if there are errors during the
735 * creation of the next objective.
736 */
737 public int getPortNextObjectiveId(PortNumber portNum, TrafficTreatment treatment,
Saurav Das961beb22017-03-29 19:09:17 -0700738 TrafficSelector meta, boolean createIfMissing) {
Charles Chane849c192016-01-11 18:28:54 -0800739 Integer nextId = portNextObjStore
Saurav Das76ae6812017-03-15 15:15:14 -0700740 .get(new PortNextObjectiveStoreKey(deviceId, portNum, treatment, meta));
Saurav Das961beb22017-03-29 19:09:17 -0700741 if (nextId != null) {
742 return nextId;
743 }
744 log.debug("getPortNextObjectiveId in device {}: Next objective id "
745 + "not found for port: {} .. {}", deviceId, portNum,
746 (createIfMissing) ? "creating" : "aborting");
747 if (!createIfMissing) {
748 return -1;
749 }
750 // create missing next objective
751 createGroupFromPort(portNum, treatment, meta);
752 nextId = portNextObjStore.get(new PortNextObjectiveStoreKey(deviceId, portNum,
753 treatment, meta));
Saurav Das4ce45962015-11-24 23:21:05 -0800754 if (nextId == null) {
Saurav Das961beb22017-03-29 19:09:17 -0700755 log.warn("getPortNextObjectiveId: unable to create next obj"
756 + "for dev:{} port:{}", deviceId, portNum);
757 return -1;
Charles Chane849c192016-01-11 18:28:54 -0800758 }
759 return nextId;
760 }
761
762 /**
sangho0b2b6d12015-05-20 22:16:38 -0700763 * Checks if the next objective ID (group) for the neighbor set exists or not.
764 *
765 * @param ns neighbor set to check
766 * @return true if it exists, false otherwise
767 */
Saurav Das7bcbe702017-06-13 15:35:54 -0700768 public boolean hasNextObjectiveId(DestinationSet ns) {
769 NextNeighbors nextHops = dsNextObjStore.
770 get(new DestinationSetNextObjectiveStoreKey(deviceId, ns));
771 if (nextHops == null) {
sangho0b2b6d12015-05-20 22:16:38 -0700772 return false;
773 }
774
775 return true;
776 }
777
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800778 private void populateNeighborMaps() {
779 Set<Link> outgoingLinks = linkService.getDeviceEgressLinks(deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700780 for (Link link : outgoingLinks) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800781 if (link.type() != Link.Type.DIRECT) {
782 continue;
783 }
784 addNeighborAtPort(link.dst().deviceId(), link.src().port());
785 }
786 }
787
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700788 protected void addNeighborAtPort(DeviceId neighborId,
789 PortNumber portToNeighbor) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800790 // Update DeviceToPort database
791 log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
792 deviceId, neighborId, portToNeighbor);
Saurav Das8a0732e2015-11-20 15:27:53 -0800793 Set<PortNumber> ports = Collections
794 .newSetFromMap(new ConcurrentHashMap<PortNumber, Boolean>());
795 ports.add(portToNeighbor);
796 Set<PortNumber> portnums = devicePortMap.putIfAbsent(neighborId, ports);
797 if (portnums != null) {
798 portnums.add(portToNeighbor);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800799 }
800
801 // Update portToDevice database
Saurav Das8a0732e2015-11-20 15:27:53 -0800802 DeviceId prev = portDeviceMap.putIfAbsent(portToNeighbor, neighborId);
803 if (prev != null) {
Saurav Dasc88d4662017-05-15 15:34:25 -0700804 log.debug("Device: {} port: {} already has neighbor: {} ",
805 deviceId, portToNeighbor, prev, neighborId);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800806 }
807 }
808
sangho1e575652015-05-14 00:39:53 -0700809 /**
Saurav Das7bcbe702017-06-13 15:35:54 -0700810 * Creates a NextObjective for a hash group in this device from a given
811 * DestinationSet.
sangho1e575652015-05-14 00:39:53 -0700812 *
Saurav Das7bcbe702017-06-13 15:35:54 -0700813 * @param ds the DestinationSet
814 * @param neighbors a map for each destination and its next-hops
Saurav Das8a0732e2015-11-20 15:27:53 -0800815 * @param meta metadata passed into the creation of a Next Objective
Pier Ventre917127a2016-10-31 16:49:19 -0700816 * @param isBos if BoS is set
sangho1e575652015-05-14 00:39:53 -0700817 */
Saurav Das7bcbe702017-06-13 15:35:54 -0700818 public void createGroupFromDestinationSet(DestinationSet ds,
819 Map<DeviceId, Set<DeviceId>> neighbors,
820 TrafficSelector meta,
821 boolean isBos) {
822 int nextId = flowObjectiveService.allocateNextId();
823 NextObjective.Type type = NextObjective.Type.HASHED;
824 if (neighbors == null || neighbors.isEmpty()) {
825 log.warn("createGroupsFromDestinationSet: needs at least one neighbor"
826 + "to create group in dev:{} for ds: {} with next-hops {}",
827 deviceId, ds, neighbors);
828 return;
829 }
830 // If Bos == False and MPLS-ECMP == false, we have
831 // to use simple group and we will pick a single neighbor for a single dest.
832 if (!isBos && !srManager.getMplsEcmp()) {
833 type = NextObjective.Type.SIMPLE;
834 }
835
836 NextObjective.Builder nextObjBuilder = DefaultNextObjective
837 .builder()
838 .withId(nextId)
839 .withType(type)
840 .fromApp(appId);
841 if (meta != null) {
842 nextObjBuilder.withMeta(meta);
843 }
844
845 // create treatment buckets for each neighbor for each dst Device
846 // except in the special case where we only want to pick a single
847 // neighbor for a simple group
848 boolean foundSingleNeighbor = false;
849 boolean treatmentAdded = false;
850 Map<DeviceId, Set<DeviceId>> dstNextHops = new ConcurrentHashMap<>();
851 for (DeviceId dst : ds.getDestinationSwitches()) {
852 Set<DeviceId> nextHops = neighbors.get(dst);
853 if (nextHops == null || nextHops.isEmpty()) {
854 continue;
Pier Ventre917127a2016-10-31 16:49:19 -0700855 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700856
857 if (foundSingleNeighbor) {
858 break;
859 }
860
861 for (DeviceId neighborId : nextHops) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800862 if (devicePortMap.get(neighborId) == null) {
863 log.warn("Neighbor {} is not in the port map yet for dev:{}",
864 neighborId, deviceId);
sangho834e4b02015-05-01 09:38:25 -0700865 return;
Jon Hallcbd1b392017-01-18 20:15:44 -0800866 } else if (devicePortMap.get(neighborId).isEmpty()) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700867 log.warn("There are no ports for "
Saurav Das8a0732e2015-11-20 15:27:53 -0800868 + "the Device {} in the port map yet", neighborId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700869 return;
sangho834e4b02015-05-01 09:38:25 -0700870 }
871
Saurav Das8a0732e2015-11-20 15:27:53 -0800872 MacAddress neighborMac;
Charles Chan0b4e6182015-11-03 10:42:14 -0800873 try {
Saurav Das8a0732e2015-11-20 15:27:53 -0800874 neighborMac = deviceConfig.getDeviceMac(neighborId);
Charles Chan0b4e6182015-11-03 10:42:14 -0800875 } catch (DeviceConfigNotFoundException e) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700876 log.warn(e.getMessage() + " Aborting createGroupsFromDestinationset.");
Charles Chan0b4e6182015-11-03 10:42:14 -0800877 return;
878 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700879 // For each port to the neighbor, we create a new treatment
Pier Ventre917127a2016-10-31 16:49:19 -0700880 Set<PortNumber> neighborPorts = devicePortMap.get(neighborId);
881 // In this case we are using a SIMPLE group. We randomly pick a port
882 if (!isBos && !srManager.getMplsEcmp()) {
883 int size = devicePortMap.get(neighborId).size();
884 int index = RandomUtils.nextInt(0, size);
885 neighborPorts = Collections.singleton(
Saurav Das7bcbe702017-06-13 15:35:54 -0700886 Iterables.get(devicePortMap.get(neighborId),
887 index));
888 foundSingleNeighbor = true;
Pier Ventre917127a2016-10-31 16:49:19 -0700889 }
890 for (PortNumber sp : neighborPorts) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700891 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
892 .builder();
Saurav Das7bcbe702017-06-13 15:35:54 -0700893 tBuilder.setEthDst(neighborMac).setEthSrc(nodeMacAddr);
894 int edgeLabel = ds.getEdgeLabel(dst);
895 if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800896 tBuilder.pushMpls()
Saurav Das7bcbe702017-06-13 15:35:54 -0700897 .copyTtlOut()
898 .setMpls(MplsLabel.mplsLabel(edgeLabel));
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700899 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800900 tBuilder.setOutput(sp);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700901 nextObjBuilder.addTreatment(tBuilder.build());
Saurav Das7bcbe702017-06-13 15:35:54 -0700902 treatmentAdded = true;
903 //update store
904 Set<DeviceId> existingNeighbors = dstNextHops.get(dst);
905 if (existingNeighbors == null) {
906 existingNeighbors = new HashSet<>();
907 }
908 existingNeighbors.add(neighborId);
909 dstNextHops.put(dst, existingNeighbors);
910 log.debug("creating treatment for port/label {}/{} in next:{}",
911 sp, edgeLabel, nextId);
912 }
913
914 if (foundSingleNeighbor) {
915 break;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800916 }
917 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800918 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700919
920 if (!treatmentAdded) {
921 log.warn("Could not createGroup from DestinationSet {} without any"
922 + "next hops {}", ds, neighbors);
923 return;
924 }
925 ObjectiveContext context = new DefaultObjectiveContext(
926 (objective) ->
927 log.debug("createGroupsFromDestinationSet installed "
928 + "NextObj {} on {}", nextId, deviceId),
929 (objective, error) ->
930 log.warn("createGroupsFromDestinationSet failed to install"
931 + " NextObj {} on {}: {}", nextId, deviceId, error)
932 );
933 NextObjective nextObj = nextObjBuilder.add(context);
934 log.debug(".. createGroupsFromDestinationSet: Submitted "
935 + "next objective {} in device {}", nextId, deviceId);
936 flowObjectiveService.next(deviceId, nextObj);
937 //update store
938 dsNextObjStore.put(new DestinationSetNextObjectiveStoreKey(deviceId, ds),
939 new NextNeighbors(dstNextHops, nextId));
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800940 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700941
Saurav Das4ce45962015-11-24 23:21:05 -0800942 /**
Saurav Das1a129a02016-11-18 15:21:57 -0800943 * Creates broadcast groups for all ports in the same subnet for
944 * all configured subnets.
Saurav Das4ce45962015-11-24 23:21:05 -0800945 */
Charles Chan59cc16d2017-02-02 16:20:42 -0800946 public void createGroupsFromVlanConfig() {
Charles Chan7ffd81f2017-02-08 15:52:08 -0800947 srManager.getVlanPortMap(deviceId).asMap().forEach((vlanId, ports) -> {
Charles Chan59cc16d2017-02-02 16:20:42 -0800948 createBcastGroupFromVlan(vlanId, ports);
Pier Ventre10bd8d12016-11-26 21:05:22 -0800949 });
Saurav Das1a129a02016-11-18 15:21:57 -0800950 }
Charles Chan9f676b62015-10-29 14:58:10 -0700951
Saurav Das1a129a02016-11-18 15:21:57 -0800952 /**
Charles Chan59cc16d2017-02-02 16:20:42 -0800953 * Creates a single broadcast group from a given vlan id and list of ports.
Saurav Das1a129a02016-11-18 15:21:57 -0800954 *
Charles Chan59cc16d2017-02-02 16:20:42 -0800955 * @param vlanId vlan id
Saurav Das1a129a02016-11-18 15:21:57 -0800956 * @param ports list of ports in the subnet
957 */
Charles Chan7ffd81f2017-02-08 15:52:08 -0800958 public void createBcastGroupFromVlan(VlanId vlanId, Collection<PortNumber> ports) {
Charles Chan59cc16d2017-02-02 16:20:42 -0800959 VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
Charles Chan9f676b62015-10-29 14:58:10 -0700960
Charles Chan59cc16d2017-02-02 16:20:42 -0800961 if (vlanNextObjStore.containsKey(key)) {
Saurav Das1a129a02016-11-18 15:21:57 -0800962 log.debug("Broadcast group for device {} and subnet {} exists",
Charles Chan59cc16d2017-02-02 16:20:42 -0800963 deviceId, vlanId);
Saurav Das1a129a02016-11-18 15:21:57 -0800964 return;
965 }
Charles Chan188ebf52015-12-23 00:15:11 -0800966
Saurav Das1a129a02016-11-18 15:21:57 -0800967 TrafficSelector metadata =
Charles Chan59cc16d2017-02-02 16:20:42 -0800968 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
Charles Chanc42e84e2015-10-20 16:24:19 -0700969
Saurav Das1a129a02016-11-18 15:21:57 -0800970 int nextId = flowObjectiveService.allocateNextId();
Charles Chanc42e84e2015-10-20 16:24:19 -0700971
Saurav Das1a129a02016-11-18 15:21:57 -0800972 NextObjective.Builder nextObjBuilder = DefaultNextObjective
973 .builder().withId(nextId)
974 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
975 .withMeta(metadata);
Charles Chanc42e84e2015-10-20 16:24:19 -0700976
Saurav Das1a129a02016-11-18 15:21:57 -0800977 ports.forEach(port -> {
978 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Charles Chan7ffd81f2017-02-08 15:52:08 -0800979 if (toPopVlan(port, vlanId)) {
980 tBuilder.popVlan();
981 }
Saurav Das1a129a02016-11-18 15:21:57 -0800982 tBuilder.setOutput(port);
983 nextObjBuilder.addTreatment(tBuilder.build());
Charles Chanc42e84e2015-10-20 16:24:19 -0700984 });
Saurav Das1a129a02016-11-18 15:21:57 -0800985
Saurav Das961beb22017-03-29 19:09:17 -0700986 ObjectiveContext context = new DefaultObjectiveContext(
987 (objective) ->
988 log.debug("createBroadcastGroupFromVlan installed "
989 + "NextObj {} on {}", nextId, deviceId),
990 (objective, error) ->
991 log.warn("createBroadcastGroupFromVlan failed to install"
992 + " NextObj {} on {}: {}", nextId, deviceId, error)
993 );
994 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Das1a129a02016-11-18 15:21:57 -0800995 flowObjectiveService.next(deviceId, nextObj);
Charles Chan59cc16d2017-02-02 16:20:42 -0800996 log.debug("createBcastGroupFromVlan: Submited next objective {} in device {}",
Saurav Das1a129a02016-11-18 15:21:57 -0800997 nextId, deviceId);
998
Charles Chan59cc16d2017-02-02 16:20:42 -0800999 vlanNextObjStore.put(key, nextId);
Charles Chanc42e84e2015-10-20 16:24:19 -07001000 }
1001
Charles Chane849c192016-01-11 18:28:54 -08001002 /**
Charles Chan7ffd81f2017-02-08 15:52:08 -08001003 * Determine if we should pop given vlan before sending packets to the given port.
1004 *
1005 * @param portNumber port number
1006 * @param vlanId vlan id
1007 * @return true if the vlan id is not contained in any vlanTagged config
1008 */
1009 private boolean toPopVlan(PortNumber portNumber, VlanId vlanId) {
Saurav Das7bcbe702017-06-13 15:35:54 -07001010 return srManager.interfaceService
1011 .getInterfacesByPort(new ConnectPoint(deviceId, portNumber))
Charles Chan7ffd81f2017-02-08 15:52:08 -08001012 .stream().noneMatch(intf -> intf.vlanTagged().contains(vlanId));
1013 }
1014
1015 /**
Saurav Das4ce45962015-11-24 23:21:05 -08001016 * Create simple next objective for a single port. The treatments can include
1017 * all outgoing actions that need to happen on the packet.
1018 *
1019 * @param portNum the outgoing port on the device
1020 * @param treatment the actions to apply on the packets (should include outport)
1021 * @param meta optional data to pass to the driver
1022 */
1023 public void createGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
1024 TrafficSelector meta) {
1025 int nextId = flowObjectiveService.allocateNextId();
1026 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
Saurav Das76ae6812017-03-15 15:15:14 -07001027 deviceId, portNum, treatment, meta);
Saurav Das4ce45962015-11-24 23:21:05 -08001028
1029 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1030 .builder().withId(nextId)
1031 .withType(NextObjective.Type.SIMPLE)
1032 .addTreatment(treatment)
1033 .fromApp(appId)
1034 .withMeta(meta);
1035
Saurav Das961beb22017-03-29 19:09:17 -07001036 ObjectiveContext context = new DefaultObjectiveContext(
1037 (objective) ->
1038 log.debug("createGroupFromPort installed "
1039 + "NextObj {} on {}", nextId, deviceId),
1040 (objective, error) ->
1041 log.warn("createGroupFromPort failed to install"
1042 + " NextObj {} on {}: {}", nextId, deviceId, error)
1043 );
1044 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Das4ce45962015-11-24 23:21:05 -08001045 flowObjectiveService.next(deviceId, nextObj);
1046 log.debug("createGroupFromPort: Submited next objective {} in device {} "
1047 + "for port {}", nextId, deviceId, portNum);
1048
1049 portNextObjStore.put(key, nextId);
1050 }
1051
sangho1e575652015-05-14 00:39:53 -07001052 /**
1053 * Removes groups for the next objective ID given.
1054 *
1055 * @param objectiveId next objective ID to remove
1056 * @return true if succeeds, false otherwise
1057 */
1058 public boolean removeGroup(int objectiveId) {
Saurav Das7bcbe702017-06-13 15:35:54 -07001059 for (Map.Entry<DestinationSetNextObjectiveStoreKey, NextNeighbors> e :
1060 dsNextObjStore.entrySet()) {
1061 if (e.getValue().nextId() != objectiveId) {
1062 continue;
1063 }
sangho1e575652015-05-14 00:39:53 -07001064 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1065 .builder().withId(objectiveId)
1066 .withType(NextObjective.Type.HASHED).fromApp(appId);
Charles Chan216e3c82016-04-23 14:48:16 -07001067 ObjectiveContext context = new DefaultObjectiveContext(
1068 (objective) -> log.debug("RemoveGroup removes NextObj {} on {}",
1069 objectiveId, deviceId),
1070 (objective, error) ->
1071 log.warn("RemoveGroup failed to remove NextObj {} on {}: {}",
1072 objectiveId, deviceId, error));
1073 NextObjective nextObjective = nextObjBuilder.remove(context);
Saurav Das8a0732e2015-11-20 15:27:53 -08001074 log.info("**removeGroup: Submited "
1075 + "next objective {} in device {}",
1076 objectiveId, deviceId);
sangho1e575652015-05-14 00:39:53 -07001077 flowObjectiveService.next(deviceId, nextObjective);
1078
Saurav Das7bcbe702017-06-13 15:35:54 -07001079 dsNextObjStore.remove(e.getKey());
sangho0b2b6d12015-05-20 22:16:38 -07001080 return true;
sangho1e575652015-05-14 00:39:53 -07001081 }
1082
1083 return false;
1084 }
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -07001085
Charles Chane849c192016-01-11 18:28:54 -08001086 /**
1087 * Removes all groups from all next objective stores.
1088 */
Saurav Das7bcbe702017-06-13 15:35:54 -07001089 /*public void removeAllGroups() {
1090 for (Map.Entry<NeighborSetNextObjectiveStoreKey, NextNeighbors> entry:
Saurav Das423fe2b2015-12-04 10:52:59 -08001091 nsNextObjStore.entrySet()) {
Saurav Das7bcbe702017-06-13 15:35:54 -07001092 removeGroup(entry.getValue().nextId());
Saurav Das423fe2b2015-12-04 10:52:59 -08001093 }
1094 for (Map.Entry<PortNextObjectiveStoreKey, Integer> entry:
1095 portNextObjStore.entrySet()) {
1096 removeGroup(entry.getValue());
1097 }
Charles Chan59cc16d2017-02-02 16:20:42 -08001098 for (Map.Entry<VlanNextObjectiveStoreKey, Integer> entry:
1099 vlanNextObjStore.entrySet()) {
Saurav Das423fe2b2015-12-04 10:52:59 -08001100 removeGroup(entry.getValue());
1101 }
Saurav Das7bcbe702017-06-13 15:35:54 -07001102 }*/ //XXX revisit
1103
Saurav Das1547b3f2017-05-05 17:01:08 -07001104
1105 /**
1106 * RetryHashBkts is a one-time retry at populating all the buckets of a
1107 * hash group based on the given link. Should only be called by the
1108 * master instance of the src-device of the link.
1109 */
1110 protected final class RetryHashBkts implements Runnable {
1111 Link link;
1112 MacAddress dstMac;
1113
1114 private RetryHashBkts(Link link, MacAddress dstMac) {
1115 this.link = link;
1116 this.dstMac = dstMac;
1117 }
1118
1119 @Override
1120 public void run() {
Charles Chan7f9737b2017-06-22 14:27:17 -07001121 log.debug("RETRY Hash buckets for linkup: {}", link);
Saurav Das7bcbe702017-06-13 15:35:54 -07001122 Set<DestinationSetNextObjectiveStoreKey> dsKeySet = dsNextObjStore.entrySet()
Saurav Das1547b3f2017-05-05 17:01:08 -07001123 .stream()
Saurav Das7bcbe702017-06-13 15:35:54 -07001124 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
1125 .filter(entry -> entry.getValue().containsNextHop(link.dst().deviceId()))
1126 .map(entry -> entry.getKey())
Saurav Das1547b3f2017-05-05 17:01:08 -07001127 .collect(Collectors.toSet());
Saurav Das7bcbe702017-06-13 15:35:54 -07001128
1129 log.debug("retry-link: dsNextObjStore contents for device {}: {}",
1130 deviceId, dsKeySet);
1131 for (DestinationSetNextObjectiveStoreKey dsKey : dsKeySet) {
1132 NextNeighbors next = dsNextObjStore.get(dsKey);
1133 if (next != null) {
1134 Set<DeviceId> dstSet = next.getDstForNextHop(link.dst().deviceId());
1135 dstSet.forEach(dst -> {
1136 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
1137 addToHashedNextObjective(link.src().port(), dstMac, edgeLabel,
1138 next.nextId(), true);
1139 });
Saurav Das1547b3f2017-05-05 17:01:08 -07001140 }
1141 }
1142 }
1143 }
1144
1145
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -08001146}