blob: fec8b39288007cbca7a6c2bd82b065a6685c42d9 [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;
20import org.apache.commons.lang3.RandomUtils;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080021import org.onlab.packet.MacAddress;
sangho32a59322015-02-17 12:07:41 -080022import org.onlab.packet.MplsLabel;
Saurav Das423fe2b2015-12-04 10:52:59 -080023import org.onlab.packet.VlanId;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070024import org.onlab.util.KryoNamespace;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080025import org.onosproject.core.ApplicationId;
Charles Chan59cc16d2017-02-02 16:20:42 -080026import org.onosproject.net.ConnectPoint;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080027import org.onosproject.net.DeviceId;
28import org.onosproject.net.Link;
29import org.onosproject.net.PortNumber;
Saurav Das423fe2b2015-12-04 10:52:59 -080030import org.onosproject.net.flow.DefaultTrafficSelector;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080031import org.onosproject.net.flow.DefaultTrafficTreatment;
Saurav Das8a0732e2015-11-20 15:27:53 -080032import org.onosproject.net.flow.TrafficSelector;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080033import org.onosproject.net.flow.TrafficTreatment;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070034import org.onosproject.net.flowobjective.DefaultNextObjective;
Charles Chan216e3c82016-04-23 14:48:16 -070035import org.onosproject.net.flowobjective.DefaultObjectiveContext;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070036import org.onosproject.net.flowobjective.FlowObjectiveService;
37import org.onosproject.net.flowobjective.NextObjective;
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -070038import org.onosproject.net.flowobjective.ObjectiveContext;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080039import org.onosproject.net.link.LinkService;
Saurav Das423fe2b2015-12-04 10:52:59 -080040import org.onosproject.segmentrouting.SegmentRoutingManager;
Charles Chan0b4e6182015-11-03 10:42:14 -080041import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
42import org.onosproject.segmentrouting.config.DeviceProperties;
Charles Chand2990362016-04-18 13:44:03 -070043import org.onosproject.segmentrouting.storekey.NeighborSetNextObjectiveStoreKey;
44import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
Charles Chan59cc16d2017-02-02 16:20:42 -080045import org.onosproject.segmentrouting.storekey.VlanNextObjectiveStoreKey;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070046import org.onosproject.store.service.EventuallyConsistentMap;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080047import org.slf4j.Logger;
48
Pier Ventre917127a2016-10-31 16:49:19 -070049import java.net.URI;
50import java.util.ArrayList;
Charles Chan7ffd81f2017-02-08 15:52:08 -080051import java.util.Collection;
Pier Ventre917127a2016-10-31 16:49:19 -070052import java.util.Collections;
53import java.util.HashSet;
54import java.util.List;
55import java.util.Map;
56import java.util.Set;
57import java.util.concurrent.ConcurrentHashMap;
Saurav Das1547b3f2017-05-05 17:01:08 -070058import java.util.concurrent.ScheduledExecutorService;
59import java.util.concurrent.TimeUnit;
Pier Ventre917127a2016-10-31 16:49:19 -070060import java.util.stream.Collectors;
61
62import static com.google.common.base.Preconditions.checkNotNull;
Saurav Das1547b3f2017-05-05 17:01:08 -070063import static java.util.concurrent.Executors.newScheduledThreadPool;
64import static org.onlab.util.Tools.groupedThreads;
Charles Chan59cc16d2017-02-02 16:20:42 -080065import static org.onosproject.segmentrouting.SegmentRoutingManager.INTERNAL_VLAN;
Pier Ventre917127a2016-10-31 16:49:19 -070066import static org.slf4j.LoggerFactory.getLogger;
67
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080068/**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070069 * Default ECMP group handler creation module. This component creates a set of
70 * ECMP groups for every neighbor that this device is connected to based on
71 * whether the current device is an edge device or a transit device.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080072 */
73public class DefaultGroupHandler {
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -070074 protected static final Logger log = getLogger(DefaultGroupHandler.class);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080075
76 protected final DeviceId deviceId;
77 protected final ApplicationId appId;
78 protected final DeviceProperties deviceConfig;
79 protected final List<Integer> allSegmentIds;
Pier Ventree0ae7a32016-11-23 09:57:42 -080080 protected int ipv4NodeSegmentId = -1;
81 protected int ipv6NodeSegmentId = -1;
Charles Chan0b4e6182015-11-03 10:42:14 -080082 protected boolean isEdgeRouter = false;
83 protected MacAddress nodeMacAddr = null;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080084 protected LinkService linkService;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070085 protected FlowObjectiveService flowObjectiveService;
Saurav Das423fe2b2015-12-04 10:52:59 -080086 // local store for neighbor-device-ids and the set of ports on this device
87 // that connect to the same neighbor
Saurav Das8a0732e2015-11-20 15:27:53 -080088 protected ConcurrentHashMap<DeviceId, Set<PortNumber>> devicePortMap =
89 new ConcurrentHashMap<>();
Saurav Das1a129a02016-11-18 15:21:57 -080090 // local store for ports on this device connected to neighbor-device-id
Saurav Das8a0732e2015-11-20 15:27:53 -080091 protected ConcurrentHashMap<PortNumber, DeviceId> portDeviceMap =
92 new ConcurrentHashMap<>();
Saurav Das1a129a02016-11-18 15:21:57 -080093 // distributed store for (device+neighborset) mapped to next-id
Charles Chane849c192016-01-11 18:28:54 -080094 protected EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey, Integer>
95 nsNextObjStore = null;
Saurav Das1a129a02016-11-18 15:21:57 -080096 // distributed store for (device+subnet-ip-prefix) mapped to next-id
Charles Chan59cc16d2017-02-02 16:20:42 -080097 protected EventuallyConsistentMap<VlanNextObjectiveStoreKey, Integer>
98 vlanNextObjStore = null;
Saurav Das1a129a02016-11-18 15:21:57 -080099 // distributed store for (device+port+treatment) mapped to next-id
Charles Chane849c192016-01-11 18:28:54 -0800100 protected EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
101 portNextObjStore = null;
Charles Chan188ebf52015-12-23 00:15:11 -0800102 private SegmentRoutingManager srManager;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800103
Saurav Das1547b3f2017-05-05 17:01:08 -0700104 private static final long RETRY_INTERVAL_SEC = 30;
105 private ScheduledExecutorService executorService
106 = newScheduledThreadPool(1, groupedThreads("retryhashbkts", "retry-%d", log));
107
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700108 protected KryoNamespace.Builder kryo = new KryoNamespace.Builder()
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700109 .register(URI.class).register(HashSet.class)
110 .register(DeviceId.class).register(PortNumber.class)
111 .register(NeighborSet.class).register(PolicyGroupIdentifier.class)
112 .register(PolicyGroupParams.class)
113 .register(GroupBucketIdentifier.class)
114 .register(GroupBucketIdentifier.BucketOutputType.class);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800115
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700116 protected DefaultGroupHandler(DeviceId deviceId, ApplicationId appId,
117 DeviceProperties config,
118 LinkService linkService,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700119 FlowObjectiveService flowObjService,
Charles Chan188ebf52015-12-23 00:15:11 -0800120 SegmentRoutingManager srManager) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800121 this.deviceId = checkNotNull(deviceId);
122 this.appId = checkNotNull(appId);
123 this.deviceConfig = checkNotNull(config);
124 this.linkService = checkNotNull(linkService);
Charles Chan0b4e6182015-11-03 10:42:14 -0800125 this.allSegmentIds = checkNotNull(config.getAllDeviceSegmentIds());
126 try {
Pier Ventree0ae7a32016-11-23 09:57:42 -0800127 this.ipv4NodeSegmentId = config.getIPv4SegmentId(deviceId);
128 this.ipv6NodeSegmentId = config.getIPv6SegmentId(deviceId);
Charles Chan0b4e6182015-11-03 10:42:14 -0800129 this.isEdgeRouter = config.isEdgeDevice(deviceId);
130 this.nodeMacAddr = checkNotNull(config.getDeviceMac(deviceId));
131 } catch (DeviceConfigNotFoundException e) {
132 log.warn(e.getMessage()
133 + " Skipping value assignment in DefaultGroupHandler");
134 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700135 this.flowObjectiveService = flowObjService;
Ray Milkeye4afdb52017-04-05 09:42:04 -0700136 this.nsNextObjStore = srManager.nsNextObjStore();
137 this.vlanNextObjStore = srManager.vlanNextObjStore();
138 this.portNextObjStore = srManager.portNextObjStore();
Charles Chan188ebf52015-12-23 00:15:11 -0800139 this.srManager = srManager;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800140
141 populateNeighborMaps();
142 }
143
144 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700145 * Creates a group handler object based on the type of device. If device is
146 * of edge type it returns edge group handler, else it returns transit group
147 * handler.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800148 *
149 * @param deviceId device identifier
150 * @param appId application identifier
151 * @param config interface to retrieve the device properties
152 * @param linkService link service object
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700153 * @param flowObjService flow objective service object
Charles Chane849c192016-01-11 18:28:54 -0800154 * @param srManager segment routing manager
Charles Chan0b4e6182015-11-03 10:42:14 -0800155 * @throws DeviceConfigNotFoundException if the device configuration is not found
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800156 * @return default group handler type
157 */
Saurav Das4ce45962015-11-24 23:21:05 -0800158 public static DefaultGroupHandler createGroupHandler(
159 DeviceId deviceId,
160 ApplicationId appId,
161 DeviceProperties config,
162 LinkService linkService,
163 FlowObjectiveService flowObjService,
Charles Chan188ebf52015-12-23 00:15:11 -0800164 SegmentRoutingManager srManager)
Saurav Das4ce45962015-11-24 23:21:05 -0800165 throws DeviceConfigNotFoundException {
Charles Chan0b4e6182015-11-03 10:42:14 -0800166 // handle possible exception in the caller
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800167 if (config.isEdgeDevice(deviceId)) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700168 return new DefaultEdgeGroupHandler(deviceId, appId, config,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700169 linkService,
170 flowObjService,
Charles Chan188ebf52015-12-23 00:15:11 -0800171 srManager
172 );
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800173 } else {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700174 return new DefaultTransitGroupHandler(deviceId, appId, config,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700175 linkService,
176 flowObjService,
Charles Chan188ebf52015-12-23 00:15:11 -0800177 srManager);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800178 }
179 }
180
181 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700182 * Creates the auto created groups for this device based on the current
183 * snapshot of the topology.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800184 */
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700185 // Empty implementations to be overridden by derived classes
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800186 public void createGroups() {
187 }
188
189 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700190 * Performs group creation or update procedures when a new link is
191 * discovered on this device.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800192 *
193 * @param newLink new neighbor link
Saurav Das1547b3f2017-05-05 17:01:08 -0700194 * @param isMaster true if local instance is the master for src-device of link
Charles Chane849c192016-01-11 18:28:54 -0800195 *
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800196 */
Saurav Das8a0732e2015-11-20 15:27:53 -0800197 public void linkUp(Link newLink, boolean isMaster) {
sanghob35a6192015-04-01 13:05:26 -0700198
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800199 if (newLink.type() != Link.Type.DIRECT) {
Charles Chanb1f8c762017-03-29 16:39:05 -0700200 // NOTE: A DIRECT link might be transiently marked as INDIRECT
201 // if BDDP is received before LLDP. We can safely ignore that
202 // until the LLDP is received and the link is marked as DIRECT.
203 log.info("Ignore link {}->{}. Link type is {} instead of DIRECT.",
204 newLink.src(), newLink.dst(), newLink.type());
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800205 return;
206 }
207
208 if (!newLink.src().deviceId().equals(deviceId)) {
Charles Chanb1f8c762017-03-29 16:39:05 -0700209 log.warn("linkUp: deviceId{} doesn't match with link src {}",
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700210 deviceId, newLink.src().deviceId());
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800211 return;
212 }
213
Saurav Das8a0732e2015-11-20 15:27:53 -0800214 log.info("* LinkUP: Device {} linkUp at local port {} to neighbor {}", deviceId,
215 newLink.src().port(), newLink.dst().deviceId());
Saurav Das59232cf2016-04-27 18:35:50 -0700216 // ensure local state is updated even if linkup is aborted later on
217 addNeighborAtPort(newLink.dst().deviceId(),
218 newLink.src().port());
219
Charles Chan0b4e6182015-11-03 10:42:14 -0800220 MacAddress dstMac;
221 try {
222 dstMac = deviceConfig.getDeviceMac(newLink.dst().deviceId());
223 } catch (DeviceConfigNotFoundException e) {
224 log.warn(e.getMessage() + " Aborting linkUp.");
225 return;
226 }
227
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700228 /*if (devicePortMap.get(newLink.dst().deviceId()) == null) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800229 // New Neighbor
230 newNeighbor(newLink);
231 } else {
232 // Old Neighbor
233 newPortToExistingNeighbor(newLink);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700234 }*/
235 Set<NeighborSet> nsSet = nsNextObjStore.keySet()
236 .stream()
237 .filter((nsStoreEntry) -> (nsStoreEntry.deviceId().equals(deviceId)))
238 .map((nsStoreEntry) -> (nsStoreEntry.neighborSet()))
239 .filter((ns) -> (ns.getDeviceIds()
240 .contains(newLink.dst().deviceId())))
241 .collect(Collectors.toSet());
Saurav Das1547b3f2017-05-05 17:01:08 -0700242 log.debug("linkUp: nsNextObjStore contents for device {}: {}",
243 deviceId, nsSet);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700244 for (NeighborSet ns : nsSet) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700245 Integer nextId = nsNextObjStore.
246 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
Saurav Das4ce45962015-11-24 23:21:05 -0800247 if (nextId != null && isMaster) {
Saurav Das1547b3f2017-05-05 17:01:08 -0700248 addToHashedNextObjective(newLink.src().port(), dstMac, ns,
249 nextId, false);
250 // some links may have come up before the next-objective was created
251 // we take this opportunity to ensure other ports to same next-hop-dst
252 // are part of the hash group (see CORD-1180). Duplicate additions
253 // to the same hash group are avoided by the driver.
254 for (PortNumber p : devicePortMap.get(newLink.dst().deviceId())) {
255 if (p.equals(newLink.src().port())) {
256 continue;
257 }
258 addToHashedNextObjective(p, dstMac, ns, nextId, false);
Saurav Das423fe2b2015-12-04 10:52:59 -0800259 }
Saurav Das4ce45962015-11-24 23:21:05 -0800260 } else if (isMaster) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800261 log.warn("linkUp in device {}, but global store has no record "
262 + "for neighbor-set {}", deviceId, ns);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700263 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800264 }
Saurav Das1547b3f2017-05-05 17:01:08 -0700265
266 // It's possible that at the time of linkup, some hash-groups have
267 // not been created yet by the instance responsible for creating them, or
268 // due to the eventually-consistent nature of the nsNextObjStore it has
269 // not synced up with this instance yet. Thus we perform this check again
270 // after a delay (see CORD-1180). Duplicate additions to the same hash group
271 // are avoided by the driver.
272 if (isMaster) {
273 executorService.schedule(new RetryHashBkts(newLink, dstMac),
274 RETRY_INTERVAL_SEC, TimeUnit.SECONDS);
275 }
276 }
277
278 private void addToHashedNextObjective(PortNumber outport, MacAddress dstMac,
279 NeighborSet ns, Integer nextId, boolean retry) {
280 // Create the new bucket to be updated
281 TrafficTreatment.Builder tBuilder =
282 DefaultTrafficTreatment.builder();
283 tBuilder.setOutput(outport)
284 .setEthDst(dstMac)
285 .setEthSrc(nodeMacAddr);
286 if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
287 tBuilder.pushMpls()
288 .copyTtlOut()
289 .setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
290 }
291 // setup metadata to pass to nextObjective - indicate the vlan on egress
292 // if needed by the switch pipeline. Since hashed next-hops are always to
293 // other neighboring routers, there is no subnet assigned on those ports.
294 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
295 metabuilder.matchVlanId(INTERNAL_VLAN);
296
297 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
298 .withId(nextId)
299 .withType(NextObjective.Type.HASHED)
300 .addTreatment(tBuilder.build())
301 .withMeta(metabuilder.build())
302 .fromApp(appId);
303 log.info("{} in device {}: Adding Bucket with Port {} to next object id {}",
304 (retry) ? "**retry" : "**linkup",
305 deviceId, outport, nextId);
306
307 ObjectiveContext context = new DefaultObjectiveContext(
308 (objective) -> log.debug("LinkUp addedTo NextObj {} on {}",
309 nextId, deviceId),
310 (objective, error) ->
311 log.warn("LinkUp failed to addTo NextObj {} on {}: {}",
312 nextId, deviceId, error));
313 NextObjective nextObjective = nextObjBuilder.addToExisting(context);
314 flowObjectiveService.next(deviceId, nextObjective);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800315 }
316
317 /**
Saurav Das1a129a02016-11-18 15:21:57 -0800318 * Performs hash group recovery procedures when a switch-to-switch
319 * port goes down on this device.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800320 *
321 * @param port port number that has gone down
Charles Chane849c192016-01-11 18:28:54 -0800322 * @param isMaster true if local instance is the master
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800323 */
Saurav Das423fe2b2015-12-04 10:52:59 -0800324 public void portDown(PortNumber port, boolean isMaster) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800325 if (portDeviceMap.get(port) == null) {
326 log.warn("portDown: unknown port");
327 return;
328 }
Charles Chan0b4e6182015-11-03 10:42:14 -0800329
330 MacAddress dstMac;
331 try {
332 dstMac = deviceConfig.getDeviceMac(portDeviceMap.get(port));
333 } catch (DeviceConfigNotFoundException e) {
334 log.warn(e.getMessage() + " Aborting portDown.");
335 return;
336 }
337
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700338 log.debug("Device {} portDown {} to neighbor {}", deviceId, port,
339 portDeviceMap.get(port));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700340 /*Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(portDeviceMap
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700341 .get(port),
342 devicePortMap
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700343 .keySet());*/
344 Set<NeighborSet> nsSet = nsNextObjStore.keySet()
345 .stream()
346 .filter((nsStoreEntry) -> (nsStoreEntry.deviceId().equals(deviceId)))
347 .map((nsStoreEntry) -> (nsStoreEntry.neighborSet()))
348 .filter((ns) -> (ns.getDeviceIds()
349 .contains(portDeviceMap.get(port))))
350 .collect(Collectors.toSet());
Saurav Das423fe2b2015-12-04 10:52:59 -0800351 log.debug("portDown: nsNextObjStore contents for device {}:{}",
352 deviceId, nsSet);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800353 for (NeighborSet ns : nsSet) {
Saurav Das80980c72016-03-23 11:22:49 -0700354 NeighborSetNextObjectiveStoreKey nsStoreKey =
355 new NeighborSetNextObjectiveStoreKey(deviceId, ns);
356 Integer nextId = nsNextObjStore.get(nsStoreKey);
Saurav Das423fe2b2015-12-04 10:52:59 -0800357 if (nextId != null && isMaster) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800358 log.info("**portDown in device {}: Removing Bucket "
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700359 + "with Port {} to next object id {}",
360 deviceId,
361 port,
362 nextId);
Saurav Das423fe2b2015-12-04 10:52:59 -0800363 // Create the bucket to be removed
364 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
365 .builder();
366 tBuilder.setOutput(port)
367 .setEthDst(dstMac)
368 .setEthSrc(nodeMacAddr);
369 if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
370 tBuilder.pushMpls()
371 .copyTtlOut()
372 .setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
373 }
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());
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700380 ObjectiveContext context = new DefaultObjectiveContext(
381 (objective) -> log.debug("portDown removedFrom NextObj {} on {}",
382 nextId, deviceId),
383 (objective, error) ->
384 log.warn("portDown failed to removeFrom NextObj {} on {}: {}",
385 nextId, deviceId, error));
Saurav Das423fe2b2015-12-04 10:52:59 -0800386 NextObjective nextObjective = nextObjBuilder.
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700387 removeFromExisting(context);
sangho834e4b02015-05-01 09:38:25 -0700388
Saurav Das423fe2b2015-12-04 10:52:59 -0800389 flowObjectiveService.next(deviceId, nextObjective);
sangho834e4b02015-05-01 09:38:25 -0700390 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800391 }
392
393 devicePortMap.get(portDeviceMap.get(port)).remove(port);
394 portDeviceMap.remove(port);
395 }
396
397 /**
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800398 * Adds or removes a port that has been configured with a vlan to a broadcast group
399 * for bridging. Should only be called by the master instance for this device.
Saurav Das1a129a02016-11-18 15:21:57 -0800400 *
401 * @param port the port on this device that needs to be added/removed to a bcast group
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800402 * @param vlanId the vlan id corresponding to the broadcast domain/group
403 * @param popVlan indicates if packets should be sent out untagged or not out
404 * of the port. If true, indicates an access (untagged) or native vlan
405 * configuration. If false, indicates a trunk (tagged) vlan config.
Saurav Das1a129a02016-11-18 15:21:57 -0800406 * @param portUp true if port is enabled, false if disabled
Saurav Das1a129a02016-11-18 15:21:57 -0800407 */
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800408 public void processEdgePort(PortNumber port, VlanId vlanId,
409 boolean popVlan, boolean portUp) {
Saurav Das1a129a02016-11-18 15:21:57 -0800410 //get the next id for the subnet and edit it.
Charles Chan59cc16d2017-02-02 16:20:42 -0800411 Integer nextId = getVlanNextObjectiveId(vlanId);
Saurav Das1a129a02016-11-18 15:21:57 -0800412 if (nextId == -1) {
413 if (portUp) {
414 log.debug("**Creating flooding group for first port enabled in"
Charles Chan59cc16d2017-02-02 16:20:42 -0800415 + " subnet {} on dev {} port {}", vlanId, deviceId, port);
416 createBcastGroupFromVlan(vlanId, Collections.singleton(port));
Saurav Das1a129a02016-11-18 15:21:57 -0800417 } else {
418 log.warn("Could not find flooding group for subnet {} on dev:{} when"
Charles Chan59cc16d2017-02-02 16:20:42 -0800419 + " removing port:{}", vlanId, deviceId, port);
Saurav Das1a129a02016-11-18 15:21:57 -0800420 }
421 return;
422 }
423
424 log.info("**port{} in device {}: {} Bucket with Port {} to"
425 + " next-id {}", (portUp) ? "UP" : "DOWN", deviceId,
426 (portUp) ? "Adding" : "Removing",
427 port, nextId);
428 // Create the bucket to be added or removed
429 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800430 if (popVlan) {
431 tBuilder.popVlan();
432 }
Saurav Das1a129a02016-11-18 15:21:57 -0800433 tBuilder.setOutput(port);
434
Saurav Das1a129a02016-11-18 15:21:57 -0800435 TrafficSelector metadata =
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800436 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
Saurav Das1a129a02016-11-18 15:21:57 -0800437
438 NextObjective.Builder nextObjBuilder = DefaultNextObjective
439 .builder().withId(nextId)
440 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
441 .addTreatment(tBuilder.build())
442 .withMeta(metadata);
443
444 ObjectiveContext context = new DefaultObjectiveContext(
445 (objective) -> log.debug("port {} successfully {} NextObj {} on {}",
446 port, (portUp) ? "addedTo" : "removedFrom",
447 nextId, deviceId),
448 (objective, error) ->
449 log.warn("port {} failed to {} NextObj {} on {}: {}",
450 port, (portUp) ? "addTo" : "removeFrom",
451 nextId, deviceId, error));
452
453 NextObjective nextObj = (portUp) ? nextObjBuilder.addToExisting(context)
454 : nextObjBuilder.removeFromExisting(context);
455 log.debug("edgePort processed: Submited next objective {} in device {}",
456 nextId, deviceId);
457 flowObjectiveService.next(deviceId, nextObj);
458 }
459
460 /**
Saurav Das4ce45962015-11-24 23:21:05 -0800461 * Returns the next objective of type hashed associated with the neighborset.
462 * If there is no next objective for this neighborset, this method
Saurav Das8a0732e2015-11-20 15:27:53 -0800463 * would create a next objective and return. Optionally metadata can be
464 * passed in for the creation of the next objective.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800465 *
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700466 * @param ns neighborset
Saurav Das8a0732e2015-11-20 15:27:53 -0800467 * @param meta metadata passed into the creation of a Next Objective
Pier Ventre917127a2016-10-31 16:49:19 -0700468 * @param isBos if Bos is set
Saurav Das8a0732e2015-11-20 15:27:53 -0800469 * @return int if found or -1 if there are errors in the creation of the
470 * neighbor set.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800471 */
Pier Ventre917127a2016-10-31 16:49:19 -0700472 public int getNextObjectiveId(NeighborSet ns, TrafficSelector meta, boolean isBos) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700473 Integer nextId = nsNextObjStore.
474 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700475 if (nextId == null) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700476 log.trace("getNextObjectiveId in device{}: Next objective id "
477 + "not found for {} and creating", deviceId, ns);
478 log.trace("getNextObjectiveId: nsNextObjStore contents for device {}: {}",
479 deviceId,
480 nsNextObjStore.entrySet()
481 .stream()
482 .filter((nsStoreEntry) ->
483 (nsStoreEntry.getKey().deviceId().equals(deviceId)))
484 .collect(Collectors.toList()));
Pier Ventre917127a2016-10-31 16:49:19 -0700485 createGroupsFromNeighborsets(Collections.singleton(ns), meta, isBos);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700486 nextId = nsNextObjStore.
487 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700488 if (nextId == null) {
489 log.warn("getNextObjectiveId: unable to create next objective");
490 return -1;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700491 } else {
492 log.debug("getNextObjectiveId in device{}: Next objective id {} "
Sho SHIMIZUaf973432015-09-11 14:24:50 -0700493 + "created for {}", deviceId, nextId, ns);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700494 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700495 } else {
496 log.trace("getNextObjectiveId in device{}: Next objective id {} "
Sho SHIMIZUaf973432015-09-11 14:24:50 -0700497 + "found for {}", deviceId, nextId, ns);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700498 }
Sho SHIMIZUaf973432015-09-11 14:24:50 -0700499 return nextId;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800500 }
501
sangho0b2b6d12015-05-20 22:16:38 -0700502 /**
Charles Chan59cc16d2017-02-02 16:20:42 -0800503 * Returns the next objective of type broadcast associated with the vlan,
Saurav Das4ce45962015-11-24 23:21:05 -0800504 * or -1 if no such objective exists. Note that this method does NOT create
505 * the next objective as a side-effect. It is expected that is objective is
Saurav Das1a129a02016-11-18 15:21:57 -0800506 * created at startup from network configuration. Typically this is used
507 * for L2 flooding within the subnet configured on the switch.
Charles Chanc42e84e2015-10-20 16:24:19 -0700508 *
Charles Chan59cc16d2017-02-02 16:20:42 -0800509 * @param vlanId vlan id
Charles Chanc42e84e2015-10-20 16:24:19 -0700510 * @return int if found or -1
511 */
Charles Chan59cc16d2017-02-02 16:20:42 -0800512 public int getVlanNextObjectiveId(VlanId vlanId) {
513 Integer nextId = vlanNextObjStore.
514 get(new VlanNextObjectiveStoreKey(deviceId, vlanId));
Charles Chan9f676b62015-10-29 14:58:10 -0700515
516 return (nextId != null) ? nextId : -1;
Charles Chanc42e84e2015-10-20 16:24:19 -0700517 }
518
519 /**
Saurav Das4ce45962015-11-24 23:21:05 -0800520 * Returns the next objective of type simple associated with the port on the
521 * device, given the treatment. Different treatments to the same port result
522 * in different next objectives. If no such objective exists, this method
Saurav Das961beb22017-03-29 19:09:17 -0700523 * creates one (if requested) and returns the id. Optionally metadata can be passed in for
Saurav Das1a129a02016-11-18 15:21:57 -0800524 * the creation of the objective. Typically this is used for L2 and L3 forwarding
525 * to compute nodes and containers/VMs on the compute nodes directly attached
526 * to the switch.
Saurav Das4ce45962015-11-24 23:21:05 -0800527 *
528 * @param portNum the port number for the simple next objective
529 * @param treatment the actions to apply on the packets (should include outport)
530 * @param meta optional metadata passed into the creation of the next objective
Saurav Das961beb22017-03-29 19:09:17 -0700531 * @param createIfMissing true if a next object should be created if not found
Saurav Das4ce45962015-11-24 23:21:05 -0800532 * @return int if found or created, -1 if there are errors during the
533 * creation of the next objective.
534 */
535 public int getPortNextObjectiveId(PortNumber portNum, TrafficTreatment treatment,
Saurav Das961beb22017-03-29 19:09:17 -0700536 TrafficSelector meta, boolean createIfMissing) {
Charles Chane849c192016-01-11 18:28:54 -0800537 Integer nextId = portNextObjStore
Saurav Das76ae6812017-03-15 15:15:14 -0700538 .get(new PortNextObjectiveStoreKey(deviceId, portNum, treatment, meta));
Saurav Das961beb22017-03-29 19:09:17 -0700539 if (nextId != null) {
540 return nextId;
541 }
542 log.debug("getPortNextObjectiveId in device {}: Next objective id "
543 + "not found for port: {} .. {}", deviceId, portNum,
544 (createIfMissing) ? "creating" : "aborting");
545 if (!createIfMissing) {
546 return -1;
547 }
548 // create missing next objective
549 createGroupFromPort(portNum, treatment, meta);
550 nextId = portNextObjStore.get(new PortNextObjectiveStoreKey(deviceId, portNum,
551 treatment, meta));
Saurav Das4ce45962015-11-24 23:21:05 -0800552 if (nextId == null) {
Saurav Das961beb22017-03-29 19:09:17 -0700553 log.warn("getPortNextObjectiveId: unable to create next obj"
554 + "for dev:{} port:{}", deviceId, portNum);
555 return -1;
Charles Chane849c192016-01-11 18:28:54 -0800556 }
557 return nextId;
558 }
559
560 /**
sangho0b2b6d12015-05-20 22:16:38 -0700561 * Checks if the next objective ID (group) for the neighbor set exists or not.
562 *
563 * @param ns neighbor set to check
564 * @return true if it exists, false otherwise
565 */
566 public boolean hasNextObjectiveId(NeighborSet ns) {
567 Integer nextId = nsNextObjStore.
568 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
569 if (nextId == null) {
570 return false;
571 }
572
573 return true;
574 }
575
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700576 // Empty implementation
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800577 protected void newNeighbor(Link newLink) {
578 }
579
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700580 // Empty implementation
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800581 protected void newPortToExistingNeighbor(Link newLink) {
582 }
583
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700584 // Empty implementation
585 protected Set<NeighborSet>
586 computeImpactedNeighborsetForPortEvent(DeviceId impactedNeighbor,
587 Set<DeviceId> updatedNeighbors) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800588 return null;
589 }
590
591 private void populateNeighborMaps() {
592 Set<Link> outgoingLinks = linkService.getDeviceEgressLinks(deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700593 for (Link link : outgoingLinks) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800594 if (link.type() != Link.Type.DIRECT) {
595 continue;
596 }
597 addNeighborAtPort(link.dst().deviceId(), link.src().port());
598 }
599 }
600
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700601 protected void addNeighborAtPort(DeviceId neighborId,
602 PortNumber portToNeighbor) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800603 // Update DeviceToPort database
604 log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
605 deviceId, neighborId, portToNeighbor);
Saurav Das8a0732e2015-11-20 15:27:53 -0800606 Set<PortNumber> ports = Collections
607 .newSetFromMap(new ConcurrentHashMap<PortNumber, Boolean>());
608 ports.add(portToNeighbor);
609 Set<PortNumber> portnums = devicePortMap.putIfAbsent(neighborId, ports);
610 if (portnums != null) {
611 portnums.add(portToNeighbor);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800612 }
613
614 // Update portToDevice database
Saurav Das8a0732e2015-11-20 15:27:53 -0800615 DeviceId prev = portDeviceMap.putIfAbsent(portToNeighbor, neighborId);
616 if (prev != null) {
617 log.warn("Device: {} port: {} has neighbor: {}. NOT updating "
618 + "to neighbor: {}", deviceId, portToNeighbor, prev, neighborId);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800619 }
620 }
621
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700622 protected Set<Set<DeviceId>> getPowerSetOfNeighbors(Set<DeviceId> neighbors) {
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700623 List<DeviceId> list = new ArrayList<>(neighbors);
624 Set<Set<DeviceId>> sets = new HashSet<>();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800625 // get the number of elements in the neighbors
626 int elements = list.size();
627 // the number of members of a power set is 2^n
628 // including the empty set
629 int powerElements = (1 << elements);
630
631 // run a binary counter for the number of power elements
632 // NOTE: Exclude empty set
633 for (long i = 1; i < powerElements; i++) {
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700634 Set<DeviceId> neighborSubSet = new HashSet<>();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800635 for (int j = 0; j < elements; j++) {
636 if ((i >> j) % 2 == 1) {
637 neighborSubSet.add(list.get(j));
638 }
639 }
640 sets.add(neighborSubSet);
641 }
642 return sets;
643 }
644
645 private boolean isSegmentIdSameAsNodeSegmentId(DeviceId deviceId, int sId) {
Charles Chan0b4e6182015-11-03 10:42:14 -0800646 int segmentId;
647 try {
Pier Ventre917127a2016-10-31 16:49:19 -0700648 // IPv6 sid is not inserted. this part of the code is not used for now.
Pier Ventree0ae7a32016-11-23 09:57:42 -0800649 segmentId = deviceConfig.getIPv4SegmentId(deviceId);
Charles Chan0b4e6182015-11-03 10:42:14 -0800650 } catch (DeviceConfigNotFoundException e) {
651 log.warn(e.getMessage() + " Aborting isSegmentIdSameAsNodeSegmentId.");
652 return false;
653 }
654
655 return segmentId == sId;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800656 }
657
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700658 protected List<Integer> getSegmentIdsTobePairedWithNeighborSet(Set<DeviceId> neighbors) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800659
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700660 List<Integer> nsSegmentIds = new ArrayList<>();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800661
sanghob35a6192015-04-01 13:05:26 -0700662 // Always pair up with no edge label
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700663 // If (neighbors.size() == 1) {
sanghob35a6192015-04-01 13:05:26 -0700664 nsSegmentIds.add(-1);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700665 // }
sanghob35a6192015-04-01 13:05:26 -0700666
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800667 // Filter out SegmentIds matching with the
668 // nodes in the combo
669 for (Integer sId : allSegmentIds) {
Pier Ventree0ae7a32016-11-23 09:57:42 -0800670 if (sId.equals(this.ipv4NodeSegmentId)) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800671 continue;
672 }
673 boolean filterOut = false;
674 // Check if the edge label being set is of
675 // any node in the Neighbor set
676 for (DeviceId deviceId : neighbors) {
677 if (isSegmentIdSameAsNodeSegmentId(deviceId, sId)) {
678 filterOut = true;
679 break;
680 }
681 }
682 if (!filterOut) {
683 nsSegmentIds.add(sId);
684 }
685 }
686 return nsSegmentIds;
687 }
688
sangho1e575652015-05-14 00:39:53 -0700689 /**
Saurav Das1a129a02016-11-18 15:21:57 -0800690 * Creates hash groups from a set of NeighborSet given.
sangho1e575652015-05-14 00:39:53 -0700691 *
692 * @param nsSet a set of NeighborSet
Saurav Das8a0732e2015-11-20 15:27:53 -0800693 * @param meta metadata passed into the creation of a Next Objective
Pier Ventre917127a2016-10-31 16:49:19 -0700694 * @param isBos if BoS is set
sangho1e575652015-05-14 00:39:53 -0700695 */
Saurav Das8a0732e2015-11-20 15:27:53 -0800696 public void createGroupsFromNeighborsets(Set<NeighborSet> nsSet,
Pier Ventre917127a2016-10-31 16:49:19 -0700697 TrafficSelector meta,
698 boolean isBos) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800699 for (NeighborSet ns : nsSet) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700700 int nextId = flowObjectiveService.allocateNextId();
Pier Ventre917127a2016-10-31 16:49:19 -0700701 NextObjective.Type type = NextObjective.Type.HASHED;
702 Set<DeviceId> neighbors = ns.getDeviceIds();
703 // If Bos == False and MPLS-ECMP == false, we have
704 // to use simple group and we will pick a single neighbor.
705 if (!isBos && !srManager.getMplsEcmp()) {
706 type = NextObjective.Type.SIMPLE;
707 neighbors = Collections.singleton(ns.getFirstNeighbor());
708 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700709 NextObjective.Builder nextObjBuilder = DefaultNextObjective
Pier Ventre917127a2016-10-31 16:49:19 -0700710 .builder()
711 .withId(nextId)
712 .withType(type)
713 .fromApp(appId);
714 // For each neighbor, we have to update the sent actions
715 for (DeviceId neighborId : neighbors) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800716 if (devicePortMap.get(neighborId) == null) {
717 log.warn("Neighbor {} is not in the port map yet for dev:{}",
718 neighborId, deviceId);
sangho834e4b02015-05-01 09:38:25 -0700719 return;
Jon Hallcbd1b392017-01-18 20:15:44 -0800720 } else if (devicePortMap.get(neighborId).isEmpty()) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700721 log.warn("There are no ports for "
Saurav Das8a0732e2015-11-20 15:27:53 -0800722 + "the Device {} in the port map yet", neighborId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700723 return;
sangho834e4b02015-05-01 09:38:25 -0700724 }
725
Saurav Das8a0732e2015-11-20 15:27:53 -0800726 MacAddress neighborMac;
Charles Chan0b4e6182015-11-03 10:42:14 -0800727 try {
Saurav Das8a0732e2015-11-20 15:27:53 -0800728 neighborMac = deviceConfig.getDeviceMac(neighborId);
Charles Chan0b4e6182015-11-03 10:42:14 -0800729 } catch (DeviceConfigNotFoundException e) {
730 log.warn(e.getMessage() + " Aborting createGroupsFromNeighborsets.");
731 return;
732 }
Pier Ventre917127a2016-10-31 16:49:19 -0700733 // For each port, we have to create a new treatment
734 Set<PortNumber> neighborPorts = devicePortMap.get(neighborId);
735 // In this case we are using a SIMPLE group. We randomly pick a port
736 if (!isBos && !srManager.getMplsEcmp()) {
737 int size = devicePortMap.get(neighborId).size();
738 int index = RandomUtils.nextInt(0, size);
739 neighborPorts = Collections.singleton(
740 Iterables.get(devicePortMap.get(neighborId), index)
741 );
742 }
743 for (PortNumber sp : neighborPorts) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700744 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
745 .builder();
Saurav Das8a0732e2015-11-20 15:27:53 -0800746 tBuilder.setEthDst(neighborMac)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700747 .setEthSrc(nodeMacAddr);
748 if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800749 tBuilder.pushMpls()
750 .copyTtlOut()
751 .setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700752 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800753 tBuilder.setOutput(sp);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700754 nextObjBuilder.addTreatment(tBuilder.build());
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800755 }
756 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800757 if (meta != null) {
Saurav Das4ce45962015-11-24 23:21:05 -0800758 nextObjBuilder.withMeta(meta);
Saurav Das8a0732e2015-11-20 15:27:53 -0800759 }
Charles Chan216e3c82016-04-23 14:48:16 -0700760
761 ObjectiveContext context = new DefaultObjectiveContext(
Pier Ventre917127a2016-10-31 16:49:19 -0700762 (objective) ->
Saurav Das961beb22017-03-29 19:09:17 -0700763 log.debug("createGroupsFromNeighborsets installed "
764 + "NextObj {} on {}", nextId, deviceId),
Charles Chan216e3c82016-04-23 14:48:16 -0700765 (objective, error) ->
Saurav Das961beb22017-03-29 19:09:17 -0700766 log.warn("createGroupsFromNeighborsets failed to install"
767 + " NextObj {} on {}: {}", nextId, deviceId, error)
768 );
Charles Chan216e3c82016-04-23 14:48:16 -0700769 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Das1547b3f2017-05-05 17:01:08 -0700770 log.debug("**createGroupsFromNeighborsets: Submitted "
Saurav Das8a0732e2015-11-20 15:27:53 -0800771 + "next objective {} in device {}",
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700772 nextId, deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800773 flowObjectiveService.next(deviceId, nextObj);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700774 nsNextObjStore.put(new NeighborSetNextObjectiveStoreKey(deviceId, ns),
775 nextId);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800776 }
777 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700778
Saurav Das4ce45962015-11-24 23:21:05 -0800779 /**
Saurav Das1a129a02016-11-18 15:21:57 -0800780 * Creates broadcast groups for all ports in the same subnet for
781 * all configured subnets.
Saurav Das4ce45962015-11-24 23:21:05 -0800782 */
Charles Chan59cc16d2017-02-02 16:20:42 -0800783 public void createGroupsFromVlanConfig() {
Charles Chan7ffd81f2017-02-08 15:52:08 -0800784 srManager.getVlanPortMap(deviceId).asMap().forEach((vlanId, ports) -> {
Charles Chan59cc16d2017-02-02 16:20:42 -0800785 createBcastGroupFromVlan(vlanId, ports);
Pier Ventre10bd8d12016-11-26 21:05:22 -0800786 });
Saurav Das1a129a02016-11-18 15:21:57 -0800787 }
Charles Chan9f676b62015-10-29 14:58:10 -0700788
Saurav Das1a129a02016-11-18 15:21:57 -0800789 /**
Charles Chan59cc16d2017-02-02 16:20:42 -0800790 * Creates a single broadcast group from a given vlan id and list of ports.
Saurav Das1a129a02016-11-18 15:21:57 -0800791 *
Charles Chan59cc16d2017-02-02 16:20:42 -0800792 * @param vlanId vlan id
Saurav Das1a129a02016-11-18 15:21:57 -0800793 * @param ports list of ports in the subnet
794 */
Charles Chan7ffd81f2017-02-08 15:52:08 -0800795 public void createBcastGroupFromVlan(VlanId vlanId, Collection<PortNumber> ports) {
Charles Chan59cc16d2017-02-02 16:20:42 -0800796 VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
Charles Chan9f676b62015-10-29 14:58:10 -0700797
Charles Chan59cc16d2017-02-02 16:20:42 -0800798 if (vlanNextObjStore.containsKey(key)) {
Saurav Das1a129a02016-11-18 15:21:57 -0800799 log.debug("Broadcast group for device {} and subnet {} exists",
Charles Chan59cc16d2017-02-02 16:20:42 -0800800 deviceId, vlanId);
Saurav Das1a129a02016-11-18 15:21:57 -0800801 return;
802 }
Charles Chan188ebf52015-12-23 00:15:11 -0800803
Saurav Das1a129a02016-11-18 15:21:57 -0800804 TrafficSelector metadata =
Charles Chan59cc16d2017-02-02 16:20:42 -0800805 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
Charles Chanc42e84e2015-10-20 16:24:19 -0700806
Saurav Das1a129a02016-11-18 15:21:57 -0800807 int nextId = flowObjectiveService.allocateNextId();
Charles Chanc42e84e2015-10-20 16:24:19 -0700808
Saurav Das1a129a02016-11-18 15:21:57 -0800809 NextObjective.Builder nextObjBuilder = DefaultNextObjective
810 .builder().withId(nextId)
811 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
812 .withMeta(metadata);
Charles Chanc42e84e2015-10-20 16:24:19 -0700813
Saurav Das1a129a02016-11-18 15:21:57 -0800814 ports.forEach(port -> {
815 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Charles Chan7ffd81f2017-02-08 15:52:08 -0800816 if (toPopVlan(port, vlanId)) {
817 tBuilder.popVlan();
818 }
Saurav Das1a129a02016-11-18 15:21:57 -0800819 tBuilder.setOutput(port);
820 nextObjBuilder.addTreatment(tBuilder.build());
Charles Chanc42e84e2015-10-20 16:24:19 -0700821 });
Saurav Das1a129a02016-11-18 15:21:57 -0800822
Saurav Das961beb22017-03-29 19:09:17 -0700823 ObjectiveContext context = new DefaultObjectiveContext(
824 (objective) ->
825 log.debug("createBroadcastGroupFromVlan installed "
826 + "NextObj {} on {}", nextId, deviceId),
827 (objective, error) ->
828 log.warn("createBroadcastGroupFromVlan failed to install"
829 + " NextObj {} on {}: {}", nextId, deviceId, error)
830 );
831 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Das1a129a02016-11-18 15:21:57 -0800832 flowObjectiveService.next(deviceId, nextObj);
Charles Chan59cc16d2017-02-02 16:20:42 -0800833 log.debug("createBcastGroupFromVlan: Submited next objective {} in device {}",
Saurav Das1a129a02016-11-18 15:21:57 -0800834 nextId, deviceId);
835
Charles Chan59cc16d2017-02-02 16:20:42 -0800836 vlanNextObjStore.put(key, nextId);
Charles Chanc42e84e2015-10-20 16:24:19 -0700837 }
838
Charles Chane849c192016-01-11 18:28:54 -0800839 /**
Charles Chan7ffd81f2017-02-08 15:52:08 -0800840 * Determine if we should pop given vlan before sending packets to the given port.
841 *
842 * @param portNumber port number
843 * @param vlanId vlan id
844 * @return true if the vlan id is not contained in any vlanTagged config
845 */
846 private boolean toPopVlan(PortNumber portNumber, VlanId vlanId) {
847 return srManager.interfaceService.getInterfacesByPort(new ConnectPoint(deviceId, portNumber))
848 .stream().noneMatch(intf -> intf.vlanTagged().contains(vlanId));
849 }
850
851 /**
Saurav Das4ce45962015-11-24 23:21:05 -0800852 * Create simple next objective for a single port. The treatments can include
853 * all outgoing actions that need to happen on the packet.
854 *
855 * @param portNum the outgoing port on the device
856 * @param treatment the actions to apply on the packets (should include outport)
857 * @param meta optional data to pass to the driver
858 */
859 public void createGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
860 TrafficSelector meta) {
861 int nextId = flowObjectiveService.allocateNextId();
862 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
Saurav Das76ae6812017-03-15 15:15:14 -0700863 deviceId, portNum, treatment, meta);
Saurav Das4ce45962015-11-24 23:21:05 -0800864
865 NextObjective.Builder nextObjBuilder = DefaultNextObjective
866 .builder().withId(nextId)
867 .withType(NextObjective.Type.SIMPLE)
868 .addTreatment(treatment)
869 .fromApp(appId)
870 .withMeta(meta);
871
Saurav Das961beb22017-03-29 19:09:17 -0700872 ObjectiveContext context = new DefaultObjectiveContext(
873 (objective) ->
874 log.debug("createGroupFromPort installed "
875 + "NextObj {} on {}", nextId, deviceId),
876 (objective, error) ->
877 log.warn("createGroupFromPort failed to install"
878 + " NextObj {} on {}: {}", nextId, deviceId, error)
879 );
880 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Das4ce45962015-11-24 23:21:05 -0800881 flowObjectiveService.next(deviceId, nextObj);
882 log.debug("createGroupFromPort: Submited next objective {} in device {} "
883 + "for port {}", nextId, deviceId, portNum);
884
885 portNextObjStore.put(key, nextId);
886 }
887
sangho1e575652015-05-14 00:39:53 -0700888 /**
889 * Removes groups for the next objective ID given.
890 *
891 * @param objectiveId next objective ID to remove
892 * @return true if succeeds, false otherwise
893 */
894 public boolean removeGroup(int objectiveId) {
895
896 if (nsNextObjStore.containsValue(objectiveId)) {
897 NextObjective.Builder nextObjBuilder = DefaultNextObjective
898 .builder().withId(objectiveId)
899 .withType(NextObjective.Type.HASHED).fromApp(appId);
Charles Chan216e3c82016-04-23 14:48:16 -0700900 ObjectiveContext context = new DefaultObjectiveContext(
901 (objective) -> log.debug("RemoveGroup removes NextObj {} on {}",
902 objectiveId, deviceId),
903 (objective, error) ->
904 log.warn("RemoveGroup failed to remove NextObj {} on {}: {}",
905 objectiveId, deviceId, error));
906 NextObjective nextObjective = nextObjBuilder.remove(context);
Saurav Das8a0732e2015-11-20 15:27:53 -0800907 log.info("**removeGroup: Submited "
908 + "next objective {} in device {}",
909 objectiveId, deviceId);
sangho1e575652015-05-14 00:39:53 -0700910 flowObjectiveService.next(deviceId, nextObjective);
911
912 for (Map.Entry<NeighborSetNextObjectiveStoreKey, Integer> entry: nsNextObjStore.entrySet()) {
913 if (entry.getValue().equals(objectiveId)) {
914 nsNextObjStore.remove(entry.getKey());
915 break;
916 }
917 }
sangho0b2b6d12015-05-20 22:16:38 -0700918 return true;
sangho1e575652015-05-14 00:39:53 -0700919 }
920
921 return false;
922 }
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -0700923
Charles Chane849c192016-01-11 18:28:54 -0800924 /**
925 * Removes all groups from all next objective stores.
926 */
Saurav Das423fe2b2015-12-04 10:52:59 -0800927 public void removeAllGroups() {
928 for (Map.Entry<NeighborSetNextObjectiveStoreKey, Integer> entry:
929 nsNextObjStore.entrySet()) {
930 removeGroup(entry.getValue());
931 }
932 for (Map.Entry<PortNextObjectiveStoreKey, Integer> entry:
933 portNextObjStore.entrySet()) {
934 removeGroup(entry.getValue());
935 }
Charles Chan59cc16d2017-02-02 16:20:42 -0800936 for (Map.Entry<VlanNextObjectiveStoreKey, Integer> entry:
937 vlanNextObjStore.entrySet()) {
Saurav Das423fe2b2015-12-04 10:52:59 -0800938 removeGroup(entry.getValue());
939 }
940 // should probably clean local stores port-neighbor
941 }
Saurav Das1547b3f2017-05-05 17:01:08 -0700942
943 /**
944 * RetryHashBkts is a one-time retry at populating all the buckets of a
945 * hash group based on the given link. Should only be called by the
946 * master instance of the src-device of the link.
947 */
948 protected final class RetryHashBkts implements Runnable {
949 Link link;
950 MacAddress dstMac;
951
952 private RetryHashBkts(Link link, MacAddress dstMac) {
953 this.link = link;
954 this.dstMac = dstMac;
955 }
956
957 @Override
958 public void run() {
959 log.info("RETRY Hash buckets for linkup: {}", link);
960 Set<NeighborSet> nsSet = nsNextObjStore.keySet()
961 .stream()
962 .filter(nsStoreEntry -> nsStoreEntry.deviceId().equals(deviceId))
963 .map(nsStoreEntry -> nsStoreEntry.neighborSet())
964 .filter(ns -> ns.getDeviceIds()
965 .contains(link.dst().deviceId()))
966 .collect(Collectors.toSet());
967 log.debug("retry-link: nsNextObjStore contents for device {}: {}",
968 deviceId, nsSet);
969 for (NeighborSet ns : nsSet) {
970 Integer nextId = nsNextObjStore.
971 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
972 if (nextId != null) {
973 addToHashedNextObjective(link.src().port(), dstMac, ns,
974 nextId, true);
975 }
976 }
977 }
978 }
979
980
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800981}