blob: ab1363c164882e6b4e5c6a984048fb5b2a535f09 [file] [log] [blame]
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -07001/*
Brian O'Connor0947d7e2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -07003 *
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 */
16package org.onosproject.segmentrouting.grouphandler;
17
Saurav Dasfe0b05e2017-08-14 16:44:43 -070018import com.google.common.collect.ImmutableMap;
Pier Ventre229fd0b2016-10-31 16:49:19 -070019import com.google.common.collect.Iterables;
Saurav Dasfe0b05e2017-08-14 16:44:43 -070020import com.google.common.collect.Lists;
Saurav Das62ae6792017-05-15 15:34:25 -070021import com.google.common.collect.Sets;
22
Pier Ventre229fd0b2016-10-31 16:49:19 -070023import org.apache.commons.lang3.RandomUtils;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070024import org.onlab.packet.MacAddress;
25import org.onlab.packet.MplsLabel;
Saurav Das62af8802015-12-04 10:52:59 -080026import org.onlab.packet.VlanId;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070027import org.onlab.util.KryoNamespace;
28import org.onosproject.core.ApplicationId;
Charles Chan10b0fb72017-02-02 16:20:42 -080029import org.onosproject.net.ConnectPoint;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070030import org.onosproject.net.DeviceId;
31import org.onosproject.net.Link;
32import org.onosproject.net.PortNumber;
Saurav Das62af8802015-12-04 10:52:59 -080033import org.onosproject.net.flow.DefaultTrafficSelector;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070034import org.onosproject.net.flow.DefaultTrafficTreatment;
Saurav Das4c35fc42015-11-20 15:27:53 -080035import org.onosproject.net.flow.TrafficSelector;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070036import org.onosproject.net.flow.TrafficTreatment;
Srikanth Vavilapalli64505482015-04-21 13:04:13 -070037import org.onosproject.net.flowobjective.DefaultNextObjective;
Charles Chana4ee4f92016-04-23 14:48:16 -070038import org.onosproject.net.flowobjective.DefaultObjectiveContext;
Srikanth Vavilapalli64505482015-04-21 13:04:13 -070039import org.onosproject.net.flowobjective.FlowObjectiveService;
40import org.onosproject.net.flowobjective.NextObjective;
Srikanth Vavilapalli8c83f1d2015-05-22 13:47:31 -070041import org.onosproject.net.flowobjective.ObjectiveContext;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070042import org.onosproject.net.link.LinkService;
Saurav Dasfbe74572017-08-03 18:30:35 -070043import org.onosproject.segmentrouting.DefaultRoutingHandler;
Saurav Das62af8802015-12-04 10:52:59 -080044import org.onosproject.segmentrouting.SegmentRoutingManager;
Charles Chan319d1a22015-11-03 10:42:14 -080045import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
46import org.onosproject.segmentrouting.config.DeviceProperties;
Saurav Das261c3002017-06-13 15:35:54 -070047import org.onosproject.segmentrouting.storekey.DestinationSetNextObjectiveStoreKey;
Charles Chan1eaf4802016-04-18 13:44:03 -070048import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
Charles Chan10b0fb72017-02-02 16:20:42 -080049import org.onosproject.segmentrouting.storekey.VlanNextObjectiveStoreKey;
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -070050import org.onosproject.store.service.EventuallyConsistentMap;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070051import org.slf4j.Logger;
52
Pier Ventre229fd0b2016-10-31 16:49:19 -070053import java.net.URI;
Charles Chan90772a72017-02-08 15:52:08 -080054import java.util.Collection;
Pier Ventre229fd0b2016-10-31 16:49:19 -070055import java.util.Collections;
Saurav Das62ae6792017-05-15 15:34:25 -070056import java.util.HashMap;
Pier Ventre229fd0b2016-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 Das8a3022d2017-05-05 17:01:08 -070062import java.util.concurrent.ScheduledExecutorService;
63import java.util.concurrent.TimeUnit;
Pier Ventre229fd0b2016-10-31 16:49:19 -070064import java.util.stream.Collectors;
65
66import static com.google.common.base.Preconditions.checkNotNull;
Saurav Das8a3022d2017-05-05 17:01:08 -070067import static java.util.concurrent.Executors.newScheduledThreadPool;
68import static org.onlab.util.Tools.groupedThreads;
Charles Chan10b0fb72017-02-02 16:20:42 -080069import static org.onosproject.segmentrouting.SegmentRoutingManager.INTERNAL_VLAN;
Pier Ventre229fd0b2016-10-31 16:49:19 -070070import static org.slf4j.LoggerFactory.getLogger;
71
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070072/**
Srikanth Vavilapalli64505482015-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 Vavilapalli37a461b2015-04-07 15:12:32 -070076 */
77public class DefaultGroupHandler {
Ray Milkey5247fa92018-01-12 14:22:06 -080078 private static final Logger log = getLogger(DefaultGroupHandler.class);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070079
Saurav Dasfbe74572017-08-03 18:30:35 -070080 private static final long VERIFY_INTERVAL = 30; // secs
81
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070082 protected final DeviceId deviceId;
83 protected final ApplicationId appId;
84 protected final DeviceProperties deviceConfig;
85 protected final List<Integer> allSegmentIds;
Pier Ventreadb4ae62016-11-23 09:57:42 -080086 protected int ipv4NodeSegmentId = -1;
87 protected int ipv6NodeSegmentId = -1;
Charles Chan319d1a22015-11-03 10:42:14 -080088 protected boolean isEdgeRouter = false;
89 protected MacAddress nodeMacAddr = null;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -070090 protected LinkService linkService;
Srikanth Vavilapalli64505482015-04-21 13:04:13 -070091 protected FlowObjectiveService flowObjectiveService;
Saurav Das62ae6792017-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 Das4c35fc42015-11-20 15:27:53 -080096 protected ConcurrentHashMap<DeviceId, Set<PortNumber>> devicePortMap =
97 new ConcurrentHashMap<>();
Saurav Das62ae6792017-05-15 15:34:25 -070098 /**
99 * local store for ports on this device connected to neighbor-device-id.
100 */
Saurav Das4c35fc42015-11-20 15:27:53 -0800101 protected ConcurrentHashMap<PortNumber, DeviceId> portDeviceMap =
102 new ConcurrentHashMap<>();
Saurav Das62ae6792017-05-15 15:34:25 -0700103
Saurav Das261c3002017-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 Dasf0f592d2016-11-18 15:21:57 -0800107 // distributed store for (device+subnet-ip-prefix) mapped to next-id
Charles Chan10b0fb72017-02-02 16:20:42 -0800108 protected EventuallyConsistentMap<VlanNextObjectiveStoreKey, Integer>
109 vlanNextObjStore = null;
Saurav Dasf0f592d2016-11-18 15:21:57 -0800110 // distributed store for (device+port+treatment) mapped to next-id
Charles Chanb7f75ac2016-01-11 18:28:54 -0800111 protected EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
112 portNextObjStore = null;
Charles Chande6655c2015-12-23 00:15:11 -0800113 private SegmentRoutingManager srManager;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700114
Saurav Das8a3022d2017-05-05 17:01:08 -0700115 private ScheduledExecutorService executorService
Saurav Dasfbe74572017-08-03 18:30:35 -0700116 = newScheduledThreadPool(1, groupedThreads("bktCorrector", "bktC-%d", log));
Saurav Das8a3022d2017-05-05 17:01:08 -0700117
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700118 protected KryoNamespace.Builder kryo = new KryoNamespace.Builder()
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700119 .register(URI.class).register(HashSet.class)
Saurav Das261c3002017-06-13 15:35:54 -0700120 .register(DeviceId.class).register(PortNumber.class)
121 .register(DestinationSet.class).register(PolicyGroupIdentifier.class)
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700122 .register(PolicyGroupParams.class)
123 .register(GroupBucketIdentifier.class)
124 .register(GroupBucketIdentifier.BucketOutputType.class);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700125
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700126 protected DefaultGroupHandler(DeviceId deviceId, ApplicationId appId,
127 DeviceProperties config,
128 LinkService linkService,
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700129 FlowObjectiveService flowObjService,
Charles Chande6655c2015-12-23 00:15:11 -0800130 SegmentRoutingManager srManager) {
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700131 this.deviceId = checkNotNull(deviceId);
132 this.appId = checkNotNull(appId);
133 this.deviceConfig = checkNotNull(config);
134 this.linkService = checkNotNull(linkService);
Charles Chan319d1a22015-11-03 10:42:14 -0800135 this.allSegmentIds = checkNotNull(config.getAllDeviceSegmentIds());
136 try {
Pier Ventreadb4ae62016-11-23 09:57:42 -0800137 this.ipv4NodeSegmentId = config.getIPv4SegmentId(deviceId);
138 this.ipv6NodeSegmentId = config.getIPv6SegmentId(deviceId);
Charles Chan319d1a22015-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 Vavilapalli64505482015-04-21 13:04:13 -0700145 this.flowObjectiveService = flowObjService;
Saurav Das261c3002017-06-13 15:35:54 -0700146 this.dsNextObjStore = srManager.dsNextObjStore();
Ray Milkeyb85de082017-04-05 09:42:04 -0700147 this.vlanNextObjStore = srManager.vlanNextObjStore();
148 this.portNextObjStore = srManager.portNextObjStore();
Charles Chande6655c2015-12-23 00:15:11 -0800149 this.srManager = srManager;
Saurav Dasfbe74572017-08-03 18:30:35 -0700150 executorService.scheduleWithFixedDelay(new BucketCorrector(), 10,
151 VERIFY_INTERVAL,
152 TimeUnit.SECONDS);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700153 populateNeighborMaps();
154 }
155
156 /**
Saurav Dasfbe74572017-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 Das62ae6792017-05-15 15:34:25 -0700165 * Creates a group handler object.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700166 *
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 Vavilapalli64505482015-04-21 13:04:13 -0700171 * @param flowObjService flow objective service object
Charles Chanb7f75ac2016-01-11 18:28:54 -0800172 * @param srManager segment routing manager
Charles Chan319d1a22015-11-03 10:42:14 -0800173 * @throws DeviceConfigNotFoundException if the device configuration is not found
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700174 * @return default group handler type
175 */
Saurav Das2d94d312015-11-24 23:21:05 -0800176 public static DefaultGroupHandler createGroupHandler(
Saurav Dasfbe74572017-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 Das62ae6792017-05-15 15:34:25 -0700184 return new DefaultGroupHandler(deviceId, appId, config,
185 linkService,
186 flowObjService,
187 srManager);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700188 }
189
190 /**
Saurav Das62ae6792017-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 Vavilapalli37a461b2015-04-07 15:12:32 -0700194 */
Saurav Das62ae6792017-05-15 15:34:25 -0700195 public void portUpForLink(Link link) {
Saurav Dasfbe74572017-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 Das62ae6792017-05-15 15:34:25 -0700201
Saurav Dasfbe74572017-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 Das62ae6792017-05-15 15:34:25 -0700208
Saurav Dasfbe74572017-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 Das62ae6792017-05-15 15:34:25 -0700219
Saurav Dasfbe74572017-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 Vavilapalli37a461b2015-04-07 15:12:32 -0700225
226 /**
Saurav Das62ae6792017-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 Vavilapalli37a461b2015-04-07 15:12:32 -0700232 *
Saurav Das62ae6792017-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 Vavilapalli37a461b2015-04-07 15:12:32 -0700237 */
Saurav Das62ae6792017-05-15 15:34:25 -0700238 public void retryHash(Link link, boolean linkDown, boolean firstTime) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700239 MacAddress neighborMac;
Charles Chan319d1a22015-11-03 10:42:14 -0800240 try {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700241 neighborMac = deviceConfig.getDeviceMac(link.dst().deviceId());
Charles Chan319d1a22015-11-03 10:42:14 -0800242 } catch (DeviceConfigNotFoundException e) {
Saurav Das62ae6792017-05-15 15:34:25 -0700243 log.warn(e.getMessage() + " Aborting retryHash.");
Charles Chan319d1a22015-11-03 10:42:14 -0800244 return;
245 }
Saurav Das261c3002017-06-13 15:35:54 -0700246 // find all the destinationSets related to link
247 Set<DestinationSetNextObjectiveStoreKey> dsKeySet = dsNextObjStore.entrySet()
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700248 .stream()
Saurav Das261c3002017-06-13 15:35:54 -0700249 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
250 .filter(entry -> entry.getValue().containsNextHop(link.dst().deviceId()))
251 .map(entry -> entry.getKey())
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700252 .collect(Collectors.toSet());
Saurav Das62ae6792017-05-15 15:34:25 -0700253
Saurav Das261c3002017-06-13 15:35:54 -0700254 log.debug("retryHash: dsNextObjStore contents for linkSrc {} -> linkDst {}: {}",
255 deviceId, link.dst().deviceId(), dsKeySet);
256
257 for (DestinationSetNextObjectiveStoreKey dsKey : dsKeySet) {
258 NextNeighbors nextHops = dsNextObjStore.get(dsKey);
259 if (nextHops == null) {
Saurav Das62ae6792017-05-15 15:34:25 -0700260 log.warn("retryHash in device {}, but global store has no record "
Saurav Das261c3002017-06-13 15:35:54 -0700261 + "for dsKey:{}", deviceId, dsKey);
Saurav Das62ae6792017-05-15 15:34:25 -0700262 continue;
263 }
Saurav Das261c3002017-06-13 15:35:54 -0700264 int nextId = nextHops.nextId();
265 Set<DeviceId> dstSet = nextHops.getDstForNextHop(link.dst().deviceId());
Saurav Das62ae6792017-05-15 15:34:25 -0700266 if (!linkDown) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700267 List<PortLabel> pl = Lists.newArrayList();
Saurav Das62ae6792017-05-15 15:34:25 -0700268 if (firstTime) {
269 // some links may have come up before the next-objective was created
270 // we take this opportunity to ensure other ports to same next-hop-dst
271 // are part of the hash group (see CORD-1180). Duplicate additions
272 // to the same hash group are avoided by the driver.
273 for (PortNumber p : devicePortMap.get(link.dst().deviceId())) {
Saurav Das261c3002017-06-13 15:35:54 -0700274 dstSet.forEach(dst -> {
275 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700276 pl.add(new PortLabel(p, edgeLabel));
Saurav Das261c3002017-06-13 15:35:54 -0700277 });
Saurav Das62ae6792017-05-15 15:34:25 -0700278 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700279 addToHashedNextObjective(pl, neighborMac, nextId);
280 } else {
281 // handle only the port that came up
282 dstSet.forEach(dst -> {
283 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
284 pl.add(new PortLabel(link.src().port(), edgeLabel));
285 });
286 addToHashedNextObjective(pl, neighborMac, nextId);
Saurav Das62ae6792017-05-15 15:34:25 -0700287 }
288 } else {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700289 // linkdown
290 List<PortLabel> pl = Lists.newArrayList();
Saurav Das261c3002017-06-13 15:35:54 -0700291 dstSet.forEach(dst -> {
292 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700293 pl.add(new PortLabel(link.src().port(), edgeLabel));
Saurav Das261c3002017-06-13 15:35:54 -0700294 });
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700295 removeFromHashedNextObjective(pl, neighborMac, nextId);
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700296 }
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700297 }
Saurav Das8a3022d2017-05-05 17:01:08 -0700298 }
299
Saurav Das62ae6792017-05-15 15:34:25 -0700300 /**
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700301 * Utility class for associating output ports and the corresponding MPLS
302 * labels to push. In dual-homing, there are different labels to push
303 * corresponding to the destination switches in an edge-pair. If both
304 * destinations are reachable via the same spine, then the output-port to
305 * the spine will be associated with two labels i.e. there will be two
306 * PortLabel objects for the same port but with different labels.
307 */
308 private class PortLabel {
309 PortNumber port;
310 int edgeLabel;
311
312 PortLabel(PortNumber port, int edgeLabel) {
313 this.port = port;
314 this.edgeLabel = edgeLabel;
315 }
316
317 @Override
318 public String toString() {
319 return port.toString() + "/" + String.valueOf(edgeLabel);
320 }
321 }
322
323 /**
324 * Makes a call to the FlowObjective service to add buckets to
325 * a hashed group. User must ensure that all the ports & labels are meant
326 * same neighbor (ie. dstMac).
Saurav Das62ae6792017-05-15 15:34:25 -0700327 *
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700328 * @param portLables a collection of port & label combinations to add
329 * to the hash group identified by the nextId
Saurav Das62ae6792017-05-15 15:34:25 -0700330 * @param dstMac destination mac address of next-hop
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700331 * @param nextId id for next-objective to which buckets will be added
Saurav Dasfbe74572017-08-03 18:30:35 -0700332 *
Saurav Das62ae6792017-05-15 15:34:25 -0700333 */
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700334 private void addToHashedNextObjective(Collection<PortLabel> portLabels,
335 MacAddress dstMac, Integer nextId) {
Saurav Das8a3022d2017-05-05 17:01:08 -0700336 // setup metadata to pass to nextObjective - indicate the vlan on egress
337 // if needed by the switch pipeline. Since hashed next-hops are always to
338 // other neighboring routers, there is no subnet assigned on those ports.
339 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
340 metabuilder.matchVlanId(INTERNAL_VLAN);
Saurav Das8a3022d2017-05-05 17:01:08 -0700341 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
342 .withId(nextId)
343 .withType(NextObjective.Type.HASHED)
Saurav Das8a3022d2017-05-05 17:01:08 -0700344 .withMeta(metabuilder.build())
345 .fromApp(appId);
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700346 // Create the new buckets to be updated
347 portLabels.forEach(pl -> {
348 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
349 tBuilder.setOutput(pl.port)
350 .setEthDst(dstMac)
351 .setEthSrc(nodeMacAddr);
352 if (pl.edgeLabel != DestinationSet.NO_EDGE_LABEL) {
353 tBuilder.pushMpls()
354 .copyTtlOut()
355 .setMpls(MplsLabel.mplsLabel(pl.edgeLabel));
356 }
357 nextObjBuilder.addTreatment(tBuilder.build());
358 });
359
360 log.debug("addToHash in device {}: Adding Bucket with port/label {} "
361 + "to nextId {}", deviceId, portLabels, nextId);
Saurav Das8a3022d2017-05-05 17:01:08 -0700362
363 ObjectiveContext context = new DefaultObjectiveContext(
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700364 (objective) -> log.debug("addToHash port/label {} addedTo "
365 + "NextObj {} on {}", portLabels, nextId, deviceId),
Saurav Das8a3022d2017-05-05 17:01:08 -0700366 (objective, error) ->
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700367 log.warn("addToHash failed to add port/label {} to"
368 + " NextObj {} on {}: {}", portLabels,
Saurav Das62ae6792017-05-15 15:34:25 -0700369 nextId, deviceId, error));
Saurav Das8a3022d2017-05-05 17:01:08 -0700370 NextObjective nextObjective = nextObjBuilder.addToExisting(context);
371 flowObjectiveService.next(deviceId, nextObjective);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700372 }
373
374 /**
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700375 * Makes a call to the FlowObjective service to remove buckets from
376 * a hash group. User must ensure that all the ports & labels are meant
377 * same neighbor (ie. dstMac).
Saurav Dasfbe74572017-08-03 18:30:35 -0700378 *
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700379 * @param portLables a collection of port & label combinations to remove
380 * from the hash group identified by the nextId
Saurav Dasfbe74572017-08-03 18:30:35 -0700381 * @param dstMac destination mac address of next-hop
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700382 * @param nextId id for next-objective from which buckets will be removed
Saurav Dasfbe74572017-08-03 18:30:35 -0700383 */
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700384 private void removeFromHashedNextObjective(Collection<PortLabel> portLabels,
385 MacAddress dstMac, Integer nextId) {
Saurav Dasfbe74572017-08-03 18:30:35 -0700386 NextObjective.Builder nextObjBuilder = DefaultNextObjective
387 .builder()
388 .withType(NextObjective.Type.HASHED) //same as original
389 .withId(nextId)
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700390 .fromApp(appId);
391 // Create the buckets to be removed
392 portLabels.forEach(pl -> {
393 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
394 tBuilder.setOutput(pl.port)
395 .setEthDst(dstMac)
396 .setEthSrc(nodeMacAddr);
397 if (pl.edgeLabel != DestinationSet.NO_EDGE_LABEL) {
398 tBuilder.pushMpls()
399 .copyTtlOut()
400 .setMpls(MplsLabel.mplsLabel(pl.edgeLabel));
401 }
402 nextObjBuilder.addTreatment(tBuilder.build());
403 });
404 log.debug("removeFromHash in device {}: Removing Bucket with port/label"
405 + " {} from nextId {}", deviceId, portLabels, nextId);
Saurav Das62ae6792017-05-15 15:34:25 -0700406
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700407 ObjectiveContext context = new DefaultObjectiveContext(
408 (objective) -> log.debug("port/label {} removedFrom NextObj"
409 + " {} on {}", portLabels, nextId, deviceId),
410 (objective, error) ->
411 log.warn("port/label {} failed to removeFrom NextObj {} on "
412 + "{}: {}", portLabels, nextId, deviceId, error));
413 NextObjective nextObjective = nextObjBuilder.removeFromExisting(context);
Saurav Dasfbe74572017-08-03 18:30:35 -0700414 flowObjectiveService.next(deviceId, nextObjective);
415 }
Saurav Das62ae6792017-05-15 15:34:25 -0700416
417 /**
418 * Checks all the hash-groups in the target-switch meant for the destination
419 * switch, and either adds or removes buckets to make the neighbor-set
420 * match the given next-hops. Typically called by the master instance of the
421 * destination switch, which may be different from the master instance of the
422 * target switch where hash-group changes are made.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700423 *
Saurav Das62ae6792017-05-15 15:34:25 -0700424 * @param targetSw the switch in which the hash groups will be edited
425 * @param nextHops the current next hops for the target switch to reach
426 * the dest sw
427 * @param destSw the destination switch
428 * @param revoke true if hash groups need to remove buckets from the
429 * the groups to match the current next hops
430 * @return true if calls are made to edit buckets, or if no edits are required
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700431 */
Saurav Das62ae6792017-05-15 15:34:25 -0700432 public boolean fixHashGroups(DeviceId targetSw, Set<DeviceId> nextHops,
433 DeviceId destSw, boolean revoke) {
434 // temporary storage of keys to be updated
Saurav Das261c3002017-06-13 15:35:54 -0700435 Map<DestinationSetNextObjectiveStoreKey, Set<DeviceId>> tempStore =
Saurav Das62ae6792017-05-15 15:34:25 -0700436 new HashMap<>();
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700437 boolean foundNextObjective = false, success = true;
Charles Chan319d1a22015-11-03 10:42:14 -0800438
Saurav Das261c3002017-06-13 15:35:54 -0700439 // retrieve hash-groups meant for destSw, which have destinationSets
Saurav Das62ae6792017-05-15 15:34:25 -0700440 // with different neighbors than the given next-hops
Saurav Das261c3002017-06-13 15:35:54 -0700441 for (DestinationSetNextObjectiveStoreKey dskey : dsNextObjStore.keySet()) {
442 if (!dskey.deviceId().equals(targetSw) ||
443 !dskey.destinationSet().getDestinationSwitches().contains(destSw)) {
Saurav Das62ae6792017-05-15 15:34:25 -0700444 continue;
445 }
446 foundNextObjective = true;
Saurav Das261c3002017-06-13 15:35:54 -0700447 NextNeighbors nhops = dsNextObjStore.get(dskey);
448 Set<DeviceId> currNeighbors = nhops.nextHops(destSw);
449 int edgeLabel = dskey.destinationSet().getEdgeLabel(destSw);
450 Integer nextId = nhops.nextId();
Charles Chan319d1a22015-11-03 10:42:14 -0800451
Saurav Das50375612017-12-13 16:19:35 -0800452 if (currNeighbors == null || nextHops == null) {
453 log.warn("fixing hash groups but found currNeighbors:{} or nextHops:{}"
454 + " in targetSw:{} for dstSw:{}", currNeighbors, nextHops,
455 targetSw, destSw);
456 success &= false;
457 continue;
458 }
459
Saurav Das62ae6792017-05-15 15:34:25 -0700460 Set<DeviceId> diff;
461 if (revoke) {
462 diff = Sets.difference(currNeighbors, nextHops);
463 log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next "
464 + "hops:{} ..removing {}", targetSw, destSw, nextId,
465 currNeighbors, diff);
466 } else {
467 diff = Sets.difference(nextHops, currNeighbors);
468 log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next "
469 + "hops:{} ..adding {}", targetSw, destSw, nextId,
470 currNeighbors, diff);
471 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700472 boolean suc = updateAllPortsToNextHop(diff, edgeLabel, nextId,
473 revoke);
474 if (suc) {
475 // to update neighbor set with changes made
Saurav Das62ae6792017-05-15 15:34:25 -0700476 if (revoke) {
Saurav Das261c3002017-06-13 15:35:54 -0700477 tempStore.put(dskey, Sets.difference(currNeighbors, diff));
Saurav Das62ae6792017-05-15 15:34:25 -0700478 } else {
Saurav Das261c3002017-06-13 15:35:54 -0700479 tempStore.put(dskey, Sets.union(currNeighbors, diff));
Saurav Das62ae6792017-05-15 15:34:25 -0700480 }
sangho2165d222015-05-01 09:38:25 -0700481 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700482 success &= suc;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700483 }
484
Saurav Das62ae6792017-05-15 15:34:25 -0700485 if (!foundNextObjective) {
486 log.debug("Cannot find any nextObjectives for route targetSw:{} "
487 + "-> dstSw:{}", targetSw, destSw);
488 return true; // nothing to do, return true so ECMPspg is updated
489 }
490
Saurav Das261c3002017-06-13 15:35:54 -0700491 // update the dsNextObjectiveStore with new destinationSet to nextId mappings
492 for (DestinationSetNextObjectiveStoreKey key : tempStore.keySet()) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700493 NextNeighbors currentNextHops = dsNextObjStore.get(key);
494 if (currentNextHops == null) {
495 log.warn("fixHashGroups could not update global store in "
496 + "device {} .. missing nextNeighbors for key {}",
497 deviceId, key);
Saurav Das62ae6792017-05-15 15:34:25 -0700498 continue;
499 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700500 Set<DeviceId> newNeighbors = new HashSet<>();
501 newNeighbors.addAll(tempStore.get(key));
502 Map<DeviceId, Set<DeviceId>> oldDstNextHops =
503 ImmutableMap.copyOf(currentNextHops.dstNextHops());
504 currentNextHops.dstNextHops().put(destSw, newNeighbors); //local change
505 log.debug("Updating nsNextObjStore target:{} -> dst:{} in key:{} nextId:{}",
506 targetSw, destSw, key, currentNextHops.nextId());
507 log.debug("Old dstNextHops: {}", oldDstNextHops);
508 log.debug("New dstNextHops: {}", currentNextHops.dstNextHops());
509 // update global store
510 dsNextObjStore.put(key,
511 new NextNeighbors(currentNextHops.dstNextHops(),
512 currentNextHops.nextId()));
Saurav Das62ae6792017-05-15 15:34:25 -0700513 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700514 // even if one fails and others succeed, return false so ECMPspg not updated
515 return success;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700516 }
517
Saurav Dasfbe74572017-08-03 18:30:35 -0700518 /**
519 * Updates the DestinationSetNextObjectiveStore with any per-destination nexthops
520 * that are not already in the store for the given DestinationSet. Note that
521 * this method does not remove existing next hops for the destinations in the
522 * DestinationSet.
523 *
524 * @param ds the DestinationSet for which the next hops need to be updated
525 * @param newDstNextHops a map of per-destination next hops to update the
526 * destinationSet with
527 * @return true if successful in updating all next hops
528 */
529 private boolean updateNextHops(DestinationSet ds,
Saurav Das261c3002017-06-13 15:35:54 -0700530 Map<DeviceId, Set<DeviceId>> newDstNextHops) {
531 DestinationSetNextObjectiveStoreKey key =
532 new DestinationSetNextObjectiveStoreKey(deviceId, ds);
533 NextNeighbors currNext = dsNextObjStore.get(key);
534 Map<DeviceId, Set<DeviceId>> currDstNextHops = currNext.dstNextHops();
535
536 // add newDstNextHops to currDstNextHops for each dst
537 boolean success = true;
538 for (DeviceId dstSw : ds.getDestinationSwitches()) {
539 Set<DeviceId> currNhops = currDstNextHops.get(dstSw);
540 Set<DeviceId> newNhops = newDstNextHops.get(dstSw);
541 currNhops = (currNhops == null) ? Sets.newHashSet() : currNhops;
542 newNhops = (newNhops == null) ? Sets.newHashSet() : newNhops;
543 int edgeLabel = ds.getEdgeLabel(dstSw);
544 int nextId = currNext.nextId();
545
546 // new next hops should be added
547 boolean suc = updateAllPortsToNextHop(Sets.difference(newNhops, currNhops),
548 edgeLabel, nextId, false);
549 if (suc) {
550 currNhops.addAll(newNhops);
551 currDstNextHops.put(dstSw, currNhops); // this is only a local change
552 }
553 success &= suc;
554 }
555
556 if (success) {
557 // update global store
558 dsNextObjStore.put(key, new NextNeighbors(currDstNextHops,
559 currNext.nextId()));
560 log.debug("Updated device:{} ds:{} new next-hops: {}", deviceId, ds,
561 dsNextObjStore.get(key));
562 }
563 return success;
564 }
565
Saurav Dasfbe74572017-08-03 18:30:35 -0700566 /**
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700567 * Adds or removes buckets for all ports to a set of neighbor devices. Caller
568 * needs to ensure that the given neighbors are all next hops towards the
569 * same destination (represented by the given edgeLabel).
Saurav Dasfbe74572017-08-03 18:30:35 -0700570 *
571 * @param neighbors set of neighbor device ids
572 * @param edgeLabel MPLS label to use in buckets
573 * @param nextId the nextObjective to change
574 * @param revoke true if buckets need to be removed, false if they need to
575 * be added
576 * @return true if successful in adding or removing buckets for all ports
577 * to the neighbors
578 */
579 private boolean updateAllPortsToNextHop(Set<DeviceId> neighbors, int edgeLabel,
Saurav Das261c3002017-06-13 15:35:54 -0700580 int nextId, boolean revoke) {
Saurav Dasfbe74572017-08-03 18:30:35 -0700581 for (DeviceId neighbor : neighbors) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700582 MacAddress neighborMac;
Saurav Das261c3002017-06-13 15:35:54 -0700583 try {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700584 neighborMac = deviceConfig.getDeviceMac(neighbor);
Saurav Das261c3002017-06-13 15:35:54 -0700585 } catch (DeviceConfigNotFoundException e) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700586 log.warn(e.getMessage() + " Aborting updateAllPortsToNextHop"
587 + " for nextId:" + nextId);
Saurav Das261c3002017-06-13 15:35:54 -0700588 return false;
589 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700590 Collection<PortNumber> portsToNeighbor = devicePortMap.get(neighbor);
591 if (portsToNeighbor == null || portsToNeighbor.isEmpty()) {
Saurav Das261c3002017-06-13 15:35:54 -0700592 log.warn("No ports found in dev:{} for neighbor:{} .. cannot "
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700593 + "updateAllPortsToNextHop for nextId: {}",
Saurav Das261c3002017-06-13 15:35:54 -0700594 deviceId, neighbor, nextId);
595 return false;
596 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700597 List<PortLabel> pl = Lists.newArrayList();
598 portsToNeighbor.forEach(p -> pl.add(new PortLabel(p, edgeLabel)));
Saurav Das261c3002017-06-13 15:35:54 -0700599 if (revoke) {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700600 log.debug("updateAllPortsToNextHops in device {}: Removing Bucket(s) "
601 + "with Port/Label:{} to next object id {}",
602 deviceId, pl, nextId);
603 removeFromHashedNextObjective(pl, neighborMac, nextId);
Saurav Das261c3002017-06-13 15:35:54 -0700604 } else {
Saurav Dasfe0b05e2017-08-14 16:44:43 -0700605 log.debug("fixHashGroup in device {}: Adding Bucket(s) "
606 + "with Port/Label: {} to next object id {}",
607 deviceId, pl, nextId);
608 addToHashedNextObjective(pl, neighborMac, nextId);
Saurav Das261c3002017-06-13 15:35:54 -0700609 }
610 }
611 return true;
612 }
613
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700614 /**
Saurav Das3fb28272017-03-04 16:08:47 -0800615 * Adds or removes a port that has been configured with a vlan to a broadcast group
616 * for bridging. Should only be called by the master instance for this device.
Saurav Dasf0f592d2016-11-18 15:21:57 -0800617 *
618 * @param port the port on this device that needs to be added/removed to a bcast group
Saurav Das3fb28272017-03-04 16:08:47 -0800619 * @param vlanId the vlan id corresponding to the broadcast domain/group
620 * @param popVlan indicates if packets should be sent out untagged or not out
621 * of the port. If true, indicates an access (untagged) or native vlan
622 * configuration. If false, indicates a trunk (tagged) vlan config.
Saurav Dasf0f592d2016-11-18 15:21:57 -0800623 * @param portUp true if port is enabled, false if disabled
Saurav Dasf0f592d2016-11-18 15:21:57 -0800624 */
Saurav Das3fb28272017-03-04 16:08:47 -0800625 public void processEdgePort(PortNumber port, VlanId vlanId,
626 boolean popVlan, boolean portUp) {
Saurav Dasf0f592d2016-11-18 15:21:57 -0800627 //get the next id for the subnet and edit it.
Charles Chan10b0fb72017-02-02 16:20:42 -0800628 Integer nextId = getVlanNextObjectiveId(vlanId);
Saurav Dasf0f592d2016-11-18 15:21:57 -0800629 if (nextId == -1) {
630 if (portUp) {
631 log.debug("**Creating flooding group for first port enabled in"
Saurav Das2b6a00f2017-12-05 15:00:23 -0800632 + " vlan {} on dev {} port {}", vlanId, deviceId, port);
Charles Chan10b0fb72017-02-02 16:20:42 -0800633 createBcastGroupFromVlan(vlanId, Collections.singleton(port));
Saurav Dasf0f592d2016-11-18 15:21:57 -0800634 } else {
635 log.warn("Could not find flooding group for subnet {} on dev:{} when"
Charles Chan10b0fb72017-02-02 16:20:42 -0800636 + " removing port:{}", vlanId, deviceId, port);
Saurav Dasf0f592d2016-11-18 15:21:57 -0800637 }
638 return;
639 }
640
641 log.info("**port{} in device {}: {} Bucket with Port {} to"
642 + " next-id {}", (portUp) ? "UP" : "DOWN", deviceId,
643 (portUp) ? "Adding" : "Removing",
644 port, nextId);
645 // Create the bucket to be added or removed
646 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Saurav Das3fb28272017-03-04 16:08:47 -0800647 if (popVlan) {
648 tBuilder.popVlan();
649 }
Saurav Dasf0f592d2016-11-18 15:21:57 -0800650 tBuilder.setOutput(port);
651
Saurav Dasf0f592d2016-11-18 15:21:57 -0800652 TrafficSelector metadata =
Saurav Das3fb28272017-03-04 16:08:47 -0800653 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
Saurav Dasf0f592d2016-11-18 15:21:57 -0800654
655 NextObjective.Builder nextObjBuilder = DefaultNextObjective
656 .builder().withId(nextId)
657 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
658 .addTreatment(tBuilder.build())
659 .withMeta(metadata);
660
661 ObjectiveContext context = new DefaultObjectiveContext(
662 (objective) -> log.debug("port {} successfully {} NextObj {} on {}",
663 port, (portUp) ? "addedTo" : "removedFrom",
664 nextId, deviceId),
665 (objective, error) ->
666 log.warn("port {} failed to {} NextObj {} on {}: {}",
667 port, (portUp) ? "addTo" : "removeFrom",
668 nextId, deviceId, error));
669
670 NextObjective nextObj = (portUp) ? nextObjBuilder.addToExisting(context)
671 : nextObjBuilder.removeFromExisting(context);
672 log.debug("edgePort processed: Submited next objective {} in device {}",
673 nextId, deviceId);
674 flowObjectiveService.next(deviceId, nextObj);
675 }
676
677 /**
Saurav Das261c3002017-06-13 15:35:54 -0700678 * Returns the next objective of type hashed associated with the destination set.
679 * In addition, updates the existing next-objective if new route-route paths found
680 * have resulted in the addition of new next-hops to a particular destination.
681 * If there is no existing next objective for this destination set, this method
682 * would create a next objective and return the nextId. Optionally metadata can be
Saurav Das4c35fc42015-11-20 15:27:53 -0800683 * passed in for the creation of the next objective.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700684 *
Saurav Das261c3002017-06-13 15:35:54 -0700685 * @param ds destination set
686 * @param nextHops a map of per destination next hops
Saurav Das4c35fc42015-11-20 15:27:53 -0800687 * @param meta metadata passed into the creation of a Next Objective
Pier Ventre229fd0b2016-10-31 16:49:19 -0700688 * @param isBos if Bos is set
Saurav Das4c35fc42015-11-20 15:27:53 -0800689 * @return int if found or -1 if there are errors in the creation of the
690 * neighbor set.
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700691 */
Saurav Das261c3002017-06-13 15:35:54 -0700692 public int getNextObjectiveId(DestinationSet ds,
693 Map<DeviceId, Set<DeviceId>> nextHops,
694 TrafficSelector meta, boolean isBos) {
695 NextNeighbors next = dsNextObjStore.
696 get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
697 if (next == null) {
698 log.debug("getNextObjectiveId in device{}: Next objective id "
699 + "not found for {} ... creating", deviceId, ds);
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700700 log.trace("getNextObjectiveId: nsNextObjStore contents for device {}: {}",
701 deviceId,
Saurav Das261c3002017-06-13 15:35:54 -0700702 dsNextObjStore.entrySet()
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700703 .stream()
704 .filter((nsStoreEntry) ->
705 (nsStoreEntry.getKey().deviceId().equals(deviceId)))
706 .collect(Collectors.toList()));
Saurav Das261c3002017-06-13 15:35:54 -0700707
708 createGroupFromDestinationSet(ds, nextHops, meta, isBos);
709 next = dsNextObjStore.
710 get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
711 if (next == null) {
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700712 log.warn("getNextObjectiveId: unable to create next objective");
Saurav Das261c3002017-06-13 15:35:54 -0700713 // failure in creating group
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700714 return -1;
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700715 } else {
716 log.debug("getNextObjectiveId in device{}: Next objective id {} "
Saurav Das261c3002017-06-13 15:35:54 -0700717 + "created for {}", deviceId, next.nextId(), ds);
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700718 }
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700719 } else {
720 log.trace("getNextObjectiveId in device{}: Next objective id {} "
Saurav Das261c3002017-06-13 15:35:54 -0700721 + "found for {}", deviceId, next.nextId(), ds);
722 // should fix hash groups too if next-hops have changed
723 if (!next.dstNextHops().equals(nextHops)) {
724 log.debug("Nexthops have changed for dev:{} nextId:{} ..updating",
725 deviceId, next.nextId());
726 if (!updateNextHops(ds, nextHops)) {
727 // failure in updating group
728 return -1;
729 }
730 }
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700731 }
Saurav Das261c3002017-06-13 15:35:54 -0700732 return next.nextId();
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700733 }
734
sangho4a5c42a2015-05-20 22:16:38 -0700735 /**
Charles Chan10b0fb72017-02-02 16:20:42 -0800736 * Returns the next objective of type broadcast associated with the vlan,
Saurav Das2d94d312015-11-24 23:21:05 -0800737 * or -1 if no such objective exists. Note that this method does NOT create
738 * the next objective as a side-effect. It is expected that is objective is
Saurav Dasf0f592d2016-11-18 15:21:57 -0800739 * created at startup from network configuration. Typically this is used
740 * for L2 flooding within the subnet configured on the switch.
Charles Chan77277672015-10-20 16:24:19 -0700741 *
Charles Chan10b0fb72017-02-02 16:20:42 -0800742 * @param vlanId vlan id
Charles Chan77277672015-10-20 16:24:19 -0700743 * @return int if found or -1
744 */
Charles Chan10b0fb72017-02-02 16:20:42 -0800745 public int getVlanNextObjectiveId(VlanId vlanId) {
746 Integer nextId = vlanNextObjStore.
747 get(new VlanNextObjectiveStoreKey(deviceId, vlanId));
Charles Chanc6ad7752015-10-29 14:58:10 -0700748
749 return (nextId != null) ? nextId : -1;
Charles Chan77277672015-10-20 16:24:19 -0700750 }
751
752 /**
Saurav Das2d94d312015-11-24 23:21:05 -0800753 * Returns the next objective of type simple associated with the port on the
754 * device, given the treatment. Different treatments to the same port result
755 * in different next objectives. If no such objective exists, this method
Saurav Das2cb38292017-03-29 19:09:17 -0700756 * creates one (if requested) and returns the id. Optionally metadata can be passed in for
Saurav Dasf0f592d2016-11-18 15:21:57 -0800757 * the creation of the objective. Typically this is used for L2 and L3 forwarding
758 * to compute nodes and containers/VMs on the compute nodes directly attached
759 * to the switch.
Saurav Das2d94d312015-11-24 23:21:05 -0800760 *
761 * @param portNum the port number for the simple next objective
762 * @param treatment the actions to apply on the packets (should include outport)
763 * @param meta optional metadata passed into the creation of the next objective
Saurav Das2cb38292017-03-29 19:09:17 -0700764 * @param createIfMissing true if a next object should be created if not found
Saurav Das2d94d312015-11-24 23:21:05 -0800765 * @return int if found or created, -1 if there are errors during the
766 * creation of the next objective.
767 */
768 public int getPortNextObjectiveId(PortNumber portNum, TrafficTreatment treatment,
Saurav Das2cb38292017-03-29 19:09:17 -0700769 TrafficSelector meta, boolean createIfMissing) {
Charles Chanb7f75ac2016-01-11 18:28:54 -0800770 Integer nextId = portNextObjStore
Saurav Das368cf212017-03-15 15:15:14 -0700771 .get(new PortNextObjectiveStoreKey(deviceId, portNum, treatment, meta));
Saurav Das2cb38292017-03-29 19:09:17 -0700772 if (nextId != null) {
773 return nextId;
774 }
775 log.debug("getPortNextObjectiveId in device {}: Next objective id "
776 + "not found for port: {} .. {}", deviceId, portNum,
777 (createIfMissing) ? "creating" : "aborting");
778 if (!createIfMissing) {
779 return -1;
780 }
781 // create missing next objective
782 createGroupFromPort(portNum, treatment, meta);
783 nextId = portNextObjStore.get(new PortNextObjectiveStoreKey(deviceId, portNum,
784 treatment, meta));
Saurav Das2d94d312015-11-24 23:21:05 -0800785 if (nextId == null) {
Saurav Das2cb38292017-03-29 19:09:17 -0700786 log.warn("getPortNextObjectiveId: unable to create next obj"
787 + "for dev:{} port:{}", deviceId, portNum);
788 return -1;
Charles Chanb7f75ac2016-01-11 18:28:54 -0800789 }
790 return nextId;
791 }
792
793 /**
sangho4a5c42a2015-05-20 22:16:38 -0700794 * Checks if the next objective ID (group) for the neighbor set exists or not.
795 *
796 * @param ns neighbor set to check
797 * @return true if it exists, false otherwise
798 */
Saurav Das261c3002017-06-13 15:35:54 -0700799 public boolean hasNextObjectiveId(DestinationSet ns) {
800 NextNeighbors nextHops = dsNextObjStore.
801 get(new DestinationSetNextObjectiveStoreKey(deviceId, ns));
802 if (nextHops == null) {
sangho4a5c42a2015-05-20 22:16:38 -0700803 return false;
804 }
805
806 return true;
807 }
808
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700809 private void populateNeighborMaps() {
810 Set<Link> outgoingLinks = linkService.getDeviceEgressLinks(deviceId);
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700811 for (Link link : outgoingLinks) {
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700812 if (link.type() != Link.Type.DIRECT) {
813 continue;
814 }
815 addNeighborAtPort(link.dst().deviceId(), link.src().port());
816 }
817 }
818
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700819 protected void addNeighborAtPort(DeviceId neighborId,
820 PortNumber portToNeighbor) {
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700821 // Update DeviceToPort database
822 log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
823 deviceId, neighborId, portToNeighbor);
Saurav Das4c35fc42015-11-20 15:27:53 -0800824 Set<PortNumber> ports = Collections
825 .newSetFromMap(new ConcurrentHashMap<PortNumber, Boolean>());
826 ports.add(portToNeighbor);
827 Set<PortNumber> portnums = devicePortMap.putIfAbsent(neighborId, ports);
828 if (portnums != null) {
829 portnums.add(portToNeighbor);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700830 }
831
832 // Update portToDevice database
Saurav Das4c35fc42015-11-20 15:27:53 -0800833 DeviceId prev = portDeviceMap.putIfAbsent(portToNeighbor, neighborId);
834 if (prev != null) {
Saurav Das62ae6792017-05-15 15:34:25 -0700835 log.debug("Device: {} port: {} already has neighbor: {} ",
836 deviceId, portToNeighbor, prev, neighborId);
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700837 }
838 }
839
sangho27462c62015-05-14 00:39:53 -0700840 /**
Saurav Das261c3002017-06-13 15:35:54 -0700841 * Creates a NextObjective for a hash group in this device from a given
842 * DestinationSet.
sangho27462c62015-05-14 00:39:53 -0700843 *
Saurav Das261c3002017-06-13 15:35:54 -0700844 * @param ds the DestinationSet
845 * @param neighbors a map for each destination and its next-hops
Saurav Das4c35fc42015-11-20 15:27:53 -0800846 * @param meta metadata passed into the creation of a Next Objective
Pier Ventre229fd0b2016-10-31 16:49:19 -0700847 * @param isBos if BoS is set
sangho27462c62015-05-14 00:39:53 -0700848 */
Saurav Das261c3002017-06-13 15:35:54 -0700849 public void createGroupFromDestinationSet(DestinationSet ds,
850 Map<DeviceId, Set<DeviceId>> neighbors,
851 TrafficSelector meta,
852 boolean isBos) {
853 int nextId = flowObjectiveService.allocateNextId();
854 NextObjective.Type type = NextObjective.Type.HASHED;
855 if (neighbors == null || neighbors.isEmpty()) {
856 log.warn("createGroupsFromDestinationSet: needs at least one neighbor"
857 + "to create group in dev:{} for ds: {} with next-hops {}",
858 deviceId, ds, neighbors);
859 return;
860 }
861 // If Bos == False and MPLS-ECMP == false, we have
862 // to use simple group and we will pick a single neighbor for a single dest.
863 if (!isBos && !srManager.getMplsEcmp()) {
864 type = NextObjective.Type.SIMPLE;
865 }
866
867 NextObjective.Builder nextObjBuilder = DefaultNextObjective
868 .builder()
869 .withId(nextId)
870 .withType(type)
871 .fromApp(appId);
872 if (meta != null) {
873 nextObjBuilder.withMeta(meta);
874 }
875
876 // create treatment buckets for each neighbor for each dst Device
877 // except in the special case where we only want to pick a single
878 // neighbor for a simple group
879 boolean foundSingleNeighbor = false;
880 boolean treatmentAdded = false;
881 Map<DeviceId, Set<DeviceId>> dstNextHops = new ConcurrentHashMap<>();
882 for (DeviceId dst : ds.getDestinationSwitches()) {
883 Set<DeviceId> nextHops = neighbors.get(dst);
884 if (nextHops == null || nextHops.isEmpty()) {
885 continue;
Pier Ventre229fd0b2016-10-31 16:49:19 -0700886 }
Saurav Das261c3002017-06-13 15:35:54 -0700887
888 if (foundSingleNeighbor) {
889 break;
890 }
891
892 for (DeviceId neighborId : nextHops) {
Saurav Das4c35fc42015-11-20 15:27:53 -0800893 if (devicePortMap.get(neighborId) == null) {
894 log.warn("Neighbor {} is not in the port map yet for dev:{}",
895 neighborId, deviceId);
sangho2165d222015-05-01 09:38:25 -0700896 return;
Jon Hall31d84782017-01-18 20:15:44 -0800897 } else if (devicePortMap.get(neighborId).isEmpty()) {
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700898 log.warn("There are no ports for "
Saurav Das4c35fc42015-11-20 15:27:53 -0800899 + "the Device {} in the port map yet", neighborId);
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700900 return;
sangho2165d222015-05-01 09:38:25 -0700901 }
902
Saurav Das4c35fc42015-11-20 15:27:53 -0800903 MacAddress neighborMac;
Charles Chan319d1a22015-11-03 10:42:14 -0800904 try {
Saurav Das4c35fc42015-11-20 15:27:53 -0800905 neighborMac = deviceConfig.getDeviceMac(neighborId);
Charles Chan319d1a22015-11-03 10:42:14 -0800906 } catch (DeviceConfigNotFoundException e) {
Saurav Das261c3002017-06-13 15:35:54 -0700907 log.warn(e.getMessage() + " Aborting createGroupsFromDestinationset.");
Charles Chan319d1a22015-11-03 10:42:14 -0800908 return;
909 }
Saurav Das261c3002017-06-13 15:35:54 -0700910 // For each port to the neighbor, we create a new treatment
Pier Ventre229fd0b2016-10-31 16:49:19 -0700911 Set<PortNumber> neighborPorts = devicePortMap.get(neighborId);
912 // In this case we are using a SIMPLE group. We randomly pick a port
913 if (!isBos && !srManager.getMplsEcmp()) {
914 int size = devicePortMap.get(neighborId).size();
915 int index = RandomUtils.nextInt(0, size);
916 neighborPorts = Collections.singleton(
Saurav Das261c3002017-06-13 15:35:54 -0700917 Iterables.get(devicePortMap.get(neighborId),
918 index));
919 foundSingleNeighbor = true;
Pier Ventre229fd0b2016-10-31 16:49:19 -0700920 }
Andreas Pantelopoulos5e7be3d2017-10-23 12:18:25 -0700921
Pier Ventre229fd0b2016-10-31 16:49:19 -0700922 for (PortNumber sp : neighborPorts) {
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700923 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
924 .builder();
Saurav Das261c3002017-06-13 15:35:54 -0700925 tBuilder.setEthDst(neighborMac).setEthSrc(nodeMacAddr);
926 int edgeLabel = ds.getEdgeLabel(dst);
927 if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
Charles Chanf4586112015-11-09 16:37:23 -0800928 tBuilder.pushMpls()
Saurav Das261c3002017-06-13 15:35:54 -0700929 .copyTtlOut()
930 .setMpls(MplsLabel.mplsLabel(edgeLabel));
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700931 }
Saurav Das4c35fc42015-11-20 15:27:53 -0800932 tBuilder.setOutput(sp);
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700933 nextObjBuilder.addTreatment(tBuilder.build());
Saurav Das261c3002017-06-13 15:35:54 -0700934 treatmentAdded = true;
935 //update store
936 Set<DeviceId> existingNeighbors = dstNextHops.get(dst);
937 if (existingNeighbors == null) {
938 existingNeighbors = new HashSet<>();
939 }
940 existingNeighbors.add(neighborId);
941 dstNextHops.put(dst, existingNeighbors);
942 log.debug("creating treatment for port/label {}/{} in next:{}",
943 sp, edgeLabel, nextId);
944 }
945
946 if (foundSingleNeighbor) {
947 break;
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700948 }
949 }
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700950 }
Saurav Das261c3002017-06-13 15:35:54 -0700951
952 if (!treatmentAdded) {
953 log.warn("Could not createGroup from DestinationSet {} without any"
954 + "next hops {}", ds, neighbors);
955 return;
956 }
957 ObjectiveContext context = new DefaultObjectiveContext(
958 (objective) ->
959 log.debug("createGroupsFromDestinationSet installed "
960 + "NextObj {} on {}", nextId, deviceId),
961 (objective, error) ->
962 log.warn("createGroupsFromDestinationSet failed to install"
963 + " NextObj {} on {}: {}", nextId, deviceId, error)
964 );
965 NextObjective nextObj = nextObjBuilder.add(context);
966 log.debug(".. createGroupsFromDestinationSet: Submitted "
967 + "next objective {} in device {}", nextId, deviceId);
968 flowObjectiveService.next(deviceId, nextObj);
969 //update store
970 dsNextObjStore.put(new DestinationSetNextObjectiveStoreKey(deviceId, ds),
971 new NextNeighbors(dstNextHops, nextId));
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700972 }
973
Saurav Das2d94d312015-11-24 23:21:05 -0800974 /**
Saurav Dasf0f592d2016-11-18 15:21:57 -0800975 * Creates broadcast groups for all ports in the same subnet for
976 * all configured subnets.
Saurav Das2d94d312015-11-24 23:21:05 -0800977 */
Charles Chan10b0fb72017-02-02 16:20:42 -0800978 public void createGroupsFromVlanConfig() {
Charles Chan90772a72017-02-08 15:52:08 -0800979 srManager.getVlanPortMap(deviceId).asMap().forEach((vlanId, ports) -> {
Charles Chan10b0fb72017-02-02 16:20:42 -0800980 createBcastGroupFromVlan(vlanId, ports);
Pier Ventreb6a7f342016-11-26 21:05:22 -0800981 });
Saurav Dasf0f592d2016-11-18 15:21:57 -0800982 }
Charles Chanc6ad7752015-10-29 14:58:10 -0700983
Saurav Dasf0f592d2016-11-18 15:21:57 -0800984 /**
Charles Chan10b0fb72017-02-02 16:20:42 -0800985 * Creates a single broadcast group from a given vlan id and list of ports.
Saurav Dasf0f592d2016-11-18 15:21:57 -0800986 *
Charles Chan10b0fb72017-02-02 16:20:42 -0800987 * @param vlanId vlan id
Saurav Dasf0f592d2016-11-18 15:21:57 -0800988 * @param ports list of ports in the subnet
989 */
Charles Chan90772a72017-02-08 15:52:08 -0800990 public void createBcastGroupFromVlan(VlanId vlanId, Collection<PortNumber> ports) {
Charles Chan10b0fb72017-02-02 16:20:42 -0800991 VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
Charles Chanc6ad7752015-10-29 14:58:10 -0700992
Charles Chan10b0fb72017-02-02 16:20:42 -0800993 if (vlanNextObjStore.containsKey(key)) {
Saurav Dasf0f592d2016-11-18 15:21:57 -0800994 log.debug("Broadcast group for device {} and subnet {} exists",
Charles Chan10b0fb72017-02-02 16:20:42 -0800995 deviceId, vlanId);
Saurav Dasf0f592d2016-11-18 15:21:57 -0800996 return;
997 }
Charles Chande6655c2015-12-23 00:15:11 -0800998
Saurav Dasf0f592d2016-11-18 15:21:57 -0800999 TrafficSelector metadata =
Charles Chan10b0fb72017-02-02 16:20:42 -08001000 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
Charles Chan77277672015-10-20 16:24:19 -07001001
Saurav Dasf0f592d2016-11-18 15:21:57 -08001002 int nextId = flowObjectiveService.allocateNextId();
Charles Chan77277672015-10-20 16:24:19 -07001003
Saurav Dasf0f592d2016-11-18 15:21:57 -08001004 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1005 .builder().withId(nextId)
1006 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1007 .withMeta(metadata);
Charles Chan77277672015-10-20 16:24:19 -07001008
Saurav Dasf0f592d2016-11-18 15:21:57 -08001009 ports.forEach(port -> {
1010 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Charles Chan90772a72017-02-08 15:52:08 -08001011 if (toPopVlan(port, vlanId)) {
1012 tBuilder.popVlan();
1013 }
Saurav Dasf0f592d2016-11-18 15:21:57 -08001014 tBuilder.setOutput(port);
1015 nextObjBuilder.addTreatment(tBuilder.build());
Charles Chan77277672015-10-20 16:24:19 -07001016 });
Saurav Dasf0f592d2016-11-18 15:21:57 -08001017
Saurav Das2cb38292017-03-29 19:09:17 -07001018 ObjectiveContext context = new DefaultObjectiveContext(
1019 (objective) ->
1020 log.debug("createBroadcastGroupFromVlan installed "
1021 + "NextObj {} on {}", nextId, deviceId),
1022 (objective, error) ->
1023 log.warn("createBroadcastGroupFromVlan failed to install"
1024 + " NextObj {} on {}: {}", nextId, deviceId, error)
1025 );
1026 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Dasf0f592d2016-11-18 15:21:57 -08001027 flowObjectiveService.next(deviceId, nextObj);
Saurav Das2b6a00f2017-12-05 15:00:23 -08001028 log.debug("createBcastGroupFromVlan: Submitted next objective {} "
1029 + "for vlan: {} in device {}", nextId, vlanId, deviceId);
Saurav Dasf0f592d2016-11-18 15:21:57 -08001030
Charles Chan10b0fb72017-02-02 16:20:42 -08001031 vlanNextObjStore.put(key, nextId);
Charles Chan77277672015-10-20 16:24:19 -07001032 }
1033
Charles Chanb7f75ac2016-01-11 18:28:54 -08001034 /**
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001035 * Removes a single broadcast group from a given vlan id.
1036 * The group should be empty.
1037 * @param deviceId device Id to remove the group
1038 * @param portNum port number related to the group
1039 * @param vlanId vlan id of the broadcast group to remove
1040 * @param popVlan true if the TrafficTreatment involves pop vlan tag action
1041 */
1042 public void removeBcastGroupFromVlan(DeviceId deviceId, PortNumber portNum,
1043 VlanId vlanId, boolean popVlan) {
1044 VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
1045
1046 if (!vlanNextObjStore.containsKey(key)) {
1047 log.debug("Broadcast group for device {} and subnet {} does not exist",
1048 deviceId, vlanId);
1049 return;
1050 }
1051
1052 TrafficSelector metadata =
1053 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
1054
1055 int nextId = vlanNextObjStore.get(key);
1056
1057 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1058 .builder().withId(nextId)
1059 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1060 .withMeta(metadata);
1061
1062 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1063 if (popVlan) {
1064 tBuilder.popVlan();
1065 }
1066 tBuilder.setOutput(portNum);
1067 nextObjBuilder.addTreatment(tBuilder.build());
1068
1069 ObjectiveContext context = new DefaultObjectiveContext(
1070 (objective) ->
1071 log.debug("removeBroadcastGroupFromVlan removed "
1072 + "NextObj {} on {}", nextId, deviceId),
1073 (objective, error) ->
1074 log.warn("removeBroadcastGroupFromVlan failed to remove "
1075 + " NextObj {} on {}: {}", nextId, deviceId, error)
1076 );
1077 NextObjective nextObj = nextObjBuilder.remove(context);
1078 flowObjectiveService.next(deviceId, nextObj);
1079 log.debug("removeBcastGroupFromVlan: Submited next objective {} in device {}",
1080 nextId, deviceId);
1081
1082 vlanNextObjStore.remove(key, nextId);
1083 }
1084
1085 /**
Charles Chan90772a72017-02-08 15:52:08 -08001086 * Determine if we should pop given vlan before sending packets to the given port.
1087 *
1088 * @param portNumber port number
1089 * @param vlanId vlan id
1090 * @return true if the vlan id is not contained in any vlanTagged config
1091 */
1092 private boolean toPopVlan(PortNumber portNumber, VlanId vlanId) {
Saurav Das261c3002017-06-13 15:35:54 -07001093 return srManager.interfaceService
1094 .getInterfacesByPort(new ConnectPoint(deviceId, portNumber))
Charles Chan90772a72017-02-08 15:52:08 -08001095 .stream().noneMatch(intf -> intf.vlanTagged().contains(vlanId));
1096 }
1097
1098 /**
Saurav Das2d94d312015-11-24 23:21:05 -08001099 * Create simple next objective for a single port. The treatments can include
1100 * all outgoing actions that need to happen on the packet.
1101 *
1102 * @param portNum the outgoing port on the device
1103 * @param treatment the actions to apply on the packets (should include outport)
1104 * @param meta optional data to pass to the driver
1105 */
1106 public void createGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
1107 TrafficSelector meta) {
1108 int nextId = flowObjectiveService.allocateNextId();
1109 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
Saurav Das368cf212017-03-15 15:15:14 -07001110 deviceId, portNum, treatment, meta);
Saurav Das2d94d312015-11-24 23:21:05 -08001111
1112 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1113 .builder().withId(nextId)
1114 .withType(NextObjective.Type.SIMPLE)
1115 .addTreatment(treatment)
1116 .fromApp(appId)
1117 .withMeta(meta);
1118
Saurav Das2cb38292017-03-29 19:09:17 -07001119 ObjectiveContext context = new DefaultObjectiveContext(
1120 (objective) ->
1121 log.debug("createGroupFromPort installed "
1122 + "NextObj {} on {}", nextId, deviceId),
1123 (objective, error) ->
1124 log.warn("createGroupFromPort failed to install"
1125 + " NextObj {} on {}: {}", nextId, deviceId, error)
1126 );
1127 NextObjective nextObj = nextObjBuilder.add(context);
Saurav Das2d94d312015-11-24 23:21:05 -08001128 flowObjectiveService.next(deviceId, nextObj);
1129 log.debug("createGroupFromPort: Submited next objective {} in device {} "
1130 + "for port {}", nextId, deviceId, portNum);
1131
1132 portNextObjStore.put(key, nextId);
1133 }
1134
sangho27462c62015-05-14 00:39:53 -07001135 /**
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001136 * Removes simple next objective for a single port.
1137 *
1138 * @param deviceId device id that has the port to deal with
1139 * @param portNum the outgoing port on the device
1140 * @param vlanId vlan id associated with the port
1141 * @param popVlan true if POP_VLAN action is applied on the packets, false otherwise
1142 */
1143 public void removePortNextObjective(DeviceId deviceId, PortNumber portNum, VlanId vlanId, boolean popVlan) {
1144 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
1145 mbuilder.matchVlanId(vlanId);
1146
1147 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
1148 tbuilder.immediate().setOutput(portNum);
1149 if (popVlan) {
1150 tbuilder.immediate().popVlan();
1151 }
1152
1153 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, portNum,
1154 tbuilder.build(), mbuilder.build(), false);
1155
1156 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
1157 deviceId, portNum, tbuilder.build(), mbuilder.build());
1158 if (portNextObjId != -1 && portNextObjStore.containsKey(key)) {
1159 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1160 .builder().withId(portNextObjId)
1161 .withType(NextObjective.Type.SIMPLE).fromApp(appId);
1162 ObjectiveContext context = new DefaultObjectiveContext(
1163 (objective) -> log.debug("removePortNextObjective removes NextObj {} on {}",
1164 portNextObjId, deviceId),
1165 (objective, error) ->
1166 log.warn("removePortNextObjective failed to remove NextObj {} on {}: {}",
1167 portNextObjId, deviceId, error));
1168 NextObjective nextObjective = nextObjBuilder.remove(context);
1169 log.info("**removePortNextObjective: Submitted "
1170 + "next objective {} in device {}",
1171 portNextObjId, deviceId);
1172 flowObjectiveService.next(deviceId, nextObjective);
1173
1174 portNextObjStore.remove(key);
1175 }
1176 }
1177 /**
sangho27462c62015-05-14 00:39:53 -07001178 * Removes groups for the next objective ID given.
1179 *
1180 * @param objectiveId next objective ID to remove
1181 * @return true if succeeds, false otherwise
1182 */
1183 public boolean removeGroup(int objectiveId) {
Saurav Das261c3002017-06-13 15:35:54 -07001184 for (Map.Entry<DestinationSetNextObjectiveStoreKey, NextNeighbors> e :
1185 dsNextObjStore.entrySet()) {
1186 if (e.getValue().nextId() != objectiveId) {
1187 continue;
1188 }
sangho27462c62015-05-14 00:39:53 -07001189 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1190 .builder().withId(objectiveId)
1191 .withType(NextObjective.Type.HASHED).fromApp(appId);
Charles Chana4ee4f92016-04-23 14:48:16 -07001192 ObjectiveContext context = new DefaultObjectiveContext(
1193 (objective) -> log.debug("RemoveGroup removes NextObj {} on {}",
1194 objectiveId, deviceId),
1195 (objective, error) ->
1196 log.warn("RemoveGroup failed to remove NextObj {} on {}: {}",
1197 objectiveId, deviceId, error));
1198 NextObjective nextObjective = nextObjBuilder.remove(context);
Saurav Das4c35fc42015-11-20 15:27:53 -08001199 log.info("**removeGroup: Submited "
1200 + "next objective {} in device {}",
1201 objectiveId, deviceId);
sangho27462c62015-05-14 00:39:53 -07001202 flowObjectiveService.next(deviceId, nextObjective);
1203
Saurav Das261c3002017-06-13 15:35:54 -07001204 dsNextObjStore.remove(e.getKey());
sangho4a5c42a2015-05-20 22:16:38 -07001205 return true;
sangho27462c62015-05-14 00:39:53 -07001206 }
1207
1208 return false;
1209 }
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001210 /**
1211 * Remove simple next objective for a single port. The treatments can include
1212 * all outgoing actions that need to happen on the packet.
1213 *
1214 * @param portNum the outgoing port on the device
1215 * @param treatment the actions applied on the packets (should include outport)
1216 * @param meta optional data to pass to the driver
1217 */
1218 public void removeGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
1219 TrafficSelector meta) {
1220 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
1221 deviceId, portNum, treatment, meta);
1222 Integer nextId = portNextObjStore.get(key);
1223
1224 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1225 .builder().withId(nextId)
1226 .withType(NextObjective.Type.SIMPLE)
1227 .addTreatment(treatment)
1228 .fromApp(appId)
1229 .withMeta(meta);
1230
1231 ObjectiveContext context = new DefaultObjectiveContext(
1232 (objective) ->
1233 log.info("removeGroupFromPort installed "
1234 + "NextObj {} on {}", nextId, deviceId),
1235 (objective, error) ->
1236 log.warn("removeGroupFromPort failed to install"
1237 + " NextObj {} on {}: {}", nextId, deviceId, error)
1238 );
1239 NextObjective nextObj = nextObjBuilder.remove(context);
1240 flowObjectiveService.next(deviceId, nextObj);
1241 log.info("removeGroupFromPort: Submitted next objective {} in device {} "
1242 + "for port {}", nextId, deviceId, portNum);
1243
1244 portNextObjStore.remove(key);
1245 }
Srikanth Vavilapalli8c83f1d2015-05-22 13:47:31 -07001246
Charles Chanb7f75ac2016-01-11 18:28:54 -08001247 /**
1248 * Removes all groups from all next objective stores.
1249 */
Saurav Das261c3002017-06-13 15:35:54 -07001250 /*public void removeAllGroups() {
1251 for (Map.Entry<NeighborSetNextObjectiveStoreKey, NextNeighbors> entry:
Saurav Das62af8802015-12-04 10:52:59 -08001252 nsNextObjStore.entrySet()) {
Saurav Das261c3002017-06-13 15:35:54 -07001253 removeGroup(entry.getValue().nextId());
Saurav Das62af8802015-12-04 10:52:59 -08001254 }
1255 for (Map.Entry<PortNextObjectiveStoreKey, Integer> entry:
1256 portNextObjStore.entrySet()) {
1257 removeGroup(entry.getValue());
1258 }
Charles Chan10b0fb72017-02-02 16:20:42 -08001259 for (Map.Entry<VlanNextObjectiveStoreKey, Integer> entry:
1260 vlanNextObjStore.entrySet()) {
Saurav Das62af8802015-12-04 10:52:59 -08001261 removeGroup(entry.getValue());
1262 }
Saurav Das261c3002017-06-13 15:35:54 -07001263 }*/ //XXX revisit
1264
Saurav Dasfbe74572017-08-03 18:30:35 -07001265 /**
1266 * Triggers a one time bucket verification operation on all hash groups
1267 * on this device.
1268 */
1269 public void triggerBucketCorrector() {
1270 BucketCorrector bc = new BucketCorrector();
1271 bc.run();
1272 }
1273
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001274 public void updateGroupFromVlanConfiguration(PortNumber portNumber, Collection<VlanId> vlanIds,
1275 int nextId, boolean install) {
1276 vlanIds.forEach(vlanId -> updateGroupFromVlanInternal(vlanId, portNumber, nextId, install));
1277 }
1278
1279 private void updateGroupFromVlanInternal(VlanId vlanId, PortNumber portNum, int nextId, boolean install) {
1280 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1281 if (toPopVlan(portNum, vlanId)) {
1282 tBuilder.popVlan();
1283 }
1284 tBuilder.setOutput(portNum);
1285
1286 TrafficSelector metadata =
1287 DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
1288
1289 NextObjective.Builder nextObjBuilder = DefaultNextObjective
1290 .builder().withId(nextId)
1291 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
1292 .addTreatment(tBuilder.build())
1293 .withMeta(metadata);
1294
1295 ObjectiveContext context = new DefaultObjectiveContext(
1296 (objective) -> log.debug("port {} successfully removedFrom NextObj {} on {}",
1297 portNum, nextId, deviceId),
1298 (objective, error) ->
1299 log.warn("port {} failed to removedFrom NextObj {} on {}: {}",
1300 portNum, nextId, deviceId, error));
1301
1302 if (install) {
1303 flowObjectiveService.next(deviceId, nextObjBuilder.addToExisting(context));
1304 } else {
1305 flowObjectiveService.next(deviceId, nextObjBuilder.removeFromExisting(context));
1306 }
1307 }
Saurav Das8a3022d2017-05-05 17:01:08 -07001308
1309 /**
Saurav Dasfe0b05e2017-08-14 16:44:43 -07001310 * Performs bucket verification operation for all hash groups in this device.
1311 * Checks RouteHandler to ensure that routing is stable before attempting
1312 * verification. Verification involves creating a nextObjective with
1313 * operation VERIFY for existing next objectives in the store, and passing
1314 * it to the driver. It is the driver that actually performs the verification
1315 * by adding or removing buckets to match the verification next objective
1316 * created here.
Saurav Das8a3022d2017-05-05 17:01:08 -07001317 */
Saurav Dasfbe74572017-08-03 18:30:35 -07001318 protected final class BucketCorrector implements Runnable {
1319 Integer nextId;
Saurav Das8a3022d2017-05-05 17:01:08 -07001320
Saurav Dasfbe74572017-08-03 18:30:35 -07001321 BucketCorrector() {
1322 this.nextId = null;
1323 }
1324
1325 BucketCorrector(Integer nextId) {
1326 this.nextId = nextId;
Saurav Das8a3022d2017-05-05 17:01:08 -07001327 }
1328
1329 @Override
1330 public void run() {
Saurav Dasfbe74572017-08-03 18:30:35 -07001331 if (!srManager.mastershipService.isLocalMaster(deviceId)) {
1332 return;
Saurav Das8a3022d2017-05-05 17:01:08 -07001333 }
Saurav Dasfbe74572017-08-03 18:30:35 -07001334 DefaultRoutingHandler rh = srManager.getRoutingHandler();
1335 if (rh == null) {
1336 return;
1337 }
1338 if (!rh.isRoutingStable()) {
1339 return;
1340 }
1341 rh.acquireRoutingLock();
1342 try {
Saurav Dasfe0b05e2017-08-14 16:44:43 -07001343 log.trace("running bucket corrector for dev: {}", deviceId);
Saurav Dasfbe74572017-08-03 18:30:35 -07001344 Set<DestinationSetNextObjectiveStoreKey> dsKeySet = dsNextObjStore.entrySet()
1345 .stream()
1346 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
1347 .map(entry -> entry.getKey())
1348 .collect(Collectors.toSet());
1349 for (DestinationSetNextObjectiveStoreKey dsKey : dsKeySet) {
1350 NextNeighbors next = dsNextObjStore.get(dsKey);
1351 if (next == null) {
1352 continue;
1353 }
1354 int nid = next.nextId();
1355 if (nextId != null && nextId != nid) {
1356 continue;
1357 }
Saurav Dasfe0b05e2017-08-14 16:44:43 -07001358 log.trace("bkt-corr: dsNextObjStore for device {}: {}",
Saurav Dasfbe74572017-08-03 18:30:35 -07001359 deviceId, dsKey, next);
1360 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
1361 metabuilder.matchVlanId(INTERNAL_VLAN);
1362 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
1363 .withId(nid)
1364 .withType(NextObjective.Type.HASHED)
1365 .withMeta(metabuilder.build())
1366 .fromApp(appId);
1367
1368 next.dstNextHops().forEach((dstDev, nextHops) -> {
1369 int edgeLabel = dsKey.destinationSet().getEdgeLabel(dstDev);
1370 nextHops.forEach(neighbor -> {
1371 MacAddress neighborMac;
1372 try {
1373 neighborMac = deviceConfig.getDeviceMac(neighbor);
1374 } catch (DeviceConfigNotFoundException e) {
1375 log.warn(e.getMessage() + " Aborting neighbor"
1376 + neighbor);
1377 return;
1378 }
1379 devicePortMap.get(neighbor).forEach(port -> {
Saurav Dasfe0b05e2017-08-14 16:44:43 -07001380 log.trace("verify in device {} nextId {}: bucket with"
Saurav Dasfbe74572017-08-03 18:30:35 -07001381 + " port/label {}/{} to dst {} via {}",
1382 deviceId, nid, port, edgeLabel,
1383 dstDev, neighbor);
1384 nextObjBuilder.addTreatment(treatmentBuilder(port,
1385 neighborMac, edgeLabel));
1386 });
1387 });
1388 });
1389
1390 NextObjective nextObjective = nextObjBuilder.verify();
1391 flowObjectiveService.next(deviceId, nextObjective);
1392 }
1393 } finally {
1394 rh.releaseRoutingLock();
1395 }
1396
1397 }
1398
1399 TrafficTreatment treatmentBuilder(PortNumber outport, MacAddress dstMac,
1400 int edgeLabel) {
1401 TrafficTreatment.Builder tBuilder =
1402 DefaultTrafficTreatment.builder();
1403 tBuilder.setOutput(outport)
1404 .setEthDst(dstMac)
1405 .setEthSrc(nodeMacAddr);
1406 if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
1407 tBuilder.pushMpls()
1408 .copyTtlOut()
1409 .setMpls(MplsLabel.mplsLabel(edgeLabel));
1410 }
1411 return tBuilder.build();
Saurav Das8a3022d2017-05-05 17:01:08 -07001412 }
1413 }
1414
Saurav Dasfbe74572017-08-03 18:30:35 -07001415}