blob: cd20ecbe11407edd146fe3675d7fadba840b174a [file] [log] [blame]
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Srikanth Vavilapalli4db76e32015-04-07 15:12:32 -070016package org.onosproject.segmentrouting.grouphandler;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080017
Saurav Das041bb782017-08-14 16:44:43 -070018import com.google.common.collect.ImmutableMap;
Pier Ventre917127a2016-10-31 16:49:19 -070019import com.google.common.collect.Iterables;
Saurav Das041bb782017-08-14 16:44:43 -070020import com.google.common.collect.Lists;
Saurav Dasc88d4662017-05-15 15:34:25 -070021import com.google.common.collect.Sets;
22
Pier Ventre917127a2016-10-31 16:49:19 -070023import org.apache.commons.lang3.RandomUtils;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080024import org.onlab.packet.MacAddress;
sangho32a59322015-02-17 12:07:41 -080025import org.onlab.packet.MplsLabel;
Saurav Das423fe2b2015-12-04 10:52:59 -080026import org.onlab.packet.VlanId;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070027import org.onlab.util.KryoNamespace;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080028import org.onosproject.core.ApplicationId;
Charles Chan59cc16d2017-02-02 16:20:42 -080029import org.onosproject.net.ConnectPoint;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080030import org.onosproject.net.DeviceId;
31import org.onosproject.net.Link;
32import org.onosproject.net.PortNumber;
Saurav Das423fe2b2015-12-04 10:52:59 -080033import org.onosproject.net.flow.DefaultTrafficSelector;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080034import org.onosproject.net.flow.DefaultTrafficTreatment;
Saurav Das8a0732e2015-11-20 15:27:53 -080035import org.onosproject.net.flow.TrafficSelector;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080036import org.onosproject.net.flow.TrafficTreatment;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070037import org.onosproject.net.flowobjective.DefaultNextObjective;
Charles Chan216e3c82016-04-23 14:48:16 -070038import org.onosproject.net.flowobjective.DefaultObjectiveContext;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070039import org.onosproject.net.flowobjective.FlowObjectiveService;
40import org.onosproject.net.flowobjective.NextObjective;
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -070041import org.onosproject.net.flowobjective.ObjectiveContext;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080042import org.onosproject.net.link.LinkService;
Saurav Dasceccf242017-08-03 18:30:35 -070043import org.onosproject.segmentrouting.DefaultRoutingHandler;
Saurav Das423fe2b2015-12-04 10:52:59 -080044import org.onosproject.segmentrouting.SegmentRoutingManager;
Charles Chan0b4e6182015-11-03 10:42:14 -080045import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
46import org.onosproject.segmentrouting.config.DeviceProperties;
Saurav Das7bcbe702017-06-13 15:35:54 -070047import org.onosproject.segmentrouting.storekey.DestinationSetNextObjectiveStoreKey;
Charles Chand2990362016-04-18 13:44:03 -070048import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
Charles Chan59cc16d2017-02-02 16:20:42 -080049import org.onosproject.segmentrouting.storekey.VlanNextObjectiveStoreKey;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070050import org.onosproject.store.service.EventuallyConsistentMap;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080051import org.slf4j.Logger;
52
Pier Ventre917127a2016-10-31 16:49:19 -070053import java.net.URI;
Charles Chan7ffd81f2017-02-08 15:52:08 -080054import java.util.Collection;
Pier Ventre917127a2016-10-31 16:49:19 -070055import java.util.Collections;
Saurav Dasc88d4662017-05-15 15:34:25 -070056import java.util.HashMap;
Pier Ventre917127a2016-10-31 16:49:19 -070057import java.util.HashSet;
58import java.util.List;
59import java.util.Map;
60import java.util.Set;
61import java.util.concurrent.ConcurrentHashMap;
Saurav Das1547b3f2017-05-05 17:01:08 -070062import java.util.concurrent.ScheduledExecutorService;
63import java.util.concurrent.TimeUnit;
Pier Ventre917127a2016-10-31 16:49:19 -070064import java.util.stream.Collectors;
65
66import static com.google.common.base.Preconditions.checkNotNull;
Saurav Das1547b3f2017-05-05 17:01:08 -070067import static java.util.concurrent.Executors.newScheduledThreadPool;
68import static org.onlab.util.Tools.groupedThreads;
Charles Chan59cc16d2017-02-02 16:20:42 -080069import static org.onosproject.segmentrouting.SegmentRoutingManager.INTERNAL_VLAN;
Pier Ventre917127a2016-10-31 16:49:19 -070070import static org.slf4j.LoggerFactory.getLogger;
71
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080072/**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070073 * Default ECMP group handler creation module. This component creates a set of
74 * ECMP groups for every neighbor that this device is connected to based on
75 * whether the current device is an edge device or a transit device.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080076 */
77public class DefaultGroupHandler {
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -070078 protected static final Logger log = getLogger(DefaultGroupHandler.class);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080079
Saurav Dasceccf242017-08-03 18:30:35 -070080 private static final long VERIFY_INTERVAL = 30; // secs
81
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080082 protected final DeviceId deviceId;
83 protected final ApplicationId appId;
84 protected final DeviceProperties deviceConfig;
85 protected final List<Integer> allSegmentIds;
Pier Ventree0ae7a32016-11-23 09:57:42 -080086 protected int ipv4NodeSegmentId = -1;
87 protected int ipv6NodeSegmentId = -1;
Charles Chan0b4e6182015-11-03 10:42:14 -080088 protected boolean isEdgeRouter = false;
89 protected MacAddress nodeMacAddr = null;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080090 protected LinkService linkService;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070091 protected FlowObjectiveService flowObjectiveService;
Saurav Dasc88d4662017-05-15 15:34:25 -070092 /**
93 * local store for neighbor-device-ids and the set of ports on this device
94 * that connect to the same neighbor.
95 */
Saurav Das8a0732e2015-11-20 15:27:53 -080096 protected ConcurrentHashMap<DeviceId, Set<PortNumber>> devicePortMap =
97 new ConcurrentHashMap<>();
Saurav Dasc88d4662017-05-15 15:34:25 -070098 /**
99 * local store for ports on this device connected to neighbor-device-id.
100 */
Saurav Das8a0732e2015-11-20 15:27:53 -0800101 protected ConcurrentHashMap<PortNumber, DeviceId> portDeviceMap =
102 new ConcurrentHashMap<>();
Saurav Dasc88d4662017-05-15 15:34:25 -0700103
Saurav Das7bcbe702017-06-13 15:35:54 -0700104 // distributed store for (device+destination-set) mapped to next-id and neighbors
105 protected EventuallyConsistentMap<DestinationSetNextObjectiveStoreKey, NextNeighbors>
106 dsNextObjStore = null;
Saurav Das1a129a02016-11-18 15:21:57 -0800107 // distributed store for (device+subnet-ip-prefix) mapped to next-id
Charles Chan59cc16d2017-02-02 16:20:42 -0800108 protected EventuallyConsistentMap<VlanNextObjectiveStoreKey, Integer>
109 vlanNextObjStore = null;
Saurav Das1a129a02016-11-18 15:21:57 -0800110 // distributed store for (device+port+treatment) mapped to next-id
Charles Chane849c192016-01-11 18:28:54 -0800111 protected EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
112 portNextObjStore = null;
Charles Chan188ebf52015-12-23 00:15:11 -0800113 private SegmentRoutingManager srManager;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800114
Saurav Das1547b3f2017-05-05 17:01:08 -0700115 private ScheduledExecutorService executorService
Saurav Dasceccf242017-08-03 18:30:35 -0700116 = newScheduledThreadPool(1, groupedThreads("bktCorrector", "bktC-%d", log));
Saurav Das1547b3f2017-05-05 17:01:08 -0700117
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700118 protected KryoNamespace.Builder kryo = new KryoNamespace.Builder()
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700119 .register(URI.class).register(HashSet.class)
Saurav Das7bcbe702017-06-13 15:35:54 -0700120 .register(DeviceId.class).register(PortNumber.class)
121 .register(DestinationSet.class).register(PolicyGroupIdentifier.class)
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700122 .register(PolicyGroupParams.class)
123 .register(GroupBucketIdentifier.class)
124 .register(GroupBucketIdentifier.BucketOutputType.class);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800125
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700126 protected DefaultGroupHandler(DeviceId deviceId, ApplicationId appId,
127 DeviceProperties config,
128 LinkService linkService,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700129 FlowObjectiveService flowObjService,
Charles Chan188ebf52015-12-23 00:15:11 -0800130 SegmentRoutingManager srManager) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800131 this.deviceId = checkNotNull(deviceId);
132 this.appId = checkNotNull(appId);
133 this.deviceConfig = checkNotNull(config);
134 this.linkService = checkNotNull(linkService);
Charles Chan0b4e6182015-11-03 10:42:14 -0800135 this.allSegmentIds = checkNotNull(config.getAllDeviceSegmentIds());
136 try {
Pier Ventree0ae7a32016-11-23 09:57:42 -0800137 this.ipv4NodeSegmentId = config.getIPv4SegmentId(deviceId);
138 this.ipv6NodeSegmentId = config.getIPv6SegmentId(deviceId);
Charles Chan0b4e6182015-11-03 10:42:14 -0800139 this.isEdgeRouter = config.isEdgeDevice(deviceId);
140 this.nodeMacAddr = checkNotNull(config.getDeviceMac(deviceId));
141 } catch (DeviceConfigNotFoundException e) {
142 log.warn(e.getMessage()
143 + " Skipping value assignment in DefaultGroupHandler");
144 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700145 this.flowObjectiveService = flowObjService;
Saurav Das7bcbe702017-06-13 15:35:54 -0700146 this.dsNextObjStore = srManager.dsNextObjStore();
Ray Milkeye4afdb52017-04-05 09:42:04 -0700147 this.vlanNextObjStore = srManager.vlanNextObjStore();
148 this.portNextObjStore = srManager.portNextObjStore();
Charles Chan188ebf52015-12-23 00:15:11 -0800149 this.srManager = srManager;
Saurav Dasceccf242017-08-03 18:30:35 -0700150 executorService.scheduleWithFixedDelay(new BucketCorrector(), 10,
151 VERIFY_INTERVAL,
152 TimeUnit.SECONDS);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800153 populateNeighborMaps();
154 }
155
156 /**
Saurav Dasceccf242017-08-03 18:30:35 -0700157 * Gracefully shuts down a groupHandler. Typically called when the handler is
158 * no longer needed.
159 */
160 public void shutdown() {
161 executorService.shutdown();
162 }
163
164 /**
Saurav Dasc88d4662017-05-15 15:34:25 -0700165 * Creates a group handler object.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800166 *
167 * @param deviceId device identifier
168 * @param appId application identifier
169 * @param config interface to retrieve the device properties
170 * @param linkService link service object
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700171 * @param flowObjService flow objective service object
Charles Chane849c192016-01-11 18:28:54 -0800172 * @param srManager segment routing manager
Charles Chan0b4e6182015-11-03 10:42:14 -0800173 * @throws DeviceConfigNotFoundException if the device configuration is not found
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800174 * @return default group handler type
175 */
Saurav Das4ce45962015-11-24 23:21:05 -0800176 public static DefaultGroupHandler createGroupHandler(
Saurav Dasceccf242017-08-03 18:30:35 -0700177 DeviceId deviceId,
178 ApplicationId appId,
179 DeviceProperties config,
180 LinkService linkService,
181 FlowObjectiveService flowObjService,
182 SegmentRoutingManager srManager)
183 throws DeviceConfigNotFoundException {
Saurav Dasc88d4662017-05-15 15:34:25 -0700184 return new DefaultGroupHandler(deviceId, appId, config,
185 linkService,
186 flowObjService,
187 srManager);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800188 }
189
190 /**
Saurav Dasc88d4662017-05-15 15:34:25 -0700191 * Updates local stores for link-src device/port to neighbor (link-dst).
192 *
193 * @param link the infrastructure link
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800194 */
Saurav Dasc88d4662017-05-15 15:34:25 -0700195 public void portUpForLink(Link link) {
Saurav Dasceccf242017-08-03 18:30:35 -0700196 if (!link.src().deviceId().equals(deviceId)) {
197 log.warn("linkUp: deviceId{} doesn't match with link src {}",
198 deviceId, link.src().deviceId());
199 return;
200 }
Saurav Dasc88d4662017-05-15 15:34:25 -0700201
Saurav Dasceccf242017-08-03 18:30:35 -0700202 log.info("* portUpForLink: Device {} linkUp at local port {} to "
203 + "neighbor {}", deviceId, link.src().port(), link.dst().deviceId());
204 // ensure local state is updated even if linkup is aborted later on
205 addNeighborAtPort(link.dst().deviceId(),
206 link.src().port());
207 }
Saurav Dasc88d4662017-05-15 15:34:25 -0700208
Saurav Dasceccf242017-08-03 18:30:35 -0700209 /**
210 * Updates local stores for port that has gone down.
211 *
212 * @param port port number that has gone down
213 */
214 public void portDown(PortNumber port) {
215 if (portDeviceMap.get(port) == null) {
216 log.warn("portDown: unknown port");
217 return;
218 }
Saurav Dasc88d4662017-05-15 15:34:25 -0700219
Saurav Dasceccf242017-08-03 18:30:35 -0700220 log.debug("Device {} portDown {} to neighbor {}", deviceId, port,
221 portDeviceMap.get(port));
222 devicePortMap.get(portDeviceMap.get(port)).remove(port);
223 portDeviceMap.remove(port);
224 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800225
226 /**
Saurav Dasc88d4662017-05-15 15:34:25 -0700227 * Checks all groups in the src-device of link for neighbor sets that include
228 * the dst-device of link, and edits the hash groups according to link up
229 * or down. Should only be called by the master instance of the src-switch
230 * of link. Typically used when there are no route-path changes due to the
231 * link up or down, as the ECMPspg does not change.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800232 *
Saurav Dasc88d4662017-05-15 15:34:25 -0700233 * @param link the infrastructure link that has gone down or come up
234 * @param linkDown true if link has gone down
235 * @param firstTime true if link has come up for the first time i.e a link
236 * not seen-before
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800237 */
Saurav Dasc88d4662017-05-15 15:34:25 -0700238 public void retryHash(Link link, boolean linkDown, boolean firstTime) {
Saurav Das041bb782017-08-14 16:44:43 -0700239 MacAddress neighborMac;
Charles Chan0b4e6182015-11-03 10:42:14 -0800240 try {
Saurav Das041bb782017-08-14 16:44:43 -0700241 neighborMac = deviceConfig.getDeviceMac(link.dst().deviceId());
Charles Chan0b4e6182015-11-03 10:42:14 -0800242 } catch (DeviceConfigNotFoundException e) {
Saurav Dasc88d4662017-05-15 15:34:25 -0700243 log.warn(e.getMessage() + " Aborting retryHash.");
Charles Chan0b4e6182015-11-03 10:42:14 -0800244 return;
245 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700246 // find all the destinationSets related to link
247 Set<DestinationSetNextObjectiveStoreKey> dsKeySet = dsNextObjStore.entrySet()
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700248 .stream()
Saurav Das7bcbe702017-06-13 15:35:54 -0700249 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
Pier Luigi8b4d20d2018-01-14 21:56:11 +0100250 // Filter out PW transit groups or include them if MPLS ECMP is supported
251 .filter(entry -> !entry.getKey().destinationSet().mplsSet() ||
252 (entry.getKey().destinationSet().mplsSet() && srManager.getMplsEcmp()))
Saurav Das7bcbe702017-06-13 15:35:54 -0700253 .filter(entry -> entry.getValue().containsNextHop(link.dst().deviceId()))
254 .map(entry -> entry.getKey())
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700255 .collect(Collectors.toSet());
Saurav Dasc88d4662017-05-15 15:34:25 -0700256
Saurav Das7bcbe702017-06-13 15:35:54 -0700257 log.debug("retryHash: dsNextObjStore contents for linkSrc {} -> linkDst {}: {}",
258 deviceId, link.dst().deviceId(), dsKeySet);
259
260 for (DestinationSetNextObjectiveStoreKey dsKey : dsKeySet) {
261 NextNeighbors nextHops = dsNextObjStore.get(dsKey);
262 if (nextHops == null) {
Saurav Dasc88d4662017-05-15 15:34:25 -0700263 log.warn("retryHash in device {}, but global store has no record "
Saurav Das7bcbe702017-06-13 15:35:54 -0700264 + "for dsKey:{}", deviceId, dsKey);
Saurav Dasc88d4662017-05-15 15:34:25 -0700265 continue;
266 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700267 int nextId = nextHops.nextId();
268 Set<DeviceId> dstSet = nextHops.getDstForNextHop(link.dst().deviceId());
Saurav Dasc88d4662017-05-15 15:34:25 -0700269 if (!linkDown) {
Saurav Das041bb782017-08-14 16:44:43 -0700270 List<PortLabel> pl = Lists.newArrayList();
Saurav Dasc88d4662017-05-15 15:34:25 -0700271 if (firstTime) {
272 // some links may have come up before the next-objective was created
273 // we take this opportunity to ensure other ports to same next-hop-dst
274 // are part of the hash group (see CORD-1180). Duplicate additions
275 // to the same hash group are avoided by the driver.
276 for (PortNumber p : devicePortMap.get(link.dst().deviceId())) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700277 dstSet.forEach(dst -> {
278 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
Saurav Das041bb782017-08-14 16:44:43 -0700279 pl.add(new PortLabel(p, edgeLabel));
Saurav Das7bcbe702017-06-13 15:35:54 -0700280 });
Saurav Dasc88d4662017-05-15 15:34:25 -0700281 }
Saurav Das041bb782017-08-14 16:44:43 -0700282 addToHashedNextObjective(pl, neighborMac, nextId);
283 } else {
284 // handle only the port that came up
285 dstSet.forEach(dst -> {
286 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
287 pl.add(new PortLabel(link.src().port(), edgeLabel));
288 });
289 addToHashedNextObjective(pl, neighborMac, nextId);
Saurav Dasc88d4662017-05-15 15:34:25 -0700290 }
291 } else {
Saurav Das041bb782017-08-14 16:44:43 -0700292 // linkdown
293 List<PortLabel> pl = Lists.newArrayList();
Saurav Das7bcbe702017-06-13 15:35:54 -0700294 dstSet.forEach(dst -> {
295 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
Saurav Das041bb782017-08-14 16:44:43 -0700296 pl.add(new PortLabel(link.src().port(), edgeLabel));
Saurav Das7bcbe702017-06-13 15:35:54 -0700297 });
Saurav Das041bb782017-08-14 16:44:43 -0700298 removeFromHashedNextObjective(pl, neighborMac, nextId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700299 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800300 }
Saurav Das1547b3f2017-05-05 17:01:08 -0700301 }
302
Saurav Dasc88d4662017-05-15 15:34:25 -0700303 /**
Saurav Das041bb782017-08-14 16:44:43 -0700304 * Utility class for associating output ports and the corresponding MPLS
305 * labels to push. In dual-homing, there are different labels to push
306 * corresponding to the destination switches in an edge-pair. If both
307 * destinations are reachable via the same spine, then the output-port to
308 * the spine will be associated with two labels i.e. there will be two
309 * PortLabel objects for the same port but with different labels.
310 */
311 private class PortLabel {
312 PortNumber port;
313 int edgeLabel;
314
315 PortLabel(PortNumber port, int edgeLabel) {
316 this.port = port;
317 this.edgeLabel = edgeLabel;
318 }
319
320 @Override
321 public String toString() {
322 return port.toString() + "/" + String.valueOf(edgeLabel);
323 }
324 }
325
326 /**
327 * Makes a call to the FlowObjective service to add buckets to
328 * a hashed group. User must ensure that all the ports & labels are meant
329 * same neighbor (ie. dstMac).
Saurav Dasc88d4662017-05-15 15:34:25 -0700330 *
Pier Luigi8b4d20d2018-01-14 21:56:11 +0100331 * @param portLabels a collection of port & label combinations to add
Saurav Das041bb782017-08-14 16:44:43 -0700332 * to the hash group identified by the nextId
Saurav Dasc88d4662017-05-15 15:34:25 -0700333 * @param dstMac destination mac address of next-hop
Saurav Das041bb782017-08-14 16:44:43 -0700334 * @param nextId id for next-objective to which buckets will be added
Saurav Dasceccf242017-08-03 18:30:35 -0700335 *
Saurav Dasc88d4662017-05-15 15:34:25 -0700336 */
Saurav Das041bb782017-08-14 16:44:43 -0700337 private void addToHashedNextObjective(Collection<PortLabel> portLabels,
338 MacAddress dstMac, Integer nextId) {
Saurav Das1547b3f2017-05-05 17:01:08 -0700339 // setup metadata to pass to nextObjective - indicate the vlan on egress
340 // if needed by the switch pipeline. Since hashed next-hops are always to
341 // other neighboring routers, there is no subnet assigned on those ports.
342 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
343 metabuilder.matchVlanId(INTERNAL_VLAN);
Saurav Das1547b3f2017-05-05 17:01:08 -0700344 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
345 .withId(nextId)
346 .withType(NextObjective.Type.HASHED)
Saurav Das1547b3f2017-05-05 17:01:08 -0700347 .withMeta(metabuilder.build())
348 .fromApp(appId);
Saurav Das041bb782017-08-14 16:44:43 -0700349 // Create the new buckets to be updated
350 portLabels.forEach(pl -> {
351 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
352 tBuilder.setOutput(pl.port)
353 .setEthDst(dstMac)
354 .setEthSrc(nodeMacAddr);
355 if (pl.edgeLabel != DestinationSet.NO_EDGE_LABEL) {
356 tBuilder.pushMpls()
357 .copyTtlOut()
358 .setMpls(MplsLabel.mplsLabel(pl.edgeLabel));
359 }
360 nextObjBuilder.addTreatment(tBuilder.build());
361 });
362
363 log.debug("addToHash in device {}: Adding Bucket with port/label {} "
364 + "to nextId {}", deviceId, portLabels, nextId);
Saurav Das1547b3f2017-05-05 17:01:08 -0700365
366 ObjectiveContext context = new DefaultObjectiveContext(
Saurav Das041bb782017-08-14 16:44:43 -0700367 (objective) -> log.debug("addToHash port/label {} addedTo "
368 + "NextObj {} on {}", portLabels, nextId, deviceId),
Saurav Das1547b3f2017-05-05 17:01:08 -0700369 (objective, error) ->
Saurav Das041bb782017-08-14 16:44:43 -0700370 log.warn("addToHash failed to add port/label {} to"
371 + " NextObj {} on {}: {}", portLabels,
Saurav Dasc88d4662017-05-15 15:34:25 -0700372 nextId, deviceId, error));
Saurav Das1547b3f2017-05-05 17:01:08 -0700373 NextObjective nextObjective = nextObjBuilder.addToExisting(context);
374 flowObjectiveService.next(deviceId, nextObjective);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800375 }
376
377 /**
Saurav Das041bb782017-08-14 16:44:43 -0700378 * Makes a call to the FlowObjective service to remove buckets from
379 * a hash group. User must ensure that all the ports & labels are meant
380 * same neighbor (ie. dstMac).
Saurav Dasceccf242017-08-03 18:30:35 -0700381 *
Pier Luigi8b4d20d2018-01-14 21:56:11 +0100382 * @param portLabels a collection of port & label combinations to remove
Saurav Das041bb782017-08-14 16:44:43 -0700383 * from the hash group identified by the nextId
Saurav Dasceccf242017-08-03 18:30:35 -0700384 * @param dstMac destination mac address of next-hop
Saurav Das041bb782017-08-14 16:44:43 -0700385 * @param nextId id for next-objective from which buckets will be removed
Saurav Dasceccf242017-08-03 18:30:35 -0700386 */
Saurav Das041bb782017-08-14 16:44:43 -0700387 private void removeFromHashedNextObjective(Collection<PortLabel> portLabels,
388 MacAddress dstMac, Integer nextId) {
Saurav Dasceccf242017-08-03 18:30:35 -0700389 NextObjective.Builder nextObjBuilder = DefaultNextObjective
390 .builder()
391 .withType(NextObjective.Type.HASHED) //same as original
392 .withId(nextId)
Saurav Das041bb782017-08-14 16:44:43 -0700393 .fromApp(appId);
394 // Create the buckets to be removed
395 portLabels.forEach(pl -> {
396 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
397 tBuilder.setOutput(pl.port)
398 .setEthDst(dstMac)
399 .setEthSrc(nodeMacAddr);
400 if (pl.edgeLabel != DestinationSet.NO_EDGE_LABEL) {
401 tBuilder.pushMpls()
402 .copyTtlOut()
403 .setMpls(MplsLabel.mplsLabel(pl.edgeLabel));
404 }
405 nextObjBuilder.addTreatment(tBuilder.build());
406 });
407 log.debug("removeFromHash in device {}: Removing Bucket with port/label"
408 + " {} from nextId {}", deviceId, portLabels, nextId);
Saurav Dasc88d4662017-05-15 15:34:25 -0700409
Saurav Das041bb782017-08-14 16:44:43 -0700410 ObjectiveContext context = new DefaultObjectiveContext(
411 (objective) -> log.debug("port/label {} removedFrom NextObj"
412 + " {} on {}", portLabels, nextId, deviceId),
413 (objective, error) ->
414 log.warn("port/label {} failed to removeFrom NextObj {} on "
415 + "{}: {}", portLabels, nextId, deviceId, error));
416 NextObjective nextObjective = nextObjBuilder.removeFromExisting(context);
Saurav Dasceccf242017-08-03 18:30:35 -0700417 flowObjectiveService.next(deviceId, nextObjective);
418 }
Saurav Dasc88d4662017-05-15 15:34:25 -0700419
420 /**
421 * Checks all the hash-groups in the target-switch meant for the destination
422 * switch, and either adds or removes buckets to make the neighbor-set
423 * match the given next-hops. Typically called by the master instance of the
424 * destination switch, which may be different from the master instance of the
425 * target switch where hash-group changes are made.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800426 *
Saurav Dasc88d4662017-05-15 15:34:25 -0700427 * @param targetSw the switch in which the hash groups will be edited
428 * @param nextHops the current next hops for the target switch to reach
429 * the dest sw
430 * @param destSw the destination switch
431 * @param revoke true if hash groups need to remove buckets from the
432 * the groups to match the current next hops
433 * @return true if calls are made to edit buckets, or if no edits are required
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800434 */
Saurav Dasc88d4662017-05-15 15:34:25 -0700435 public boolean fixHashGroups(DeviceId targetSw, Set<DeviceId> nextHops,
436 DeviceId destSw, boolean revoke) {
437 // temporary storage of keys to be updated
Saurav Das7bcbe702017-06-13 15:35:54 -0700438 Map<DestinationSetNextObjectiveStoreKey, Set<DeviceId>> tempStore =
Saurav Dasc88d4662017-05-15 15:34:25 -0700439 new HashMap<>();
Saurav Das041bb782017-08-14 16:44:43 -0700440 boolean foundNextObjective = false, success = true;
Charles Chan0b4e6182015-11-03 10:42:14 -0800441
Saurav Das7bcbe702017-06-13 15:35:54 -0700442 // retrieve hash-groups meant for destSw, which have destinationSets
Saurav Dasc88d4662017-05-15 15:34:25 -0700443 // with different neighbors than the given next-hops
Saurav Das7bcbe702017-06-13 15:35:54 -0700444 for (DestinationSetNextObjectiveStoreKey dskey : dsNextObjStore.keySet()) {
445 if (!dskey.deviceId().equals(targetSw) ||
446 !dskey.destinationSet().getDestinationSwitches().contains(destSw)) {
Saurav Dasc88d4662017-05-15 15:34:25 -0700447 continue;
448 }
449 foundNextObjective = true;
Saurav Das7bcbe702017-06-13 15:35:54 -0700450 NextNeighbors nhops = dsNextObjStore.get(dskey);
451 Set<DeviceId> currNeighbors = nhops.nextHops(destSw);
452 int edgeLabel = dskey.destinationSet().getEdgeLabel(destSw);
453 Integer nextId = nhops.nextId();
Charles Chan0b4e6182015-11-03 10:42:14 -0800454
Saurav Dasba7eb1a2017-12-13 16:19:35 -0800455 if (currNeighbors == null || nextHops == null) {
456 log.warn("fixing hash groups but found currNeighbors:{} or nextHops:{}"
457 + " in targetSw:{} for dstSw:{}", currNeighbors, nextHops,
458 targetSw, destSw);
459 success &= false;
460 continue;
461 }
462
Saurav Dasc88d4662017-05-15 15:34:25 -0700463 Set<DeviceId> diff;
464 if (revoke) {
465 diff = Sets.difference(currNeighbors, nextHops);
466 log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next "
467 + "hops:{} ..removing {}", targetSw, destSw, nextId,
468 currNeighbors, diff);
469 } else {
470 diff = Sets.difference(nextHops, currNeighbors);
471 log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next "
472 + "hops:{} ..adding {}", targetSw, destSw, nextId,
473 currNeighbors, diff);
474 }
Saurav Das041bb782017-08-14 16:44:43 -0700475 boolean suc = updateAllPortsToNextHop(diff, edgeLabel, nextId,
476 revoke);
477 if (suc) {
478 // to update neighbor set with changes made
Saurav Dasc88d4662017-05-15 15:34:25 -0700479 if (revoke) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700480 tempStore.put(dskey, Sets.difference(currNeighbors, diff));
Saurav Dasc88d4662017-05-15 15:34:25 -0700481 } else {
Saurav Das7bcbe702017-06-13 15:35:54 -0700482 tempStore.put(dskey, Sets.union(currNeighbors, diff));
Saurav Dasc88d4662017-05-15 15:34:25 -0700483 }
sangho834e4b02015-05-01 09:38:25 -0700484 }
Saurav Das041bb782017-08-14 16:44:43 -0700485 success &= suc;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800486 }
487
Saurav Dasc88d4662017-05-15 15:34:25 -0700488 if (!foundNextObjective) {
489 log.debug("Cannot find any nextObjectives for route targetSw:{} "
490 + "-> dstSw:{}", targetSw, destSw);
491 return true; // nothing to do, return true so ECMPspg is updated
492 }
493
Saurav Das7bcbe702017-06-13 15:35:54 -0700494 // update the dsNextObjectiveStore with new destinationSet to nextId mappings
495 for (DestinationSetNextObjectiveStoreKey key : tempStore.keySet()) {
Saurav Das041bb782017-08-14 16:44:43 -0700496 NextNeighbors currentNextHops = dsNextObjStore.get(key);
497 if (currentNextHops == null) {
498 log.warn("fixHashGroups could not update global store in "
499 + "device {} .. missing nextNeighbors for key {}",
500 deviceId, key);
Saurav Dasc88d4662017-05-15 15:34:25 -0700501 continue;
502 }
Saurav Das041bb782017-08-14 16:44:43 -0700503 Set<DeviceId> newNeighbors = new HashSet<>();
504 newNeighbors.addAll(tempStore.get(key));
505 Map<DeviceId, Set<DeviceId>> oldDstNextHops =
506 ImmutableMap.copyOf(currentNextHops.dstNextHops());
507 currentNextHops.dstNextHops().put(destSw, newNeighbors); //local change
508 log.debug("Updating nsNextObjStore target:{} -> dst:{} in key:{} nextId:{}",
509 targetSw, destSw, key, currentNextHops.nextId());
510 log.debug("Old dstNextHops: {}", oldDstNextHops);
511 log.debug("New dstNextHops: {}", currentNextHops.dstNextHops());
512 // update global store
513 dsNextObjStore.put(key,
514 new NextNeighbors(currentNextHops.dstNextHops(),
515 currentNextHops.nextId()));
Saurav Dasc88d4662017-05-15 15:34:25 -0700516 }
Saurav Das041bb782017-08-14 16:44:43 -0700517 // even if one fails and others succeed, return false so ECMPspg not updated
518 return success;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800519 }
520
Saurav Dasceccf242017-08-03 18:30:35 -0700521 /**
522 * Updates the DestinationSetNextObjectiveStore with any per-destination nexthops
523 * that are not already in the store for the given DestinationSet. Note that
524 * this method does not remove existing next hops for the destinations in the
525 * DestinationSet.
526 *
527 * @param ds the DestinationSet for which the next hops need to be updated
528 * @param newDstNextHops a map of per-destination next hops to update the
529 * destinationSet with
530 * @return true if successful in updating all next hops
531 */
532 private boolean updateNextHops(DestinationSet ds,
Saurav Das7bcbe702017-06-13 15:35:54 -0700533 Map<DeviceId, Set<DeviceId>> newDstNextHops) {
534 DestinationSetNextObjectiveStoreKey key =
535 new DestinationSetNextObjectiveStoreKey(deviceId, ds);
536 NextNeighbors currNext = dsNextObjStore.get(key);
537 Map<DeviceId, Set<DeviceId>> currDstNextHops = currNext.dstNextHops();
538
539 // add newDstNextHops to currDstNextHops for each dst
540 boolean success = true;
541 for (DeviceId dstSw : ds.getDestinationSwitches()) {
542 Set<DeviceId> currNhops = currDstNextHops.get(dstSw);
543 Set<DeviceId> newNhops = newDstNextHops.get(dstSw);
544 currNhops = (currNhops == null) ? Sets.newHashSet() : currNhops;
545 newNhops = (newNhops == null) ? Sets.newHashSet() : newNhops;
546 int edgeLabel = ds.getEdgeLabel(dstSw);
547 int nextId = currNext.nextId();
548
549 // new next hops should be added
550 boolean suc = updateAllPortsToNextHop(Sets.difference(newNhops, currNhops),
551 edgeLabel, nextId, false);
552 if (suc) {
553 currNhops.addAll(newNhops);
554 currDstNextHops.put(dstSw, currNhops); // this is only a local change
555 }
556 success &= suc;
557 }
558
559 if (success) {
560 // update global store
561 dsNextObjStore.put(key, new NextNeighbors(currDstNextHops,
562 currNext.nextId()));
563 log.debug("Updated device:{} ds:{} new next-hops: {}", deviceId, ds,
564 dsNextObjStore.get(key));
565 }
566 return success;
567 }
568
Saurav Dasceccf242017-08-03 18:30:35 -0700569 /**
Saurav Das041bb782017-08-14 16:44:43 -0700570 * Adds or removes buckets for all ports to a set of neighbor devices. Caller
571 * needs to ensure that the given neighbors are all next hops towards the
572 * same destination (represented by the given edgeLabel).
Saurav Dasceccf242017-08-03 18:30:35 -0700573 *
574 * @param neighbors set of neighbor device ids
575 * @param edgeLabel MPLS label to use in buckets
576 * @param nextId the nextObjective to change
577 * @param revoke true if buckets need to be removed, false if they need to
578 * be added
579 * @return true if successful in adding or removing buckets for all ports
580 * to the neighbors
581 */
582 private boolean updateAllPortsToNextHop(Set<DeviceId> neighbors, int edgeLabel,
Saurav Das7bcbe702017-06-13 15:35:54 -0700583 int nextId, boolean revoke) {
Saurav Dasceccf242017-08-03 18:30:35 -0700584 for (DeviceId neighbor : neighbors) {
Saurav Das041bb782017-08-14 16:44:43 -0700585 MacAddress neighborMac;
Saurav Das7bcbe702017-06-13 15:35:54 -0700586 try {
Saurav Das041bb782017-08-14 16:44:43 -0700587 neighborMac = deviceConfig.getDeviceMac(neighbor);
Saurav Das7bcbe702017-06-13 15:35:54 -0700588 } catch (DeviceConfigNotFoundException e) {
Saurav Das041bb782017-08-14 16:44:43 -0700589 log.warn(e.getMessage() + " Aborting updateAllPortsToNextHop"
590 + " for nextId:" + nextId);
Saurav Das7bcbe702017-06-13 15:35:54 -0700591 return false;
592 }
Saurav Das041bb782017-08-14 16:44:43 -0700593 Collection<PortNumber> portsToNeighbor = devicePortMap.get(neighbor);
594 if (portsToNeighbor == null || portsToNeighbor.isEmpty()) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700595 log.warn("No ports found in dev:{} for neighbor:{} .. cannot "
Saurav Das041bb782017-08-14 16:44:43 -0700596 + "updateAllPortsToNextHop for nextId: {}",
Saurav Das7bcbe702017-06-13 15:35:54 -0700597 deviceId, neighbor, nextId);
598 return false;
599 }
Saurav Das041bb782017-08-14 16:44:43 -0700600 List<PortLabel> pl = Lists.newArrayList();
601 portsToNeighbor.forEach(p -> pl.add(new PortLabel(p, edgeLabel)));
Saurav Das7bcbe702017-06-13 15:35:54 -0700602 if (revoke) {
Saurav Das041bb782017-08-14 16:44:43 -0700603 log.debug("updateAllPortsToNextHops in device {}: Removing Bucket(s) "
604 + "with Port/Label:{} to next object id {}",
605 deviceId, pl, nextId);
606 removeFromHashedNextObjective(pl, neighborMac, nextId);
Saurav Das7bcbe702017-06-13 15:35:54 -0700607 } else {
Saurav Das041bb782017-08-14 16:44:43 -0700608 log.debug("fixHashGroup in device {}: Adding Bucket(s) "
609 + "with Port/Label: {} to next object id {}",
610 deviceId, pl, nextId);
611 addToHashedNextObjective(pl, neighborMac, nextId);
Saurav Das7bcbe702017-06-13 15:35:54 -0700612 }
613 }
614 return true;
615 }
616
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800617 /**
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800618 * Adds or removes a port that has been configured with a vlan to a broadcast group
619 * for bridging. Should only be called by the master instance for this device.
Saurav Das1a129a02016-11-18 15:21:57 -0800620 *
621 * @param port the port on this device that needs to be added/removed to a bcast group
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800622 * @param vlanId the vlan id corresponding to the broadcast domain/group
623 * @param popVlan indicates if packets should be sent out untagged or not out
624 * of the port. If true, indicates an access (untagged) or native vlan
625 * configuration. If false, indicates a trunk (tagged) vlan config.
Saurav Das1a129a02016-11-18 15:21:57 -0800626 * @param portUp true if port is enabled, false if disabled
Saurav Das1a129a02016-11-18 15:21:57 -0800627 */
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800628 public void processEdgePort(PortNumber port, VlanId vlanId,
629 boolean popVlan, boolean portUp) {
Saurav Das1a129a02016-11-18 15:21:57 -0800630 //get the next id for the subnet and edit it.
Charles Chan59cc16d2017-02-02 16:20:42 -0800631 Integer nextId = getVlanNextObjectiveId(vlanId);
Saurav Das1a129a02016-11-18 15:21:57 -0800632 if (nextId == -1) {
633 if (portUp) {
634 log.debug("**Creating flooding group for first port enabled in"
Saurav Das9aa28bd2017-12-05 15:00:23 -0800635 + " vlan {} on dev {} port {}", vlanId, deviceId, port);
Charles Chan59cc16d2017-02-02 16:20:42 -0800636 createBcastGroupFromVlan(vlanId, Collections.singleton(port));
Saurav Das1a129a02016-11-18 15:21:57 -0800637 } else {
638 log.warn("Could not find flooding group for subnet {} on dev:{} when"
Charles Chan59cc16d2017-02-02 16:20:42 -0800639 + " removing port:{}", vlanId, deviceId, port);
Saurav Das1a129a02016-11-18 15:21:57 -0800640 }
641 return;
642 }
643
644 log.info("**port{} in device {}: {} Bucket with Port {} to"
645 + " next-id {}", (portUp) ? "UP" : "DOWN", deviceId,
646 (portUp) ? "Adding" : "Removing",
647 port, nextId);
648 // Create the bucket to be added or removed
649 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800650 if (popVlan) {
651 tBuilder.popVlan();
652 }
Saurav Das1a129a02016-11-18 15:21:57 -0800653 tBuilder.setOutput(port);
654
Saurav Das1a129a02016-11-18 15:21:57 -0800655 TrafficSelector metadata =
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800656 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
Saurav Das1a129a02016-11-18 15:21:57 -0800657
658 NextObjective.Builder nextObjBuilder = DefaultNextObjective
659 .builder().withId(nextId)
660 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
661 .addTreatment(tBuilder.build())
662 .withMeta(metadata);
663
664 ObjectiveContext context = new DefaultObjectiveContext(
665 (objective) -> log.debug("port {} successfully {} NextObj {} on {}",
666 port, (portUp) ? "addedTo" : "removedFrom",
667 nextId, deviceId),
668 (objective, error) ->
669 log.warn("port {} failed to {} NextObj {} on {}: {}",
670 port, (portUp) ? "addTo" : "removeFrom",
671 nextId, deviceId, error));
672
673 NextObjective nextObj = (portUp) ? nextObjBuilder.addToExisting(context)
674 : nextObjBuilder.removeFromExisting(context);
675 log.debug("edgePort processed: Submited next objective {} in device {}",
676 nextId, deviceId);
677 flowObjectiveService.next(deviceId, nextObj);
678 }
679
680 /**
Saurav Das7bcbe702017-06-13 15:35:54 -0700681 * Returns the next objective of type hashed associated with the destination set.
682 * In addition, updates the existing next-objective if new route-route paths found
683 * have resulted in the addition of new next-hops to a particular destination.
684 * If there is no existing next objective for this destination set, this method
685 * would create a next objective and return the nextId. Optionally metadata can be
Saurav Das8a0732e2015-11-20 15:27:53 -0800686 * passed in for the creation of the next objective.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800687 *
Saurav Das7bcbe702017-06-13 15:35:54 -0700688 * @param ds destination set
689 * @param nextHops a map of per destination next hops
Saurav Das8a0732e2015-11-20 15:27:53 -0800690 * @param meta metadata passed into the creation of a Next Objective
Pier Ventre917127a2016-10-31 16:49:19 -0700691 * @param isBos if Bos is set
Saurav Das8a0732e2015-11-20 15:27:53 -0800692 * @return int if found or -1 if there are errors in the creation of the
693 * neighbor set.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800694 */
Saurav Das7bcbe702017-06-13 15:35:54 -0700695 public int getNextObjectiveId(DestinationSet ds,
696 Map<DeviceId, Set<DeviceId>> nextHops,
697 TrafficSelector meta, boolean isBos) {
698 NextNeighbors next = dsNextObjStore.
699 get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
700 if (next == null) {
701 log.debug("getNextObjectiveId in device{}: Next objective id "
702 + "not found for {} ... creating", deviceId, ds);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700703 log.trace("getNextObjectiveId: nsNextObjStore contents for device {}: {}",
704 deviceId,
Saurav Das7bcbe702017-06-13 15:35:54 -0700705 dsNextObjStore.entrySet()
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700706 .stream()
707 .filter((nsStoreEntry) ->
708 (nsStoreEntry.getKey().deviceId().equals(deviceId)))
709 .collect(Collectors.toList()));
Saurav Das7bcbe702017-06-13 15:35:54 -0700710
711 createGroupFromDestinationSet(ds, nextHops, meta, isBos);
712 next = dsNextObjStore.
713 get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
714 if (next == null) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700715 log.warn("getNextObjectiveId: unable to create next objective");
Saurav Das7bcbe702017-06-13 15:35:54 -0700716 // failure in creating group
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700717 return -1;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700718 } else {
719 log.debug("getNextObjectiveId in device{}: Next objective id {} "
Saurav Das7bcbe702017-06-13 15:35:54 -0700720 + "created for {}", deviceId, next.nextId(), ds);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700721 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700722 } else {
723 log.trace("getNextObjectiveId in device{}: Next objective id {} "
Saurav Das7bcbe702017-06-13 15:35:54 -0700724 + "found for {}", deviceId, next.nextId(), ds);
725 // should fix hash groups too if next-hops have changed
726 if (!next.dstNextHops().equals(nextHops)) {
727 log.debug("Nexthops have changed for dev:{} nextId:{} ..updating",
728 deviceId, next.nextId());
729 if (!updateNextHops(ds, nextHops)) {
730 // failure in updating group
731 return -1;
732 }
733 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700734 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700735 return next.nextId();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800736 }
737
sangho0b2b6d12015-05-20 22:16:38 -0700738 /**
Charles Chan59cc16d2017-02-02 16:20:42 -0800739 * Returns the next objective of type broadcast associated with the vlan,
Saurav Das4ce45962015-11-24 23:21:05 -0800740 * or -1 if no such objective exists. Note that this method does NOT create
741 * the next objective as a side-effect. It is expected that is objective is
Saurav Das1a129a02016-11-18 15:21:57 -0800742 * created at startup from network configuration. Typically this is used
743 * for L2 flooding within the subnet configured on the switch.
Charles Chanc42e84e2015-10-20 16:24:19 -0700744 *
Charles Chan59cc16d2017-02-02 16:20:42 -0800745 * @param vlanId vlan id
Charles Chanc42e84e2015-10-20 16:24:19 -0700746 * @return int if found or -1
747 */
Charles Chan59cc16d2017-02-02 16:20:42 -0800748 public int getVlanNextObjectiveId(VlanId vlanId) {
749 Integer nextId = vlanNextObjStore.
750 get(new VlanNextObjectiveStoreKey(deviceId, vlanId));
Charles Chan9f676b62015-10-29 14:58:10 -0700751
752 return (nextId != null) ? nextId : -1;
Charles Chanc42e84e2015-10-20 16:24:19 -0700753 }
754
755 /**
Saurav Das4ce45962015-11-24 23:21:05 -0800756 * Returns the next objective of type simple associated with the port on the
757 * device, given the treatment. Different treatments to the same port result
758 * in different next objectives. If no such objective exists, this method
Saurav Das961beb22017-03-29 19:09:17 -0700759 * creates one (if requested) and returns the id. Optionally metadata can be passed in for
Saurav Das1a129a02016-11-18 15:21:57 -0800760 * the creation of the objective. Typically this is used for L2 and L3 forwarding
761 * to compute nodes and containers/VMs on the compute nodes directly attached
762 * to the switch.
Saurav Das4ce45962015-11-24 23:21:05 -0800763 *
764 * @param portNum the port number for the simple next objective
765 * @param treatment the actions to apply on the packets (should include outport)
766 * @param meta optional metadata passed into the creation of the next objective
Saurav Das961beb22017-03-29 19:09:17 -0700767 * @param createIfMissing true if a next object should be created if not found
Saurav Das4ce45962015-11-24 23:21:05 -0800768 * @return int if found or created, -1 if there are errors during the
769 * creation of the next objective.
770 */
771 public int getPortNextObjectiveId(PortNumber portNum, TrafficTreatment treatment,
Saurav Das961beb22017-03-29 19:09:17 -0700772 TrafficSelector meta, boolean createIfMissing) {
Charles Chane849c192016-01-11 18:28:54 -0800773 Integer nextId = portNextObjStore
Saurav Das76ae6812017-03-15 15:15:14 -0700774 .get(new PortNextObjectiveStoreKey(deviceId, portNum, treatment, meta));
Saurav Das961beb22017-03-29 19:09:17 -0700775 if (nextId != null) {
776 return nextId;
777 }
778 log.debug("getPortNextObjectiveId in device {}: Next objective id "
779 + "not found for port: {} .. {}", deviceId, portNum,
780 (createIfMissing) ? "creating" : "aborting");
781 if (!createIfMissing) {
782 return -1;
783 }
784 // create missing next objective
785 createGroupFromPort(portNum, treatment, meta);
786 nextId = portNextObjStore.get(new PortNextObjectiveStoreKey(deviceId, portNum,
787 treatment, meta));
Saurav Das4ce45962015-11-24 23:21:05 -0800788 if (nextId == null) {
Saurav Das961beb22017-03-29 19:09:17 -0700789 log.warn("getPortNextObjectiveId: unable to create next obj"
790 + "for dev:{} port:{}", deviceId, portNum);
791 return -1;
Charles Chane849c192016-01-11 18:28:54 -0800792 }
793 return nextId;
794 }
795
796 /**
sangho0b2b6d12015-05-20 22:16:38 -0700797 * Checks if the next objective ID (group) for the neighbor set exists or not.
798 *
799 * @param ns neighbor set to check
800 * @return true if it exists, false otherwise
801 */
Saurav Das7bcbe702017-06-13 15:35:54 -0700802 public boolean hasNextObjectiveId(DestinationSet ns) {
803 NextNeighbors nextHops = dsNextObjStore.
804 get(new DestinationSetNextObjectiveStoreKey(deviceId, ns));
805 if (nextHops == null) {
sangho0b2b6d12015-05-20 22:16:38 -0700806 return false;
807 }
808
809 return true;
810 }
811
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800812 private void populateNeighborMaps() {
813 Set<Link> outgoingLinks = linkService.getDeviceEgressLinks(deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700814 for (Link link : outgoingLinks) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800815 if (link.type() != Link.Type.DIRECT) {
816 continue;
817 }
818 addNeighborAtPort(link.dst().deviceId(), link.src().port());
819 }
820 }
821
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700822 protected void addNeighborAtPort(DeviceId neighborId,
823 PortNumber portToNeighbor) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800824 // Update DeviceToPort database
825 log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
826 deviceId, neighborId, portToNeighbor);
Saurav Das8a0732e2015-11-20 15:27:53 -0800827 Set<PortNumber> ports = Collections
828 .newSetFromMap(new ConcurrentHashMap<PortNumber, Boolean>());
829 ports.add(portToNeighbor);
830 Set<PortNumber> portnums = devicePortMap.putIfAbsent(neighborId, ports);
831 if (portnums != null) {
832 portnums.add(portToNeighbor);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800833 }
834
835 // Update portToDevice database
Saurav Das8a0732e2015-11-20 15:27:53 -0800836 DeviceId prev = portDeviceMap.putIfAbsent(portToNeighbor, neighborId);
837 if (prev != null) {
Saurav Dasc88d4662017-05-15 15:34:25 -0700838 log.debug("Device: {} port: {} already has neighbor: {} ",
839 deviceId, portToNeighbor, prev, neighborId);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800840 }
841 }
842
sangho1e575652015-05-14 00:39:53 -0700843 /**
Saurav Das7bcbe702017-06-13 15:35:54 -0700844 * Creates a NextObjective for a hash group in this device from a given
845 * DestinationSet.
sangho1e575652015-05-14 00:39:53 -0700846 *
Saurav Das7bcbe702017-06-13 15:35:54 -0700847 * @param ds the DestinationSet
848 * @param neighbors a map for each destination and its next-hops
Saurav Das8a0732e2015-11-20 15:27:53 -0800849 * @param meta metadata passed into the creation of a Next Objective
Pier Ventre917127a2016-10-31 16:49:19 -0700850 * @param isBos if BoS is set
sangho1e575652015-05-14 00:39:53 -0700851 */
Saurav Das7bcbe702017-06-13 15:35:54 -0700852 public void createGroupFromDestinationSet(DestinationSet ds,
853 Map<DeviceId, Set<DeviceId>> neighbors,
854 TrafficSelector meta,
855 boolean isBos) {
856 int nextId = flowObjectiveService.allocateNextId();
857 NextObjective.Type type = NextObjective.Type.HASHED;
858 if (neighbors == null || neighbors.isEmpty()) {
859 log.warn("createGroupsFromDestinationSet: needs at least one neighbor"
860 + "to create group in dev:{} for ds: {} with next-hops {}",
861 deviceId, ds, neighbors);
862 return;
863 }
864 // If Bos == False and MPLS-ECMP == false, we have
865 // to use simple group and we will pick a single neighbor for a single dest.
866 if (!isBos && !srManager.getMplsEcmp()) {
867 type = NextObjective.Type.SIMPLE;
868 }
869
870 NextObjective.Builder nextObjBuilder = DefaultNextObjective
871 .builder()
872 .withId(nextId)
873 .withType(type)
874 .fromApp(appId);
875 if (meta != null) {
876 nextObjBuilder.withMeta(meta);
877 }
878
879 // create treatment buckets for each neighbor for each dst Device
880 // except in the special case where we only want to pick a single
881 // neighbor for a simple group
882 boolean foundSingleNeighbor = false;
883 boolean treatmentAdded = false;
884 Map<DeviceId, Set<DeviceId>> dstNextHops = new ConcurrentHashMap<>();
885 for (DeviceId dst : ds.getDestinationSwitches()) {
886 Set<DeviceId> nextHops = neighbors.get(dst);
887 if (nextHops == null || nextHops.isEmpty()) {
888 continue;
Pier Ventre917127a2016-10-31 16:49:19 -0700889 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700890
891 if (foundSingleNeighbor) {
892 break;
893 }
894
895 for (DeviceId neighborId : nextHops) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800896 if (devicePortMap.get(neighborId) == null) {
897 log.warn("Neighbor {} is not in the port map yet for dev:{}",
898 neighborId, deviceId);
sangho834e4b02015-05-01 09:38:25 -0700899 return;
Jon Hallcbd1b392017-01-18 20:15:44 -0800900 } else if (devicePortMap.get(neighborId).isEmpty()) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700901 log.warn("There are no ports for "
Saurav Das8a0732e2015-11-20 15:27:53 -0800902 + "the Device {} in the port map yet", neighborId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700903 return;
sangho834e4b02015-05-01 09:38:25 -0700904 }
905
Saurav Das8a0732e2015-11-20 15:27:53 -0800906 MacAddress neighborMac;
Charles Chan0b4e6182015-11-03 10:42:14 -0800907 try {
Saurav Das8a0732e2015-11-20 15:27:53 -0800908 neighborMac = deviceConfig.getDeviceMac(neighborId);
Charles Chan0b4e6182015-11-03 10:42:14 -0800909 } catch (DeviceConfigNotFoundException e) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700910 log.warn(e.getMessage() + " Aborting createGroupsFromDestinationset.");
Charles Chan0b4e6182015-11-03 10:42:14 -0800911 return;
912 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700913 // For each port to the neighbor, we create a new treatment
Pier Ventre917127a2016-10-31 16:49:19 -0700914 Set<PortNumber> neighborPorts = devicePortMap.get(neighborId);
915 // In this case we are using a SIMPLE group. We randomly pick a port
916 if (!isBos && !srManager.getMplsEcmp()) {
917 int size = devicePortMap.get(neighborId).size();
918 int index = RandomUtils.nextInt(0, size);
919 neighborPorts = Collections.singleton(
Saurav Das7bcbe702017-06-13 15:35:54 -0700920 Iterables.get(devicePortMap.get(neighborId),
921 index));
922 foundSingleNeighbor = true;
Pier Ventre917127a2016-10-31 16:49:19 -0700923 }
924 for (PortNumber sp : neighborPorts) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700925 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
926 .builder();
Saurav Das7bcbe702017-06-13 15:35:54 -0700927 tBuilder.setEthDst(neighborMac).setEthSrc(nodeMacAddr);
928 int edgeLabel = ds.getEdgeLabel(dst);
929 if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800930 tBuilder.pushMpls()
Saurav Das7bcbe702017-06-13 15:35:54 -0700931 .copyTtlOut()
932 .setMpls(MplsLabel.mplsLabel(edgeLabel));
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700933 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800934 tBuilder.setOutput(sp);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700935 nextObjBuilder.addTreatment(tBuilder.build());
Saurav Das7bcbe702017-06-13 15:35:54 -0700936 treatmentAdded = true;
937 //update store
938 Set<DeviceId> existingNeighbors = dstNextHops.get(dst);
939 if (existingNeighbors == null) {
940 existingNeighbors = new HashSet<>();
941 }
942 existingNeighbors.add(neighborId);
943 dstNextHops.put(dst, existingNeighbors);
944 log.debug("creating treatment for port/label {}/{} in next:{}",
945 sp, edgeLabel, nextId);
946 }
947
948 if (foundSingleNeighbor) {
949 break;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800950 }
951 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800952 }
Saurav Das7bcbe702017-06-13 15:35:54 -0700953
954 if (!treatmentAdded) {
955 log.warn("Could not createGroup from DestinationSet {} without any"
956 + "next hops {}", ds, neighbors);
957 return;
958 }
959 ObjectiveContext context = new DefaultObjectiveContext(
960 (objective) ->
961 log.debug("createGroupsFromDestinationSet installed "
962 + "NextObj {} on {}", nextId, deviceId),
963 (objective, error) ->
964 log.warn("createGroupsFromDestinationSet failed to install"
965 + " NextObj {} on {}: {}", nextId, deviceId, error)
966 );
967 NextObjective nextObj = nextObjBuilder.add(context);
968 log.debug(".. createGroupsFromDestinationSet: Submitted "
969 + "next objective {} in device {}", nextId, deviceId);
970 flowObjectiveService.next(deviceId, nextObj);
971 //update store
972 dsNextObjStore.put(new DestinationSetNextObjectiveStoreKey(deviceId, ds),
973 new NextNeighbors(dstNextHops, nextId));
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800974 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700975
Saurav Das4ce45962015-11-24 23:21:05 -0800976 /**
Saurav Das1a129a02016-11-18 15:21:57 -0800977 * Creates broadcast groups for all ports in the same subnet for
978 * all configured subnets.
Saurav Das4ce45962015-11-24 23:21:05 -0800979 */
Charles Chan59cc16d2017-02-02 16:20:42 -0800980 public void createGroupsFromVlanConfig() {
Charles Chan7ffd81f2017-02-08 15:52:08 -0800981 srManager.getVlanPortMap(deviceId).asMap().forEach((vlanId, ports) -> {
Charles Chan59cc16d2017-02-02 16:20:42 -0800982 createBcastGroupFromVlan(vlanId, ports);
Pier Ventre10bd8d12016-11-26 21:05:22 -0800983 });
Saurav Das1a129a02016-11-18 15:21:57 -0800984 }
Charles Chan9f676b62015-10-29 14:58:10 -0700985
Saurav Das1a129a02016-11-18 15:21:57 -0800986 /**
Charles Chan59cc16d2017-02-02 16:20:42 -0800987 * Creates a single broadcast group from a given vlan id and list of ports.
Saurav Das1a129a02016-11-18 15:21:57 -0800988 *
Charles Chan59cc16d2017-02-02 16:20:42 -0800989 * @param vlanId vlan id
Saurav Das1a129a02016-11-18 15:21:57 -0800990 * @param ports list of ports in the subnet
991 */
Charles Chan7ffd81f2017-02-08 15:52:08 -0800992 public void createBcastGroupFromVlan(VlanId vlanId, Collection<PortNumber> ports) {
Charles Chan59cc16d2017-02-02 16:20:42 -0800993 VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
Charles Chan9f676b62015-10-29 14:58:10 -0700994
Charles Chan59cc16d2017-02-02 16:20:42 -0800995 if (vlanNextObjStore.containsKey(key)) {
Saurav Das1a129a02016-11-18 15:21:57 -0800996 log.debug("Broadcast group for device {} and subnet {} exists",
Charles Chan59cc16d2017-02-02 16:20:42 -0800997 deviceId, vlanId);
Saurav Das1a129a02016-11-18 15:21:57 -0800998 return;
999 }
Charles Chan188ebf52015-12-23 00:15:11 -08001000
Saurav Das1a129a02016-11-18 15:21:57 -08001001 TrafficSelector metadata =
Charles Chan59cc16d2017-02-02 16:20:42 -08001002 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
Charles Chanc42e84e2015-10-20 16:24:19 -07001003
Saurav Das1a129a02016-11-18 15:21:57 -08001004 int nextId = flowObjectiveService.allocateNextId();
Charles Chanc42e84e2015-10-20 16:24:19 -07001005
Saurav Das1a129a02016-11-18 15:21:57 -08001006 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1007 .builder().withId(nextId)
1008 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1009 .withMeta(metadata);
Charles Chanc42e84e2015-10-20 16:24:19 -07001010
Saurav Das1a129a02016-11-18 15:21:57 -08001011 ports.forEach(port -> {
1012 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Charles Chan7ffd81f2017-02-08 15:52:08 -08001013 if (toPopVlan(port, vlanId)) {
1014 tBuilder.popVlan();
1015 }
Saurav Das1a129a02016-11-18 15:21:57 -08001016 tBuilder.setOutput(port);
1017 nextObjBuilder.addTreatment(tBuilder.build());
Charles Chanc42e84e2015-10-20 16:24:19 -07001018 });
Saurav Das1a129a02016-11-18 15:21:57 -08001019
Saurav Das961beb22017-03-29 19:09:17 -07001020 ObjectiveContext context = new DefaultObjectiveContext(
1021 (objective) ->
1022 log.debug("createBroadcastGroupFromVlan installed "
1023 + "NextObj {} on {}", nextId, deviceId),
1024 (objective, error) ->
1025 log.warn("createBroadcastGroupFromVlan failed to install"
1026 + " NextObj {} on {}: {}", nextId, deviceId, error)
1027 );
1028 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Das1a129a02016-11-18 15:21:57 -08001029 flowObjectiveService.next(deviceId, nextObj);
Saurav Das9aa28bd2017-12-05 15:00:23 -08001030 log.debug("createBcastGroupFromVlan: Submitted next objective {} "
1031 + "for vlan: {} in device {}", nextId, vlanId, deviceId);
Saurav Das1a129a02016-11-18 15:21:57 -08001032
Charles Chan59cc16d2017-02-02 16:20:42 -08001033 vlanNextObjStore.put(key, nextId);
Charles Chanc42e84e2015-10-20 16:24:19 -07001034 }
1035
Charles Chane849c192016-01-11 18:28:54 -08001036 /**
Jonghwan Hyun5f1def82017-08-25 17:48:36 -07001037 * Removes a single broadcast group from a given vlan id.
1038 * The group should be empty.
1039 * @param deviceId device Id to remove the group
1040 * @param portNum port number related to the group
1041 * @param vlanId vlan id of the broadcast group to remove
1042 * @param popVlan true if the TrafficTreatment involves pop vlan tag action
1043 */
1044 public void removeBcastGroupFromVlan(DeviceId deviceId, PortNumber portNum,
1045 VlanId vlanId, boolean popVlan) {
1046 VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
1047
1048 if (!vlanNextObjStore.containsKey(key)) {
1049 log.debug("Broadcast group for device {} and subnet {} does not exist",
1050 deviceId, vlanId);
1051 return;
1052 }
1053
1054 TrafficSelector metadata =
1055 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
1056
1057 int nextId = vlanNextObjStore.get(key);
1058
1059 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1060 .builder().withId(nextId)
1061 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1062 .withMeta(metadata);
1063
1064 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1065 if (popVlan) {
1066 tBuilder.popVlan();
1067 }
1068 tBuilder.setOutput(portNum);
1069 nextObjBuilder.addTreatment(tBuilder.build());
1070
1071 ObjectiveContext context = new DefaultObjectiveContext(
1072 (objective) ->
1073 log.debug("removeBroadcastGroupFromVlan removed "
1074 + "NextObj {} on {}", nextId, deviceId),
1075 (objective, error) ->
1076 log.warn("removeBroadcastGroupFromVlan failed to remove "
1077 + " NextObj {} on {}: {}", nextId, deviceId, error)
1078 );
1079 NextObjective nextObj = nextObjBuilder.remove(context);
1080 flowObjectiveService.next(deviceId, nextObj);
1081 log.debug("removeBcastGroupFromVlan: Submited next objective {} in device {}",
1082 nextId, deviceId);
1083
1084 vlanNextObjStore.remove(key, nextId);
1085 }
1086
1087 /**
Charles Chan7ffd81f2017-02-08 15:52:08 -08001088 * Determine if we should pop given vlan before sending packets to the given port.
1089 *
1090 * @param portNumber port number
1091 * @param vlanId vlan id
1092 * @return true if the vlan id is not contained in any vlanTagged config
1093 */
1094 private boolean toPopVlan(PortNumber portNumber, VlanId vlanId) {
Saurav Das7bcbe702017-06-13 15:35:54 -07001095 return srManager.interfaceService
1096 .getInterfacesByPort(new ConnectPoint(deviceId, portNumber))
Charles Chan7ffd81f2017-02-08 15:52:08 -08001097 .stream().noneMatch(intf -> intf.vlanTagged().contains(vlanId));
1098 }
1099
1100 /**
Saurav Das4ce45962015-11-24 23:21:05 -08001101 * Create simple next objective for a single port. The treatments can include
1102 * all outgoing actions that need to happen on the packet.
1103 *
1104 * @param portNum the outgoing port on the device
1105 * @param treatment the actions to apply on the packets (should include outport)
1106 * @param meta optional data to pass to the driver
1107 */
1108 public void createGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
1109 TrafficSelector meta) {
1110 int nextId = flowObjectiveService.allocateNextId();
1111 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
Saurav Das76ae6812017-03-15 15:15:14 -07001112 deviceId, portNum, treatment, meta);
Saurav Das4ce45962015-11-24 23:21:05 -08001113
1114 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1115 .builder().withId(nextId)
1116 .withType(NextObjective.Type.SIMPLE)
1117 .addTreatment(treatment)
1118 .fromApp(appId)
1119 .withMeta(meta);
1120
Saurav Das961beb22017-03-29 19:09:17 -07001121 ObjectiveContext context = new DefaultObjectiveContext(
1122 (objective) ->
1123 log.debug("createGroupFromPort installed "
1124 + "NextObj {} on {}", nextId, deviceId),
1125 (objective, error) ->
1126 log.warn("createGroupFromPort failed to install"
1127 + " NextObj {} on {}: {}", nextId, deviceId, error)
1128 );
1129 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Das4ce45962015-11-24 23:21:05 -08001130 flowObjectiveService.next(deviceId, nextObj);
1131 log.debug("createGroupFromPort: Submited next objective {} in device {} "
1132 + "for port {}", nextId, deviceId, portNum);
1133
1134 portNextObjStore.put(key, nextId);
1135 }
1136
sangho1e575652015-05-14 00:39:53 -07001137 /**
Jonghwan Hyun5f1def82017-08-25 17:48:36 -07001138 * Removes simple next objective for a single port.
1139 *
1140 * @param deviceId device id that has the port to deal with
1141 * @param portNum the outgoing port on the device
1142 * @param vlanId vlan id associated with the port
1143 * @param popVlan true if POP_VLAN action is applied on the packets, false otherwise
1144 */
1145 public void removePortNextObjective(DeviceId deviceId, PortNumber portNum, VlanId vlanId, boolean popVlan) {
1146 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
1147 mbuilder.matchVlanId(vlanId);
1148
1149 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
1150 tbuilder.immediate().setOutput(portNum);
1151 if (popVlan) {
1152 tbuilder.immediate().popVlan();
1153 }
1154
1155 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, portNum,
1156 tbuilder.build(), mbuilder.build(), false);
1157
1158 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
1159 deviceId, portNum, tbuilder.build(), mbuilder.build());
1160 if (portNextObjId != -1 && portNextObjStore.containsKey(key)) {
1161 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1162 .builder().withId(portNextObjId)
1163 .withType(NextObjective.Type.SIMPLE).fromApp(appId);
1164 ObjectiveContext context = new DefaultObjectiveContext(
1165 (objective) -> log.debug("removePortNextObjective removes NextObj {} on {}",
1166 portNextObjId, deviceId),
1167 (objective, error) ->
1168 log.warn("removePortNextObjective failed to remove NextObj {} on {}: {}",
1169 portNextObjId, deviceId, error));
1170 NextObjective nextObjective = nextObjBuilder.remove(context);
1171 log.info("**removePortNextObjective: Submitted "
1172 + "next objective {} in device {}",
1173 portNextObjId, deviceId);
1174 flowObjectiveService.next(deviceId, nextObjective);
1175
1176 portNextObjStore.remove(key);
1177 }
1178 }
1179 /**
sangho1e575652015-05-14 00:39:53 -07001180 * Removes groups for the next objective ID given.
1181 *
1182 * @param objectiveId next objective ID to remove
1183 * @return true if succeeds, false otherwise
1184 */
1185 public boolean removeGroup(int objectiveId) {
Saurav Das7bcbe702017-06-13 15:35:54 -07001186 for (Map.Entry<DestinationSetNextObjectiveStoreKey, NextNeighbors> e :
1187 dsNextObjStore.entrySet()) {
1188 if (e.getValue().nextId() != objectiveId) {
1189 continue;
1190 }
Pier Luigi8b4d20d2018-01-14 21:56:11 +01001191 // Right now it is just used in TunnelHandler
1192 // remember in future that PW transit groups could
1193 // be Indirect groups
sangho1e575652015-05-14 00:39:53 -07001194 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1195 .builder().withId(objectiveId)
1196 .withType(NextObjective.Type.HASHED).fromApp(appId);
Charles Chan216e3c82016-04-23 14:48:16 -07001197 ObjectiveContext context = new DefaultObjectiveContext(
1198 (objective) -> log.debug("RemoveGroup removes NextObj {} on {}",
1199 objectiveId, deviceId),
1200 (objective, error) ->
1201 log.warn("RemoveGroup failed to remove NextObj {} on {}: {}",
1202 objectiveId, deviceId, error));
1203 NextObjective nextObjective = nextObjBuilder.remove(context);
Saurav Das8a0732e2015-11-20 15:27:53 -08001204 log.info("**removeGroup: Submited "
1205 + "next objective {} in device {}",
1206 objectiveId, deviceId);
sangho1e575652015-05-14 00:39:53 -07001207 flowObjectiveService.next(deviceId, nextObjective);
1208
Saurav Das7bcbe702017-06-13 15:35:54 -07001209 dsNextObjStore.remove(e.getKey());
sangho0b2b6d12015-05-20 22:16:38 -07001210 return true;
sangho1e575652015-05-14 00:39:53 -07001211 }
1212
1213 return false;
1214 }
Jonghwan Hyun5f1def82017-08-25 17:48:36 -07001215 /**
1216 * Remove simple next objective for a single port. The treatments can include
1217 * all outgoing actions that need to happen on the packet.
1218 *
1219 * @param portNum the outgoing port on the device
1220 * @param treatment the actions applied on the packets (should include outport)
1221 * @param meta optional data to pass to the driver
1222 */
1223 public void removeGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
1224 TrafficSelector meta) {
1225 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
1226 deviceId, portNum, treatment, meta);
1227 Integer nextId = portNextObjStore.get(key);
1228
1229 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1230 .builder().withId(nextId)
1231 .withType(NextObjective.Type.SIMPLE)
1232 .addTreatment(treatment)
1233 .fromApp(appId)
1234 .withMeta(meta);
1235
1236 ObjectiveContext context = new DefaultObjectiveContext(
1237 (objective) ->
1238 log.info("removeGroupFromPort installed "
1239 + "NextObj {} on {}", nextId, deviceId),
1240 (objective, error) ->
1241 log.warn("removeGroupFromPort failed to install"
1242 + " NextObj {} on {}: {}", nextId, deviceId, error)
1243 );
1244 NextObjective nextObj = nextObjBuilder.remove(context);
1245 flowObjectiveService.next(deviceId, nextObj);
1246 log.info("removeGroupFromPort: Submitted next objective {} in device {} "
1247 + "for port {}", nextId, deviceId, portNum);
1248
1249 portNextObjStore.remove(key);
1250 }
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -07001251
Charles Chane849c192016-01-11 18:28:54 -08001252 /**
1253 * Removes all groups from all next objective stores.
1254 */
Saurav Das7bcbe702017-06-13 15:35:54 -07001255 /*public void removeAllGroups() {
1256 for (Map.Entry<NeighborSetNextObjectiveStoreKey, NextNeighbors> entry:
Saurav Das423fe2b2015-12-04 10:52:59 -08001257 nsNextObjStore.entrySet()) {
Saurav Das7bcbe702017-06-13 15:35:54 -07001258 removeGroup(entry.getValue().nextId());
Saurav Das423fe2b2015-12-04 10:52:59 -08001259 }
1260 for (Map.Entry<PortNextObjectiveStoreKey, Integer> entry:
1261 portNextObjStore.entrySet()) {
1262 removeGroup(entry.getValue());
1263 }
Charles Chan59cc16d2017-02-02 16:20:42 -08001264 for (Map.Entry<VlanNextObjectiveStoreKey, Integer> entry:
1265 vlanNextObjStore.entrySet()) {
Saurav Das423fe2b2015-12-04 10:52:59 -08001266 removeGroup(entry.getValue());
1267 }
Saurav Das7bcbe702017-06-13 15:35:54 -07001268 }*/ //XXX revisit
1269
Saurav Dasceccf242017-08-03 18:30:35 -07001270 /**
1271 * Triggers a one time bucket verification operation on all hash groups
1272 * on this device.
1273 */
1274 public void triggerBucketCorrector() {
1275 BucketCorrector bc = new BucketCorrector();
1276 bc.run();
1277 }
1278
Jonghwan Hyun5f1def82017-08-25 17:48:36 -07001279 public void updateGroupFromVlanConfiguration(PortNumber portNumber, Collection<VlanId> vlanIds,
1280 int nextId, boolean install) {
1281 vlanIds.forEach(vlanId -> updateGroupFromVlanInternal(vlanId, portNumber, nextId, install));
1282 }
1283
1284 private void updateGroupFromVlanInternal(VlanId vlanId, PortNumber portNum, int nextId, boolean install) {
1285 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1286 if (toPopVlan(portNum, vlanId)) {
1287 tBuilder.popVlan();
1288 }
1289 tBuilder.setOutput(portNum);
1290
1291 TrafficSelector metadata =
1292 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
1293
1294 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1295 .builder().withId(nextId)
1296 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1297 .addTreatment(tBuilder.build())
1298 .withMeta(metadata);
1299
1300 ObjectiveContext context = new DefaultObjectiveContext(
1301 (objective) -> log.debug("port {} successfully removedFrom NextObj {} on {}",
1302 portNum, nextId, deviceId),
1303 (objective, error) ->
1304 log.warn("port {} failed to removedFrom NextObj {} on {}: {}",
1305 portNum, nextId, deviceId, error));
1306
1307 if (install) {
1308 flowObjectiveService.next(deviceId, nextObjBuilder.addToExisting(context));
1309 } else {
1310 flowObjectiveService.next(deviceId, nextObjBuilder.removeFromExisting(context));
1311 }
1312 }
Saurav Das1547b3f2017-05-05 17:01:08 -07001313
1314 /**
Saurav Das041bb782017-08-14 16:44:43 -07001315 * Performs bucket verification operation for all hash groups in this device.
1316 * Checks RouteHandler to ensure that routing is stable before attempting
1317 * verification. Verification involves creating a nextObjective with
1318 * operation VERIFY for existing next objectives in the store, and passing
1319 * it to the driver. It is the driver that actually performs the verification
1320 * by adding or removing buckets to match the verification next objective
1321 * created here.
Saurav Das1547b3f2017-05-05 17:01:08 -07001322 */
Saurav Dasceccf242017-08-03 18:30:35 -07001323 protected final class BucketCorrector implements Runnable {
1324 Integer nextId;
Saurav Das1547b3f2017-05-05 17:01:08 -07001325
Saurav Dasceccf242017-08-03 18:30:35 -07001326 BucketCorrector() {
1327 this.nextId = null;
1328 }
1329
1330 BucketCorrector(Integer nextId) {
1331 this.nextId = nextId;
Saurav Das1547b3f2017-05-05 17:01:08 -07001332 }
1333
1334 @Override
1335 public void run() {
Saurav Dasceccf242017-08-03 18:30:35 -07001336 if (!srManager.mastershipService.isLocalMaster(deviceId)) {
1337 return;
Saurav Das1547b3f2017-05-05 17:01:08 -07001338 }
Saurav Dasceccf242017-08-03 18:30:35 -07001339 DefaultRoutingHandler rh = srManager.getRoutingHandler();
1340 if (rh == null) {
1341 return;
1342 }
1343 if (!rh.isRoutingStable()) {
1344 return;
1345 }
1346 rh.acquireRoutingLock();
1347 try {
Saurav Das041bb782017-08-14 16:44:43 -07001348 log.trace("running bucket corrector for dev: {}", deviceId);
Saurav Dasceccf242017-08-03 18:30:35 -07001349 Set<DestinationSetNextObjectiveStoreKey> dsKeySet = dsNextObjStore.entrySet()
1350 .stream()
1351 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
Pier Luigi8b4d20d2018-01-14 21:56:11 +01001352 // Filter out PW transit groups or include them if MPLS ECMP is supported
1353 .filter(entry -> !entry.getKey().destinationSet().mplsSet() ||
1354 (entry.getKey().destinationSet().mplsSet() && srManager.getMplsEcmp()))
Saurav Dasceccf242017-08-03 18:30:35 -07001355 .map(entry -> entry.getKey())
1356 .collect(Collectors.toSet());
1357 for (DestinationSetNextObjectiveStoreKey dsKey : dsKeySet) {
1358 NextNeighbors next = dsNextObjStore.get(dsKey);
1359 if (next == null) {
1360 continue;
1361 }
1362 int nid = next.nextId();
1363 if (nextId != null && nextId != nid) {
1364 continue;
1365 }
Saurav Das041bb782017-08-14 16:44:43 -07001366 log.trace("bkt-corr: dsNextObjStore for device {}: {}",
Saurav Dasceccf242017-08-03 18:30:35 -07001367 deviceId, dsKey, next);
1368 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
1369 metabuilder.matchVlanId(INTERNAL_VLAN);
1370 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
1371 .withId(nid)
1372 .withType(NextObjective.Type.HASHED)
1373 .withMeta(metabuilder.build())
1374 .fromApp(appId);
1375
1376 next.dstNextHops().forEach((dstDev, nextHops) -> {
1377 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dstDev);
1378 nextHops.forEach(neighbor -> {
1379 MacAddress neighborMac;
1380 try {
1381 neighborMac = deviceConfig.getDeviceMac(neighbor);
1382 } catch (DeviceConfigNotFoundException e) {
1383 log.warn(e.getMessage() + " Aborting neighbor"
1384 + neighbor);
1385 return;
1386 }
1387 devicePortMap.get(neighbor).forEach(port -> {
Saurav Das041bb782017-08-14 16:44:43 -07001388 log.trace("verify in device {} nextId {}: bucket with"
Saurav Dasceccf242017-08-03 18:30:35 -07001389 + " port/label {}/{} to dst {} via {}",
1390 deviceId, nid, port, edgeLabel,
1391 dstDev, neighbor);
1392 nextObjBuilder.addTreatment(treatmentBuilder(port,
1393 neighborMac, edgeLabel));
1394 });
1395 });
1396 });
1397
1398 NextObjective nextObjective = nextObjBuilder.verify();
1399 flowObjectiveService.next(deviceId, nextObjective);
1400 }
1401 } finally {
1402 rh.releaseRoutingLock();
1403 }
1404
1405 }
1406
1407 TrafficTreatment treatmentBuilder(PortNumber outport, MacAddress dstMac,
1408 int edgeLabel) {
1409 TrafficTreatment.Builder tBuilder =
1410 DefaultTrafficTreatment.builder();
1411 tBuilder.setOutput(outport)
1412 .setEthDst(dstMac)
1413 .setEthSrc(nodeMacAddr);
1414 if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
1415 tBuilder.pushMpls()
1416 .copyTtlOut()
1417 .setMpls(MplsLabel.mplsLabel(edgeLabel));
1418 }
1419 return tBuilder.build();
Saurav Das1547b3f2017-05-05 17:01:08 -07001420 }
1421 }
1422
Pier Luigi8b4d20d2018-01-14 21:56:11 +01001423}