blob: 031c80584fdc009efaf122994a3088fbccb94341 [file] [log] [blame]
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
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
Pier Ventre917127a2016-10-31 16:49:19 -070019import com.google.common.collect.Iterables;
Saurav Dasc88d4662017-05-15 15:34:25 -070020import com.google.common.collect.Sets;
21
Pier Ventre917127a2016-10-31 16:49:19 -070022import org.apache.commons.lang3.RandomUtils;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080023import org.onlab.packet.MacAddress;
sangho32a59322015-02-17 12:07:41 -080024import org.onlab.packet.MplsLabel;
Saurav Das423fe2b2015-12-04 10:52:59 -080025import org.onlab.packet.VlanId;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070026import org.onlab.util.KryoNamespace;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080027import org.onosproject.core.ApplicationId;
Charles Chan59cc16d2017-02-02 16:20:42 -080028import org.onosproject.net.ConnectPoint;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080029import org.onosproject.net.DeviceId;
30import org.onosproject.net.Link;
31import org.onosproject.net.PortNumber;
Saurav Das423fe2b2015-12-04 10:52:59 -080032import org.onosproject.net.flow.DefaultTrafficSelector;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080033import org.onosproject.net.flow.DefaultTrafficTreatment;
Saurav Das8a0732e2015-11-20 15:27:53 -080034import org.onosproject.net.flow.TrafficSelector;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080035import org.onosproject.net.flow.TrafficTreatment;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070036import org.onosproject.net.flowobjective.DefaultNextObjective;
Charles Chan216e3c82016-04-23 14:48:16 -070037import org.onosproject.net.flowobjective.DefaultObjectiveContext;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070038import org.onosproject.net.flowobjective.FlowObjectiveService;
39import org.onosproject.net.flowobjective.NextObjective;
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -070040import org.onosproject.net.flowobjective.ObjectiveContext;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080041import org.onosproject.net.link.LinkService;
Saurav Das423fe2b2015-12-04 10:52:59 -080042import org.onosproject.segmentrouting.SegmentRoutingManager;
Charles Chan0b4e6182015-11-03 10:42:14 -080043import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
44import org.onosproject.segmentrouting.config.DeviceProperties;
Charles Chand2990362016-04-18 13:44:03 -070045import org.onosproject.segmentrouting.storekey.NeighborSetNextObjectiveStoreKey;
46import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
Charles Chan59cc16d2017-02-02 16:20:42 -080047import org.onosproject.segmentrouting.storekey.VlanNextObjectiveStoreKey;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070048import org.onosproject.store.service.EventuallyConsistentMap;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080049import org.slf4j.Logger;
50
Pier Ventre917127a2016-10-31 16:49:19 -070051import java.net.URI;
52import java.util.ArrayList;
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 Das1a129a02016-11-18 15:21:57 -0800101 // distributed store for (device+neighborset) mapped to next-id
Charles Chane849c192016-01-11 18:28:54 -0800102 protected EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey, Integer>
103 nsNextObjStore = 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)
118 .register(DeviceId.class).register(PortNumber.class)
119 .register(NeighborSet.class).register(PolicyGroupIdentifier.class)
120 .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;
Ray Milkeye4afdb52017-04-05 09:42:04 -0700144 this.nsNextObjStore = srManager.nsNextObjStore();
145 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 Dasc88d4662017-05-15 15:34:25 -0700234 // find all the neighborSets related to link
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700235 Set<NeighborSet> nsSet = nsNextObjStore.keySet()
236 .stream()
237 .filter((nsStoreEntry) -> (nsStoreEntry.deviceId().equals(deviceId)))
238 .map((nsStoreEntry) -> (nsStoreEntry.neighborSet()))
239 .filter((ns) -> (ns.getDeviceIds()
Saurav Dasc88d4662017-05-15 15:34:25 -0700240 .contains(link.dst().deviceId())))
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700241 .collect(Collectors.toSet());
Saurav Dasc88d4662017-05-15 15:34:25 -0700242 log.debug("retryHash: nsNextObjStore contents for linkSrc {} -> linkDst {}: {}",
243 deviceId, link.dst().deviceId(), nsSet);
244
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700245 for (NeighborSet ns : nsSet) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700246 Integer nextId = nsNextObjStore.
247 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
Saurav Dasc88d4662017-05-15 15:34:25 -0700248 if (nextId == null) {
249 log.warn("retryHash in device {}, but global store has no record "
Saurav Das8a0732e2015-11-20 15:27:53 -0800250 + "for neighbor-set {}", deviceId, ns);
Saurav Dasc88d4662017-05-15 15:34:25 -0700251 continue;
252 }
253 if (!linkDown) {
254 addToHashedNextObjective(link.src().port(), dstMac, ns,
255 nextId, false);
256 if (firstTime) {
257 // some links may have come up before the next-objective was created
258 // we take this opportunity to ensure other ports to same next-hop-dst
259 // are part of the hash group (see CORD-1180). Duplicate additions
260 // to the same hash group are avoided by the driver.
261 for (PortNumber p : devicePortMap.get(link.dst().deviceId())) {
262 if (p.equals(link.src().port())) {
263 continue;
264 }
265 addToHashedNextObjective(p, dstMac, ns, nextId, false);
266 }
267 }
268 } else {
269 removeFromHashedNextObjective(link.src().port(), dstMac, ns,
270 nextId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700271 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800272 }
Saurav Das1547b3f2017-05-05 17:01:08 -0700273
274 // It's possible that at the time of linkup, some hash-groups have
275 // not been created yet by the instance responsible for creating them, or
276 // due to the eventually-consistent nature of the nsNextObjStore it has
277 // not synced up with this instance yet. Thus we perform this check again
278 // after a delay (see CORD-1180). Duplicate additions to the same hash group
279 // are avoided by the driver.
Saurav Dasc88d4662017-05-15 15:34:25 -0700280 if (!linkDown && firstTime) {
281 executorService.schedule(new RetryHashBkts(link, dstMac),
Saurav Das1547b3f2017-05-05 17:01:08 -0700282 RETRY_INTERVAL_SEC, TimeUnit.SECONDS);
283 }
284 }
285
Saurav Dasc88d4662017-05-15 15:34:25 -0700286 /**
287 * Makes a call to the FlowObjective service to add a single bucket to
288 * a hashed group.
289 *
290 * @param outport port to add to hash group
291 * @param dstMac destination mac address of next-hop
292 * @param ns neighbor set representing next-hops and destination switch
293 * @param nextId id for next-objective to which the bucket will be added
294 * @param retry indicates if this method is being called on a retry attempt
295 * at adding a bucket to the group
296 */
Saurav Das1547b3f2017-05-05 17:01:08 -0700297 private void addToHashedNextObjective(PortNumber outport, MacAddress dstMac,
298 NeighborSet ns, Integer nextId, boolean retry) {
299 // Create the new bucket to be updated
300 TrafficTreatment.Builder tBuilder =
301 DefaultTrafficTreatment.builder();
302 tBuilder.setOutput(outport)
303 .setEthDst(dstMac)
304 .setEthSrc(nodeMacAddr);
305 if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
306 tBuilder.pushMpls()
307 .copyTtlOut()
308 .setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
309 }
310 // setup metadata to pass to nextObjective - indicate the vlan on egress
311 // if needed by the switch pipeline. Since hashed next-hops are always to
312 // other neighboring routers, there is no subnet assigned on those ports.
313 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
314 metabuilder.matchVlanId(INTERNAL_VLAN);
315
316 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
317 .withId(nextId)
318 .withType(NextObjective.Type.HASHED)
319 .addTreatment(tBuilder.build())
320 .withMeta(metabuilder.build())
321 .fromApp(appId);
322 log.info("{} in device {}: Adding Bucket with Port {} to next object id {}",
Saurav Dasc88d4662017-05-15 15:34:25 -0700323 (retry) ? "retry-addToHash" : "addToHash",
Saurav Das1547b3f2017-05-05 17:01:08 -0700324 deviceId, outport, nextId);
325
326 ObjectiveContext context = new DefaultObjectiveContext(
Saurav Dasc88d4662017-05-15 15:34:25 -0700327 (objective) -> log.debug("{} addedTo NextObj {} on {}",
328 (retry) ? "retry-addToHash" : "addToHash",
329 nextId, deviceId),
Saurav Das1547b3f2017-05-05 17:01:08 -0700330 (objective, error) ->
Saurav Dasc88d4662017-05-15 15:34:25 -0700331 log.warn("{} failed to addTo NextObj {} on {}: {}",
332 (retry) ? "retry-addToHash" : "addToHash",
333 nextId, deviceId, error));
Saurav Das1547b3f2017-05-05 17:01:08 -0700334 NextObjective nextObjective = nextObjBuilder.addToExisting(context);
335 flowObjectiveService.next(deviceId, nextObjective);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800336 }
337
338 /**
Saurav Dasc88d4662017-05-15 15:34:25 -0700339 * Makes a call to the FlowObjective service to remove a single bucket from
340 * a hashed group.
341 *
342 * @param port port to remove from hash group
343 * @param dstMac destination mac address of next-hop
344 * @param ns neighbor set representing next-hops and destination switch
345 * @param nextId id for next-objective from which the bucket will be removed
346 */
347 private void removeFromHashedNextObjective(PortNumber port, MacAddress dstMac,
348 NeighborSet ns, Integer nextId) {
349 // Create the bucket to be removed
350 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
351 .builder();
352 tBuilder.setOutput(port)
353 .setEthDst(dstMac)
354 .setEthSrc(nodeMacAddr);
355 if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
356 tBuilder.pushMpls()
357 .copyTtlOut()
358 .setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
359 }
360 log.info("{} in device {}: Removing Bucket with Port {} to next object id {}",
361 "removeFromHash", deviceId, port, nextId);
362 NextObjective.Builder nextObjBuilder = DefaultNextObjective
363 .builder()
364 .withType(NextObjective.Type.HASHED) //same as original
365 .withId(nextId)
366 .fromApp(appId)
367 .addTreatment(tBuilder.build());
368 ObjectiveContext context = new DefaultObjectiveContext(
369 (objective) -> log.debug("port {} removedFrom NextObj {} on {}",
370 port, nextId, deviceId),
371 (objective, error) ->
372 log.warn("port {} failed to removeFrom NextObj {} on {}: {}",
373 port, nextId, deviceId, error));
374 NextObjective nextObjective = nextObjBuilder.
375 removeFromExisting(context);
376
377 flowObjectiveService.next(deviceId, nextObjective);
378 }
379
380 /**
381 * Checks all the hash-groups in the target-switch meant for the destination
382 * switch, and either adds or removes buckets to make the neighbor-set
383 * match the given next-hops. Typically called by the master instance of the
384 * destination switch, which may be different from the master instance of the
385 * target switch where hash-group changes are made.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800386 *
Saurav Dasc88d4662017-05-15 15:34:25 -0700387 * @param targetSw the switch in which the hash groups will be edited
388 * @param nextHops the current next hops for the target switch to reach
389 * the dest sw
390 * @param destSw the destination switch
391 * @param revoke true if hash groups need to remove buckets from the
392 * the groups to match the current next hops
393 * @return true if calls are made to edit buckets, or if no edits are required
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800394 */
Saurav Dasc88d4662017-05-15 15:34:25 -0700395 public boolean fixHashGroups(DeviceId targetSw, Set<DeviceId> nextHops,
396 DeviceId destSw, boolean revoke) {
397 // temporary storage of keys to be updated
398 Map<NeighborSetNextObjectiveStoreKey, Set<DeviceId>> tempStore =
399 new HashMap<>();
400 boolean foundNextObjective = false;
Charles Chan0b4e6182015-11-03 10:42:14 -0800401
Saurav Dasc88d4662017-05-15 15:34:25 -0700402 // retrieve hash-groups meant for destSw, which have neighborSets
403 // with different neighbors than the given next-hops
404 for (NeighborSetNextObjectiveStoreKey nskey : nsNextObjStore.keySet()) {
405 if (!nskey.deviceId().equals(targetSw) ||
406 !nskey.neighborSet().getDestinationSw().equals(destSw)) {
407 continue;
408 }
409 foundNextObjective = true;
410 Set<DeviceId> currNeighbors = nskey.neighborSet().getDeviceIds();
411 Integer nextId = nsNextObjStore.get(nskey);
Charles Chan0b4e6182015-11-03 10:42:14 -0800412
Saurav Dasc88d4662017-05-15 15:34:25 -0700413 Set<DeviceId> diff;
414 if (revoke) {
415 diff = Sets.difference(currNeighbors, nextHops);
416 log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next "
417 + "hops:{} ..removing {}", targetSw, destSw, nextId,
418 currNeighbors, diff);
419 } else {
420 diff = Sets.difference(nextHops, currNeighbors);
421 log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next "
422 + "hops:{} ..adding {}", targetSw, destSw, nextId,
423 currNeighbors, diff);
424 }
425 for (DeviceId neighbor : diff) {
426 MacAddress dstMac;
427 try {
428 dstMac = deviceConfig.getDeviceMac(neighbor);
429 } catch (DeviceConfigNotFoundException e) {
430 log.warn(e.getMessage() + " Aborting fixHashGroup for nextId:"
431 + nskey);
432 return false;
Saurav Das423fe2b2015-12-04 10:52:59 -0800433 }
Saurav Dasc88d4662017-05-15 15:34:25 -0700434 if (devicePortMap.get(neighbor) == null ||
435 devicePortMap.get(neighbor).isEmpty()) {
436 log.warn("No ports found in dev:{} for neighbor:{} .. cannot "
437 + "fix hash group for nextId: {}",
438 deviceId, neighbor, nextId);
439 return false;
440 }
441 if (revoke) {
442 for (PortNumber port : devicePortMap.get(neighbor)) {
443 log.info("fixHashGroup in device {}: Removing Bucket "
444 + "with Port {} to next object id {}",
445 deviceId, port, nextId);
446 removeFromHashedNextObjective(port, dstMac,
447 nskey.neighborSet(),
448 nextId);
449 }
450 // to update neighbor set with changes made
451 tempStore.put(nskey, Sets.difference(currNeighbors, diff));
452 } else {
453 for (PortNumber port : devicePortMap.get(neighbor)) {
454 log.info("fixHashGroup in device {}: Adding Bucket "
455 + "with Port {} to next object id {}",
456 deviceId, port, nextId);
457 addToHashedNextObjective(port, dstMac,
458 nskey.neighborSet(),
459 nextId, false);
460 }
461 // to update neighbor set with changes made
462 tempStore.put(nskey, Sets.union(currNeighbors, diff));
463 }
sangho834e4b02015-05-01 09:38:25 -0700464 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800465 }
466
Saurav Dasc88d4662017-05-15 15:34:25 -0700467 if (!foundNextObjective) {
468 log.debug("Cannot find any nextObjectives for route targetSw:{} "
469 + "-> dstSw:{}", targetSw, destSw);
470 return true; // nothing to do, return true so ECMPspg is updated
471 }
472
473 // update the nsNextObjectiveStore with new neighborSets to nextId mappings
474 for (NeighborSetNextObjectiveStoreKey oldkey : tempStore.keySet()) {
475 Integer nextId = nsNextObjStore.get(oldkey);
476 if (nextId == null) {
477 continue;
478 }
479 Set<DeviceId> newNeighbors = tempStore.get(oldkey);
480 NeighborSet newNs = new NeighborSet(newNeighbors,
481 oldkey.neighborSet().mplsSet(),
482 oldkey.neighborSet().getEdgeLabel(),
483 oldkey.neighborSet().getDestinationSw());
484 NeighborSetNextObjectiveStoreKey newkey =
485 new NeighborSetNextObjectiveStoreKey(deviceId, newNs);
486 log.debug("Updating nsNextObjStore: oldKey:{} -> newKey:{} :: nextId:{}",
487 oldkey, newkey, nextId);
488 synchronized (nsNextObjStore) {
489 nsNextObjStore.remove(oldkey);
490 nsNextObjStore.put(newkey, nextId);
491 }
492 }
493
494 return true;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800495 }
496
497 /**
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800498 * Adds or removes a port that has been configured with a vlan to a broadcast group
499 * for bridging. Should only be called by the master instance for this device.
Saurav Das1a129a02016-11-18 15:21:57 -0800500 *
501 * @param port the port on this device that needs to be added/removed to a bcast group
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800502 * @param vlanId the vlan id corresponding to the broadcast domain/group
503 * @param popVlan indicates if packets should be sent out untagged or not out
504 * of the port. If true, indicates an access (untagged) or native vlan
505 * configuration. If false, indicates a trunk (tagged) vlan config.
Saurav Das1a129a02016-11-18 15:21:57 -0800506 * @param portUp true if port is enabled, false if disabled
Saurav Das1a129a02016-11-18 15:21:57 -0800507 */
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800508 public void processEdgePort(PortNumber port, VlanId vlanId,
509 boolean popVlan, boolean portUp) {
Saurav Das1a129a02016-11-18 15:21:57 -0800510 //get the next id for the subnet and edit it.
Charles Chan59cc16d2017-02-02 16:20:42 -0800511 Integer nextId = getVlanNextObjectiveId(vlanId);
Saurav Das1a129a02016-11-18 15:21:57 -0800512 if (nextId == -1) {
513 if (portUp) {
514 log.debug("**Creating flooding group for first port enabled in"
Charles Chan59cc16d2017-02-02 16:20:42 -0800515 + " subnet {} on dev {} port {}", vlanId, deviceId, port);
516 createBcastGroupFromVlan(vlanId, Collections.singleton(port));
Saurav Das1a129a02016-11-18 15:21:57 -0800517 } else {
518 log.warn("Could not find flooding group for subnet {} on dev:{} when"
Charles Chan59cc16d2017-02-02 16:20:42 -0800519 + " removing port:{}", vlanId, deviceId, port);
Saurav Das1a129a02016-11-18 15:21:57 -0800520 }
521 return;
522 }
523
524 log.info("**port{} in device {}: {} Bucket with Port {} to"
525 + " next-id {}", (portUp) ? "UP" : "DOWN", deviceId,
526 (portUp) ? "Adding" : "Removing",
527 port, nextId);
528 // Create the bucket to be added or removed
529 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800530 if (popVlan) {
531 tBuilder.popVlan();
532 }
Saurav Das1a129a02016-11-18 15:21:57 -0800533 tBuilder.setOutput(port);
534
Saurav Das1a129a02016-11-18 15:21:57 -0800535 TrafficSelector metadata =
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800536 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
Saurav Das1a129a02016-11-18 15:21:57 -0800537
538 NextObjective.Builder nextObjBuilder = DefaultNextObjective
539 .builder().withId(nextId)
540 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
541 .addTreatment(tBuilder.build())
542 .withMeta(metadata);
543
544 ObjectiveContext context = new DefaultObjectiveContext(
545 (objective) -> log.debug("port {} successfully {} NextObj {} on {}",
546 port, (portUp) ? "addedTo" : "removedFrom",
547 nextId, deviceId),
548 (objective, error) ->
549 log.warn("port {} failed to {} NextObj {} on {}: {}",
550 port, (portUp) ? "addTo" : "removeFrom",
551 nextId, deviceId, error));
552
553 NextObjective nextObj = (portUp) ? nextObjBuilder.addToExisting(context)
554 : nextObjBuilder.removeFromExisting(context);
555 log.debug("edgePort processed: Submited next objective {} in device {}",
556 nextId, deviceId);
557 flowObjectiveService.next(deviceId, nextObj);
558 }
559
560 /**
Saurav Das4ce45962015-11-24 23:21:05 -0800561 * Returns the next objective of type hashed associated with the neighborset.
562 * If there is no next objective for this neighborset, this method
Saurav Das8a0732e2015-11-20 15:27:53 -0800563 * would create a next objective and return. Optionally metadata can be
564 * passed in for the creation of the next objective.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800565 *
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700566 * @param ns neighborset
Saurav Das8a0732e2015-11-20 15:27:53 -0800567 * @param meta metadata passed into the creation of a Next Objective
Pier Ventre917127a2016-10-31 16:49:19 -0700568 * @param isBos if Bos is set
Saurav Das8a0732e2015-11-20 15:27:53 -0800569 * @return int if found or -1 if there are errors in the creation of the
570 * neighbor set.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800571 */
Pier Ventre917127a2016-10-31 16:49:19 -0700572 public int getNextObjectiveId(NeighborSet ns, TrafficSelector meta, boolean isBos) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700573 Integer nextId = nsNextObjStore.
574 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700575 if (nextId == null) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700576 log.trace("getNextObjectiveId in device{}: Next objective id "
577 + "not found for {} and creating", deviceId, ns);
578 log.trace("getNextObjectiveId: nsNextObjStore contents for device {}: {}",
579 deviceId,
580 nsNextObjStore.entrySet()
581 .stream()
582 .filter((nsStoreEntry) ->
583 (nsStoreEntry.getKey().deviceId().equals(deviceId)))
584 .collect(Collectors.toList()));
Pier Ventre917127a2016-10-31 16:49:19 -0700585 createGroupsFromNeighborsets(Collections.singleton(ns), meta, isBos);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700586 nextId = nsNextObjStore.
587 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700588 if (nextId == null) {
589 log.warn("getNextObjectiveId: unable to create next objective");
590 return -1;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700591 } else {
592 log.debug("getNextObjectiveId in device{}: Next objective id {} "
Sho SHIMIZUaf973432015-09-11 14:24:50 -0700593 + "created for {}", deviceId, nextId, ns);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700594 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700595 } else {
596 log.trace("getNextObjectiveId in device{}: Next objective id {} "
Sho SHIMIZUaf973432015-09-11 14:24:50 -0700597 + "found for {}", deviceId, nextId, ns);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700598 }
Sho SHIMIZUaf973432015-09-11 14:24:50 -0700599 return nextId;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800600 }
601
sangho0b2b6d12015-05-20 22:16:38 -0700602 /**
Charles Chan59cc16d2017-02-02 16:20:42 -0800603 * Returns the next objective of type broadcast associated with the vlan,
Saurav Das4ce45962015-11-24 23:21:05 -0800604 * or -1 if no such objective exists. Note that this method does NOT create
605 * the next objective as a side-effect. It is expected that is objective is
Saurav Das1a129a02016-11-18 15:21:57 -0800606 * created at startup from network configuration. Typically this is used
607 * for L2 flooding within the subnet configured on the switch.
Charles Chanc42e84e2015-10-20 16:24:19 -0700608 *
Charles Chan59cc16d2017-02-02 16:20:42 -0800609 * @param vlanId vlan id
Charles Chanc42e84e2015-10-20 16:24:19 -0700610 * @return int if found or -1
611 */
Charles Chan59cc16d2017-02-02 16:20:42 -0800612 public int getVlanNextObjectiveId(VlanId vlanId) {
613 Integer nextId = vlanNextObjStore.
614 get(new VlanNextObjectiveStoreKey(deviceId, vlanId));
Charles Chan9f676b62015-10-29 14:58:10 -0700615
616 return (nextId != null) ? nextId : -1;
Charles Chanc42e84e2015-10-20 16:24:19 -0700617 }
618
619 /**
Saurav Das4ce45962015-11-24 23:21:05 -0800620 * Returns the next objective of type simple associated with the port on the
621 * device, given the treatment. Different treatments to the same port result
622 * in different next objectives. If no such objective exists, this method
Saurav Das961beb22017-03-29 19:09:17 -0700623 * creates one (if requested) and returns the id. Optionally metadata can be passed in for
Saurav Das1a129a02016-11-18 15:21:57 -0800624 * the creation of the objective. Typically this is used for L2 and L3 forwarding
625 * to compute nodes and containers/VMs on the compute nodes directly attached
626 * to the switch.
Saurav Das4ce45962015-11-24 23:21:05 -0800627 *
628 * @param portNum the port number for the simple next objective
629 * @param treatment the actions to apply on the packets (should include outport)
630 * @param meta optional metadata passed into the creation of the next objective
Saurav Das961beb22017-03-29 19:09:17 -0700631 * @param createIfMissing true if a next object should be created if not found
Saurav Das4ce45962015-11-24 23:21:05 -0800632 * @return int if found or created, -1 if there are errors during the
633 * creation of the next objective.
634 */
635 public int getPortNextObjectiveId(PortNumber portNum, TrafficTreatment treatment,
Saurav Das961beb22017-03-29 19:09:17 -0700636 TrafficSelector meta, boolean createIfMissing) {
Charles Chane849c192016-01-11 18:28:54 -0800637 Integer nextId = portNextObjStore
Saurav Das76ae6812017-03-15 15:15:14 -0700638 .get(new PortNextObjectiveStoreKey(deviceId, portNum, treatment, meta));
Saurav Das961beb22017-03-29 19:09:17 -0700639 if (nextId != null) {
640 return nextId;
641 }
642 log.debug("getPortNextObjectiveId in device {}: Next objective id "
643 + "not found for port: {} .. {}", deviceId, portNum,
644 (createIfMissing) ? "creating" : "aborting");
645 if (!createIfMissing) {
646 return -1;
647 }
648 // create missing next objective
649 createGroupFromPort(portNum, treatment, meta);
650 nextId = portNextObjStore.get(new PortNextObjectiveStoreKey(deviceId, portNum,
651 treatment, meta));
Saurav Das4ce45962015-11-24 23:21:05 -0800652 if (nextId == null) {
Saurav Das961beb22017-03-29 19:09:17 -0700653 log.warn("getPortNextObjectiveId: unable to create next obj"
654 + "for dev:{} port:{}", deviceId, portNum);
655 return -1;
Charles Chane849c192016-01-11 18:28:54 -0800656 }
657 return nextId;
658 }
659
660 /**
sangho0b2b6d12015-05-20 22:16:38 -0700661 * Checks if the next objective ID (group) for the neighbor set exists or not.
662 *
663 * @param ns neighbor set to check
664 * @return true if it exists, false otherwise
665 */
666 public boolean hasNextObjectiveId(NeighborSet ns) {
667 Integer nextId = nsNextObjStore.
668 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
669 if (nextId == null) {
670 return false;
671 }
672
673 return true;
674 }
675
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800676 private void populateNeighborMaps() {
677 Set<Link> outgoingLinks = linkService.getDeviceEgressLinks(deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700678 for (Link link : outgoingLinks) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800679 if (link.type() != Link.Type.DIRECT) {
680 continue;
681 }
682 addNeighborAtPort(link.dst().deviceId(), link.src().port());
683 }
684 }
685
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700686 protected void addNeighborAtPort(DeviceId neighborId,
687 PortNumber portToNeighbor) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800688 // Update DeviceToPort database
689 log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
690 deviceId, neighborId, portToNeighbor);
Saurav Das8a0732e2015-11-20 15:27:53 -0800691 Set<PortNumber> ports = Collections
692 .newSetFromMap(new ConcurrentHashMap<PortNumber, Boolean>());
693 ports.add(portToNeighbor);
694 Set<PortNumber> portnums = devicePortMap.putIfAbsent(neighborId, ports);
695 if (portnums != null) {
696 portnums.add(portToNeighbor);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800697 }
698
699 // Update portToDevice database
Saurav Das8a0732e2015-11-20 15:27:53 -0800700 DeviceId prev = portDeviceMap.putIfAbsent(portToNeighbor, neighborId);
701 if (prev != null) {
Saurav Dasc88d4662017-05-15 15:34:25 -0700702 log.debug("Device: {} port: {} already has neighbor: {} ",
703 deviceId, portToNeighbor, prev, neighborId);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800704 }
705 }
706
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700707 protected Set<Set<DeviceId>> getPowerSetOfNeighbors(Set<DeviceId> neighbors) {
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700708 List<DeviceId> list = new ArrayList<>(neighbors);
709 Set<Set<DeviceId>> sets = new HashSet<>();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800710 // get the number of elements in the neighbors
711 int elements = list.size();
712 // the number of members of a power set is 2^n
713 // including the empty set
714 int powerElements = (1 << elements);
715
716 // run a binary counter for the number of power elements
717 // NOTE: Exclude empty set
718 for (long i = 1; i < powerElements; i++) {
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700719 Set<DeviceId> neighborSubSet = new HashSet<>();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800720 for (int j = 0; j < elements; j++) {
721 if ((i >> j) % 2 == 1) {
722 neighborSubSet.add(list.get(j));
723 }
724 }
725 sets.add(neighborSubSet);
726 }
727 return sets;
728 }
729
730 private boolean isSegmentIdSameAsNodeSegmentId(DeviceId deviceId, int sId) {
Charles Chan0b4e6182015-11-03 10:42:14 -0800731 int segmentId;
732 try {
Pier Ventre917127a2016-10-31 16:49:19 -0700733 // IPv6 sid is not inserted. this part of the code is not used for now.
Pier Ventree0ae7a32016-11-23 09:57:42 -0800734 segmentId = deviceConfig.getIPv4SegmentId(deviceId);
Charles Chan0b4e6182015-11-03 10:42:14 -0800735 } catch (DeviceConfigNotFoundException e) {
736 log.warn(e.getMessage() + " Aborting isSegmentIdSameAsNodeSegmentId.");
737 return false;
738 }
739
740 return segmentId == sId;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800741 }
742
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700743 protected List<Integer> getSegmentIdsTobePairedWithNeighborSet(Set<DeviceId> neighbors) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800744
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700745 List<Integer> nsSegmentIds = new ArrayList<>();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800746
sanghob35a6192015-04-01 13:05:26 -0700747 // Always pair up with no edge label
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700748 // If (neighbors.size() == 1) {
sanghob35a6192015-04-01 13:05:26 -0700749 nsSegmentIds.add(-1);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700750 // }
sanghob35a6192015-04-01 13:05:26 -0700751
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800752 // Filter out SegmentIds matching with the
753 // nodes in the combo
754 for (Integer sId : allSegmentIds) {
Pier Ventree0ae7a32016-11-23 09:57:42 -0800755 if (sId.equals(this.ipv4NodeSegmentId)) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800756 continue;
757 }
758 boolean filterOut = false;
759 // Check if the edge label being set is of
760 // any node in the Neighbor set
761 for (DeviceId deviceId : neighbors) {
762 if (isSegmentIdSameAsNodeSegmentId(deviceId, sId)) {
763 filterOut = true;
764 break;
765 }
766 }
767 if (!filterOut) {
768 nsSegmentIds.add(sId);
769 }
770 }
771 return nsSegmentIds;
772 }
773
sangho1e575652015-05-14 00:39:53 -0700774 /**
Saurav Das1a129a02016-11-18 15:21:57 -0800775 * Creates hash groups from a set of NeighborSet given.
sangho1e575652015-05-14 00:39:53 -0700776 *
777 * @param nsSet a set of NeighborSet
Saurav Das8a0732e2015-11-20 15:27:53 -0800778 * @param meta metadata passed into the creation of a Next Objective
Pier Ventre917127a2016-10-31 16:49:19 -0700779 * @param isBos if BoS is set
sangho1e575652015-05-14 00:39:53 -0700780 */
Saurav Das8a0732e2015-11-20 15:27:53 -0800781 public void createGroupsFromNeighborsets(Set<NeighborSet> nsSet,
Pier Ventre917127a2016-10-31 16:49:19 -0700782 TrafficSelector meta,
783 boolean isBos) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800784 for (NeighborSet ns : nsSet) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700785 int nextId = flowObjectiveService.allocateNextId();
Pier Ventre917127a2016-10-31 16:49:19 -0700786 NextObjective.Type type = NextObjective.Type.HASHED;
787 Set<DeviceId> neighbors = ns.getDeviceIds();
788 // If Bos == False and MPLS-ECMP == false, we have
789 // to use simple group and we will pick a single neighbor.
790 if (!isBos && !srManager.getMplsEcmp()) {
791 type = NextObjective.Type.SIMPLE;
792 neighbors = Collections.singleton(ns.getFirstNeighbor());
793 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700794 NextObjective.Builder nextObjBuilder = DefaultNextObjective
Pier Ventre917127a2016-10-31 16:49:19 -0700795 .builder()
796 .withId(nextId)
797 .withType(type)
798 .fromApp(appId);
799 // For each neighbor, we have to update the sent actions
800 for (DeviceId neighborId : neighbors) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800801 if (devicePortMap.get(neighborId) == null) {
802 log.warn("Neighbor {} is not in the port map yet for dev:{}",
803 neighborId, deviceId);
sangho834e4b02015-05-01 09:38:25 -0700804 return;
Jon Hallcbd1b392017-01-18 20:15:44 -0800805 } else if (devicePortMap.get(neighborId).isEmpty()) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700806 log.warn("There are no ports for "
Saurav Das8a0732e2015-11-20 15:27:53 -0800807 + "the Device {} in the port map yet", neighborId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700808 return;
sangho834e4b02015-05-01 09:38:25 -0700809 }
810
Saurav Das8a0732e2015-11-20 15:27:53 -0800811 MacAddress neighborMac;
Charles Chan0b4e6182015-11-03 10:42:14 -0800812 try {
Saurav Das8a0732e2015-11-20 15:27:53 -0800813 neighborMac = deviceConfig.getDeviceMac(neighborId);
Charles Chan0b4e6182015-11-03 10:42:14 -0800814 } catch (DeviceConfigNotFoundException e) {
815 log.warn(e.getMessage() + " Aborting createGroupsFromNeighborsets.");
816 return;
817 }
Pier Ventre917127a2016-10-31 16:49:19 -0700818 // For each port, we have to create a new treatment
819 Set<PortNumber> neighborPorts = devicePortMap.get(neighborId);
820 // In this case we are using a SIMPLE group. We randomly pick a port
821 if (!isBos && !srManager.getMplsEcmp()) {
822 int size = devicePortMap.get(neighborId).size();
823 int index = RandomUtils.nextInt(0, size);
824 neighborPorts = Collections.singleton(
825 Iterables.get(devicePortMap.get(neighborId), index)
826 );
827 }
828 for (PortNumber sp : neighborPorts) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700829 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
830 .builder();
Saurav Das8a0732e2015-11-20 15:27:53 -0800831 tBuilder.setEthDst(neighborMac)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700832 .setEthSrc(nodeMacAddr);
833 if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800834 tBuilder.pushMpls()
835 .copyTtlOut()
836 .setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700837 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800838 tBuilder.setOutput(sp);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700839 nextObjBuilder.addTreatment(tBuilder.build());
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800840 }
841 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800842 if (meta != null) {
Saurav Das4ce45962015-11-24 23:21:05 -0800843 nextObjBuilder.withMeta(meta);
Saurav Das8a0732e2015-11-20 15:27:53 -0800844 }
Charles Chan216e3c82016-04-23 14:48:16 -0700845
846 ObjectiveContext context = new DefaultObjectiveContext(
Pier Ventre917127a2016-10-31 16:49:19 -0700847 (objective) ->
Saurav Das961beb22017-03-29 19:09:17 -0700848 log.debug("createGroupsFromNeighborsets installed "
849 + "NextObj {} on {}", nextId, deviceId),
Charles Chan216e3c82016-04-23 14:48:16 -0700850 (objective, error) ->
Saurav Das961beb22017-03-29 19:09:17 -0700851 log.warn("createGroupsFromNeighborsets failed to install"
852 + " NextObj {} on {}: {}", nextId, deviceId, error)
853 );
Charles Chan216e3c82016-04-23 14:48:16 -0700854 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Das1547b3f2017-05-05 17:01:08 -0700855 log.debug("**createGroupsFromNeighborsets: Submitted "
Saurav Das8a0732e2015-11-20 15:27:53 -0800856 + "next objective {} in device {}",
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700857 nextId, deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800858 flowObjectiveService.next(deviceId, nextObj);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700859 nsNextObjStore.put(new NeighborSetNextObjectiveStoreKey(deviceId, ns),
860 nextId);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800861 }
862 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700863
Saurav Das4ce45962015-11-24 23:21:05 -0800864 /**
Saurav Das1a129a02016-11-18 15:21:57 -0800865 * Creates broadcast groups for all ports in the same subnet for
866 * all configured subnets.
Saurav Das4ce45962015-11-24 23:21:05 -0800867 */
Charles Chan59cc16d2017-02-02 16:20:42 -0800868 public void createGroupsFromVlanConfig() {
Charles Chan7ffd81f2017-02-08 15:52:08 -0800869 srManager.getVlanPortMap(deviceId).asMap().forEach((vlanId, ports) -> {
Charles Chan59cc16d2017-02-02 16:20:42 -0800870 createBcastGroupFromVlan(vlanId, ports);
Pier Ventre10bd8d12016-11-26 21:05:22 -0800871 });
Saurav Das1a129a02016-11-18 15:21:57 -0800872 }
Charles Chan9f676b62015-10-29 14:58:10 -0700873
Saurav Das1a129a02016-11-18 15:21:57 -0800874 /**
Charles Chan59cc16d2017-02-02 16:20:42 -0800875 * Creates a single broadcast group from a given vlan id and list of ports.
Saurav Das1a129a02016-11-18 15:21:57 -0800876 *
Charles Chan59cc16d2017-02-02 16:20:42 -0800877 * @param vlanId vlan id
Saurav Das1a129a02016-11-18 15:21:57 -0800878 * @param ports list of ports in the subnet
879 */
Charles Chan7ffd81f2017-02-08 15:52:08 -0800880 public void createBcastGroupFromVlan(VlanId vlanId, Collection<PortNumber> ports) {
Charles Chan59cc16d2017-02-02 16:20:42 -0800881 VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
Charles Chan9f676b62015-10-29 14:58:10 -0700882
Charles Chan59cc16d2017-02-02 16:20:42 -0800883 if (vlanNextObjStore.containsKey(key)) {
Saurav Das1a129a02016-11-18 15:21:57 -0800884 log.debug("Broadcast group for device {} and subnet {} exists",
Charles Chan59cc16d2017-02-02 16:20:42 -0800885 deviceId, vlanId);
Saurav Das1a129a02016-11-18 15:21:57 -0800886 return;
887 }
Charles Chan188ebf52015-12-23 00:15:11 -0800888
Saurav Das1a129a02016-11-18 15:21:57 -0800889 TrafficSelector metadata =
Charles Chan59cc16d2017-02-02 16:20:42 -0800890 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
Charles Chanc42e84e2015-10-20 16:24:19 -0700891
Saurav Das1a129a02016-11-18 15:21:57 -0800892 int nextId = flowObjectiveService.allocateNextId();
Charles Chanc42e84e2015-10-20 16:24:19 -0700893
Saurav Das1a129a02016-11-18 15:21:57 -0800894 NextObjective.Builder nextObjBuilder = DefaultNextObjective
895 .builder().withId(nextId)
896 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
897 .withMeta(metadata);
Charles Chanc42e84e2015-10-20 16:24:19 -0700898
Saurav Das1a129a02016-11-18 15:21:57 -0800899 ports.forEach(port -> {
900 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Charles Chan7ffd81f2017-02-08 15:52:08 -0800901 if (toPopVlan(port, vlanId)) {
902 tBuilder.popVlan();
903 }
Saurav Das1a129a02016-11-18 15:21:57 -0800904 tBuilder.setOutput(port);
905 nextObjBuilder.addTreatment(tBuilder.build());
Charles Chanc42e84e2015-10-20 16:24:19 -0700906 });
Saurav Das1a129a02016-11-18 15:21:57 -0800907
Saurav Das961beb22017-03-29 19:09:17 -0700908 ObjectiveContext context = new DefaultObjectiveContext(
909 (objective) ->
910 log.debug("createBroadcastGroupFromVlan installed "
911 + "NextObj {} on {}", nextId, deviceId),
912 (objective, error) ->
913 log.warn("createBroadcastGroupFromVlan failed to install"
914 + " NextObj {} on {}: {}", nextId, deviceId, error)
915 );
916 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Das1a129a02016-11-18 15:21:57 -0800917 flowObjectiveService.next(deviceId, nextObj);
Charles Chan59cc16d2017-02-02 16:20:42 -0800918 log.debug("createBcastGroupFromVlan: Submited next objective {} in device {}",
Saurav Das1a129a02016-11-18 15:21:57 -0800919 nextId, deviceId);
920
Charles Chan59cc16d2017-02-02 16:20:42 -0800921 vlanNextObjStore.put(key, nextId);
Charles Chanc42e84e2015-10-20 16:24:19 -0700922 }
923
Charles Chane849c192016-01-11 18:28:54 -0800924 /**
Charles Chan7ffd81f2017-02-08 15:52:08 -0800925 * Determine if we should pop given vlan before sending packets to the given port.
926 *
927 * @param portNumber port number
928 * @param vlanId vlan id
929 * @return true if the vlan id is not contained in any vlanTagged config
930 */
931 private boolean toPopVlan(PortNumber portNumber, VlanId vlanId) {
932 return srManager.interfaceService.getInterfacesByPort(new ConnectPoint(deviceId, portNumber))
933 .stream().noneMatch(intf -> intf.vlanTagged().contains(vlanId));
934 }
935
936 /**
Saurav Das4ce45962015-11-24 23:21:05 -0800937 * Create simple next objective for a single port. The treatments can include
938 * all outgoing actions that need to happen on the packet.
939 *
940 * @param portNum the outgoing port on the device
941 * @param treatment the actions to apply on the packets (should include outport)
942 * @param meta optional data to pass to the driver
943 */
944 public void createGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
945 TrafficSelector meta) {
946 int nextId = flowObjectiveService.allocateNextId();
947 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
Saurav Das76ae6812017-03-15 15:15:14 -0700948 deviceId, portNum, treatment, meta);
Saurav Das4ce45962015-11-24 23:21:05 -0800949
950 NextObjective.Builder nextObjBuilder = DefaultNextObjective
951 .builder().withId(nextId)
952 .withType(NextObjective.Type.SIMPLE)
953 .addTreatment(treatment)
954 .fromApp(appId)
955 .withMeta(meta);
956
Saurav Das961beb22017-03-29 19:09:17 -0700957 ObjectiveContext context = new DefaultObjectiveContext(
958 (objective) ->
959 log.debug("createGroupFromPort installed "
960 + "NextObj {} on {}", nextId, deviceId),
961 (objective, error) ->
962 log.warn("createGroupFromPort failed to install"
963 + " NextObj {} on {}: {}", nextId, deviceId, error)
964 );
965 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Das4ce45962015-11-24 23:21:05 -0800966 flowObjectiveService.next(deviceId, nextObj);
967 log.debug("createGroupFromPort: Submited next objective {} in device {} "
968 + "for port {}", nextId, deviceId, portNum);
969
970 portNextObjStore.put(key, nextId);
971 }
972
sangho1e575652015-05-14 00:39:53 -0700973 /**
974 * Removes groups for the next objective ID given.
975 *
976 * @param objectiveId next objective ID to remove
977 * @return true if succeeds, false otherwise
978 */
979 public boolean removeGroup(int objectiveId) {
980
981 if (nsNextObjStore.containsValue(objectiveId)) {
982 NextObjective.Builder nextObjBuilder = DefaultNextObjective
983 .builder().withId(objectiveId)
984 .withType(NextObjective.Type.HASHED).fromApp(appId);
Charles Chan216e3c82016-04-23 14:48:16 -0700985 ObjectiveContext context = new DefaultObjectiveContext(
986 (objective) -> log.debug("RemoveGroup removes NextObj {} on {}",
987 objectiveId, deviceId),
988 (objective, error) ->
989 log.warn("RemoveGroup failed to remove NextObj {} on {}: {}",
990 objectiveId, deviceId, error));
991 NextObjective nextObjective = nextObjBuilder.remove(context);
Saurav Das8a0732e2015-11-20 15:27:53 -0800992 log.info("**removeGroup: Submited "
993 + "next objective {} in device {}",
994 objectiveId, deviceId);
sangho1e575652015-05-14 00:39:53 -0700995 flowObjectiveService.next(deviceId, nextObjective);
996
997 for (Map.Entry<NeighborSetNextObjectiveStoreKey, Integer> entry: nsNextObjStore.entrySet()) {
998 if (entry.getValue().equals(objectiveId)) {
999 nsNextObjStore.remove(entry.getKey());
1000 break;
1001 }
1002 }
sangho0b2b6d12015-05-20 22:16:38 -07001003 return true;
sangho1e575652015-05-14 00:39:53 -07001004 }
1005
1006 return false;
1007 }
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -07001008
Charles Chane849c192016-01-11 18:28:54 -08001009 /**
1010 * Removes all groups from all next objective stores.
1011 */
Saurav Das423fe2b2015-12-04 10:52:59 -08001012 public void removeAllGroups() {
1013 for (Map.Entry<NeighborSetNextObjectiveStoreKey, Integer> entry:
1014 nsNextObjStore.entrySet()) {
1015 removeGroup(entry.getValue());
1016 }
1017 for (Map.Entry<PortNextObjectiveStoreKey, Integer> entry:
1018 portNextObjStore.entrySet()) {
1019 removeGroup(entry.getValue());
1020 }
Charles Chan59cc16d2017-02-02 16:20:42 -08001021 for (Map.Entry<VlanNextObjectiveStoreKey, Integer> entry:
1022 vlanNextObjStore.entrySet()) {
Saurav Das423fe2b2015-12-04 10:52:59 -08001023 removeGroup(entry.getValue());
1024 }
1025 // should probably clean local stores port-neighbor
1026 }
Saurav Das1547b3f2017-05-05 17:01:08 -07001027
1028 /**
1029 * RetryHashBkts is a one-time retry at populating all the buckets of a
1030 * hash group based on the given link. Should only be called by the
1031 * master instance of the src-device of the link.
1032 */
1033 protected final class RetryHashBkts implements Runnable {
1034 Link link;
1035 MacAddress dstMac;
1036
1037 private RetryHashBkts(Link link, MacAddress dstMac) {
1038 this.link = link;
1039 this.dstMac = dstMac;
1040 }
1041
1042 @Override
1043 public void run() {
1044 log.info("RETRY Hash buckets for linkup: {}", link);
1045 Set<NeighborSet> nsSet = nsNextObjStore.keySet()
1046 .stream()
1047 .filter(nsStoreEntry -> nsStoreEntry.deviceId().equals(deviceId))
1048 .map(nsStoreEntry -> nsStoreEntry.neighborSet())
1049 .filter(ns -> ns.getDeviceIds()
1050 .contains(link.dst().deviceId()))
1051 .collect(Collectors.toSet());
1052 log.debug("retry-link: nsNextObjStore contents for device {}: {}",
1053 deviceId, nsSet);
1054 for (NeighborSet ns : nsSet) {
1055 Integer nextId = nsNextObjStore.
1056 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
1057 if (nextId != null) {
1058 addToHashedNextObjective(link.src().port(), dstMac, ns,
1059 nextId, true);
1060 }
1061 }
1062 }
1063 }
1064
1065
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -08001066}