blob: c378541faf692b78a4a79001c2a51df4e7a0435d [file] [log] [blame]
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Srikanth Vavilapalli4db76e32015-04-07 15:12:32 -070016package org.onosproject.segmentrouting.grouphandler;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080017
18import static com.google.common.base.Preconditions.checkNotNull;
19import static org.slf4j.LoggerFactory.getLogger;
20
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070021import java.net.URI;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080022import java.util.ArrayList;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070023import java.util.Collections;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080024import java.util.HashSet;
25import java.util.List;
sangho1e575652015-05-14 00:39:53 -070026import java.util.Map;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080027import java.util.Set;
Saurav Das8a0732e2015-11-20 15:27:53 -080028import java.util.concurrent.ConcurrentHashMap;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070029import java.util.stream.Collectors;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080030
Charles Chanc42e84e2015-10-20 16:24:19 -070031import org.onlab.packet.Ip4Prefix;
32import org.onlab.packet.IpPrefix;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080033import org.onlab.packet.MacAddress;
sangho32a59322015-02-17 12:07:41 -080034import org.onlab.packet.MplsLabel;
Saurav Das423fe2b2015-12-04 10:52:59 -080035import org.onlab.packet.VlanId;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070036import org.onlab.util.KryoNamespace;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080037import org.onosproject.core.ApplicationId;
38import org.onosproject.net.DeviceId;
39import org.onosproject.net.Link;
40import org.onosproject.net.PortNumber;
Saurav Das423fe2b2015-12-04 10:52:59 -080041import org.onosproject.net.flow.DefaultTrafficSelector;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080042import org.onosproject.net.flow.DefaultTrafficTreatment;
Saurav Das8a0732e2015-11-20 15:27:53 -080043import org.onosproject.net.flow.TrafficSelector;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080044import org.onosproject.net.flow.TrafficTreatment;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070045import org.onosproject.net.flowobjective.DefaultNextObjective;
Charles Chan216e3c82016-04-23 14:48:16 -070046import org.onosproject.net.flowobjective.DefaultObjectiveContext;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070047import org.onosproject.net.flowobjective.FlowObjectiveService;
48import org.onosproject.net.flowobjective.NextObjective;
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -070049import org.onosproject.net.flowobjective.ObjectiveContext;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080050import org.onosproject.net.link.LinkService;
Saurav Das423fe2b2015-12-04 10:52:59 -080051import org.onosproject.segmentrouting.SegmentRoutingManager;
Charles Chan0b4e6182015-11-03 10:42:14 -080052import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
53import org.onosproject.segmentrouting.config.DeviceProperties;
Charles Chand2990362016-04-18 13:44:03 -070054import org.onosproject.segmentrouting.storekey.NeighborSetNextObjectiveStoreKey;
55import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
56import org.onosproject.segmentrouting.storekey.SubnetNextObjectiveStoreKey;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070057import org.onosproject.store.service.EventuallyConsistentMap;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080058import org.slf4j.Logger;
59
60/**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070061 * Default ECMP group handler creation module. This component creates a set of
62 * ECMP groups for every neighbor that this device is connected to based on
63 * whether the current device is an edge device or a transit device.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080064 */
65public class DefaultGroupHandler {
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -070066 protected static final Logger log = getLogger(DefaultGroupHandler.class);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080067
68 protected final DeviceId deviceId;
69 protected final ApplicationId appId;
70 protected final DeviceProperties deviceConfig;
71 protected final List<Integer> allSegmentIds;
Charles Chan0b4e6182015-11-03 10:42:14 -080072 protected int nodeSegmentId = -1;
73 protected boolean isEdgeRouter = false;
74 protected MacAddress nodeMacAddr = null;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080075 protected LinkService linkService;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070076 protected FlowObjectiveService flowObjectiveService;
Saurav Das423fe2b2015-12-04 10:52:59 -080077 // local store for neighbor-device-ids and the set of ports on this device
78 // that connect to the same neighbor
Saurav Das8a0732e2015-11-20 15:27:53 -080079 protected ConcurrentHashMap<DeviceId, Set<PortNumber>> devicePortMap =
80 new ConcurrentHashMap<>();
Saurav Das423fe2b2015-12-04 10:52:59 -080081 //local store for ports on this device connected to neighbor-device-id
Saurav Das8a0732e2015-11-20 15:27:53 -080082 protected ConcurrentHashMap<PortNumber, DeviceId> portDeviceMap =
83 new ConcurrentHashMap<>();
Charles Chane849c192016-01-11 18:28:54 -080084 protected EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey, Integer>
85 nsNextObjStore = null;
86 protected EventuallyConsistentMap<SubnetNextObjectiveStoreKey, Integer>
87 subnetNextObjStore = null;
88 protected EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
89 portNextObjStore = null;
Charles Chan188ebf52015-12-23 00:15:11 -080090 private SegmentRoutingManager srManager;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080091
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070092 protected KryoNamespace.Builder kryo = new KryoNamespace.Builder()
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070093 .register(URI.class).register(HashSet.class)
94 .register(DeviceId.class).register(PortNumber.class)
95 .register(NeighborSet.class).register(PolicyGroupIdentifier.class)
96 .register(PolicyGroupParams.class)
97 .register(GroupBucketIdentifier.class)
98 .register(GroupBucketIdentifier.BucketOutputType.class);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080099
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700100 protected DefaultGroupHandler(DeviceId deviceId, ApplicationId appId,
101 DeviceProperties config,
102 LinkService linkService,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700103 FlowObjectiveService flowObjService,
Charles Chan188ebf52015-12-23 00:15:11 -0800104 SegmentRoutingManager srManager) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800105 this.deviceId = checkNotNull(deviceId);
106 this.appId = checkNotNull(appId);
107 this.deviceConfig = checkNotNull(config);
108 this.linkService = checkNotNull(linkService);
Charles Chan0b4e6182015-11-03 10:42:14 -0800109 this.allSegmentIds = checkNotNull(config.getAllDeviceSegmentIds());
110 try {
111 this.nodeSegmentId = config.getSegmentId(deviceId);
112 this.isEdgeRouter = config.isEdgeDevice(deviceId);
113 this.nodeMacAddr = checkNotNull(config.getDeviceMac(deviceId));
114 } catch (DeviceConfigNotFoundException e) {
115 log.warn(e.getMessage()
116 + " Skipping value assignment in DefaultGroupHandler");
117 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700118 this.flowObjectiveService = flowObjService;
Charles Chane849c192016-01-11 18:28:54 -0800119 this.nsNextObjStore = srManager.nsNextObjStore;
120 this.subnetNextObjStore = srManager.subnetNextObjStore;
121 this.portNextObjStore = srManager.portNextObjStore;
Charles Chan188ebf52015-12-23 00:15:11 -0800122 this.srManager = srManager;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800123
124 populateNeighborMaps();
125 }
126
127 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700128 * Creates a group handler object based on the type of device. If device is
129 * of edge type it returns edge group handler, else it returns transit group
130 * handler.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800131 *
132 * @param deviceId device identifier
133 * @param appId application identifier
134 * @param config interface to retrieve the device properties
135 * @param linkService link service object
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700136 * @param flowObjService flow objective service object
Charles Chane849c192016-01-11 18:28:54 -0800137 * @param srManager segment routing manager
Charles Chan0b4e6182015-11-03 10:42:14 -0800138 * @throws DeviceConfigNotFoundException if the device configuration is not found
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800139 * @return default group handler type
140 */
Saurav Das4ce45962015-11-24 23:21:05 -0800141 public static DefaultGroupHandler createGroupHandler(
142 DeviceId deviceId,
143 ApplicationId appId,
144 DeviceProperties config,
145 LinkService linkService,
146 FlowObjectiveService flowObjService,
Charles Chan188ebf52015-12-23 00:15:11 -0800147 SegmentRoutingManager srManager)
Saurav Das4ce45962015-11-24 23:21:05 -0800148 throws DeviceConfigNotFoundException {
Charles Chan0b4e6182015-11-03 10:42:14 -0800149 // handle possible exception in the caller
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800150 if (config.isEdgeDevice(deviceId)) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700151 return new DefaultEdgeGroupHandler(deviceId, appId, config,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700152 linkService,
153 flowObjService,
Charles Chan188ebf52015-12-23 00:15:11 -0800154 srManager
155 );
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800156 } else {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700157 return new DefaultTransitGroupHandler(deviceId, appId, config,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700158 linkService,
159 flowObjService,
Charles Chan188ebf52015-12-23 00:15:11 -0800160 srManager);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800161 }
162 }
163
164 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700165 * Creates the auto created groups for this device based on the current
166 * snapshot of the topology.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800167 */
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700168 // Empty implementations to be overridden by derived classes
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800169 public void createGroups() {
170 }
171
172 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700173 * Performs group creation or update procedures when a new link is
174 * discovered on this device.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800175 *
176 * @param newLink new neighbor link
Charles Chane849c192016-01-11 18:28:54 -0800177 * @param isMaster true if local instance is the master
178 *
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800179 */
Saurav Das8a0732e2015-11-20 15:27:53 -0800180 public void linkUp(Link newLink, boolean isMaster) {
sanghob35a6192015-04-01 13:05:26 -0700181
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800182 if (newLink.type() != Link.Type.DIRECT) {
183 log.warn("linkUp: unknown link type");
184 return;
185 }
186
187 if (!newLink.src().deviceId().equals(deviceId)) {
188 log.warn("linkUp: deviceId{} doesn't match with link src{}",
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700189 deviceId, newLink.src().deviceId());
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800190 return;
191 }
192
Saurav Das8a0732e2015-11-20 15:27:53 -0800193 log.info("* LinkUP: Device {} linkUp at local port {} to neighbor {}", deviceId,
194 newLink.src().port(), newLink.dst().deviceId());
Saurav Das59232cf2016-04-27 18:35:50 -0700195 // ensure local state is updated even if linkup is aborted later on
196 addNeighborAtPort(newLink.dst().deviceId(),
197 newLink.src().port());
198
Charles Chan0b4e6182015-11-03 10:42:14 -0800199 MacAddress dstMac;
200 try {
201 dstMac = deviceConfig.getDeviceMac(newLink.dst().deviceId());
202 } catch (DeviceConfigNotFoundException e) {
203 log.warn(e.getMessage() + " Aborting linkUp.");
204 return;
205 }
206
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700207 /*if (devicePortMap.get(newLink.dst().deviceId()) == null) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800208 // New Neighbor
209 newNeighbor(newLink);
210 } else {
211 // Old Neighbor
212 newPortToExistingNeighbor(newLink);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700213 }*/
214 Set<NeighborSet> nsSet = nsNextObjStore.keySet()
215 .stream()
216 .filter((nsStoreEntry) -> (nsStoreEntry.deviceId().equals(deviceId)))
217 .map((nsStoreEntry) -> (nsStoreEntry.neighborSet()))
218 .filter((ns) -> (ns.getDeviceIds()
219 .contains(newLink.dst().deviceId())))
220 .collect(Collectors.toSet());
221 log.trace("linkUp: nsNextObjStore contents for device {}:",
sangho0b2b6d12015-05-20 22:16:38 -0700222 deviceId,
223 nsSet);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700224 for (NeighborSet ns : nsSet) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700225 Integer nextId = nsNextObjStore.
226 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
Saurav Das4ce45962015-11-24 23:21:05 -0800227 if (nextId != null && isMaster) {
Saurav Das423fe2b2015-12-04 10:52:59 -0800228 // Create the new bucket to be updated
229 TrafficTreatment.Builder tBuilder =
230 DefaultTrafficTreatment.builder();
231 tBuilder.setOutput(newLink.src().port())
232 .setEthDst(dstMac)
233 .setEthSrc(nodeMacAddr);
234 if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
235 tBuilder.pushMpls()
236 .copyTtlOut()
237 .setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
238 }
239 // setup metadata to pass to nextObjective - indicate the vlan on egress
240 // if needed by the switch pipeline. Since hashed next-hops are always to
241 // other neighboring routers, there is no subnet assigned on those ports.
242 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
243 metabuilder.matchVlanId(
244 VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700245
Saurav Das423fe2b2015-12-04 10:52:59 -0800246 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
247 .withId(nextId)
248 .withType(NextObjective.Type.HASHED)
249 .addTreatment(tBuilder.build())
250 .withMeta(metabuilder.build())
251 .fromApp(appId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800252 log.info("**linkUp in device {}: Adding Bucket "
Saurav Das4ce45962015-11-24 23:21:05 -0800253 + "with Port {} to next object id {}",
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700254 deviceId,
255 newLink.src().port(),
Saurav Das4ce45962015-11-24 23:21:05 -0800256 nextId);
Charles Chan216e3c82016-04-23 14:48:16 -0700257
258 ObjectiveContext context = new DefaultObjectiveContext(
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700259 (objective) -> log.debug("LinkUp addedTo NextObj {} on {}",
Charles Chan216e3c82016-04-23 14:48:16 -0700260 nextId, deviceId),
261 (objective, error) ->
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700262 log.warn("LinkUp failed to addTo NextObj {} on {}: {}",
Charles Chan216e3c82016-04-23 14:48:16 -0700263 nextId, deviceId, error));
264 NextObjective nextObjective = nextObjBuilder.addToExisting(context);
Saurav Das4ce45962015-11-24 23:21:05 -0800265 flowObjectiveService.next(deviceId, nextObjective);
266 } else if (isMaster) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800267 log.warn("linkUp in device {}, but global store has no record "
268 + "for neighbor-set {}", deviceId, ns);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700269 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800270 }
271 }
272
273 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700274 * Performs group recovery procedures when a port goes down on this device.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800275 *
276 * @param port port number that has gone down
Charles Chane849c192016-01-11 18:28:54 -0800277 * @param isMaster true if local instance is the master
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800278 */
Saurav Das423fe2b2015-12-04 10:52:59 -0800279 public void portDown(PortNumber port, boolean isMaster) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800280 if (portDeviceMap.get(port) == null) {
281 log.warn("portDown: unknown port");
282 return;
283 }
Charles Chan0b4e6182015-11-03 10:42:14 -0800284
285 MacAddress dstMac;
286 try {
287 dstMac = deviceConfig.getDeviceMac(portDeviceMap.get(port));
288 } catch (DeviceConfigNotFoundException e) {
289 log.warn(e.getMessage() + " Aborting portDown.");
290 return;
291 }
292
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700293 log.debug("Device {} portDown {} to neighbor {}", deviceId, port,
294 portDeviceMap.get(port));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700295 /*Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(portDeviceMap
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700296 .get(port),
297 devicePortMap
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700298 .keySet());*/
299 Set<NeighborSet> nsSet = nsNextObjStore.keySet()
300 .stream()
301 .filter((nsStoreEntry) -> (nsStoreEntry.deviceId().equals(deviceId)))
302 .map((nsStoreEntry) -> (nsStoreEntry.neighborSet()))
303 .filter((ns) -> (ns.getDeviceIds()
304 .contains(portDeviceMap.get(port))))
305 .collect(Collectors.toSet());
Saurav Das423fe2b2015-12-04 10:52:59 -0800306 log.debug("portDown: nsNextObjStore contents for device {}:{}",
307 deviceId, nsSet);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800308 for (NeighborSet ns : nsSet) {
Saurav Das80980c72016-03-23 11:22:49 -0700309 NeighborSetNextObjectiveStoreKey nsStoreKey =
310 new NeighborSetNextObjectiveStoreKey(deviceId, ns);
311 Integer nextId = nsNextObjStore.get(nsStoreKey);
Saurav Das423fe2b2015-12-04 10:52:59 -0800312 if (nextId != null && isMaster) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800313 log.info("**portDown in device {}: Removing Bucket "
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700314 + "with Port {} to next object id {}",
315 deviceId,
316 port,
317 nextId);
Saurav Das423fe2b2015-12-04 10:52:59 -0800318 // Create the bucket to be removed
319 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
320 .builder();
321 tBuilder.setOutput(port)
322 .setEthDst(dstMac)
323 .setEthSrc(nodeMacAddr);
324 if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
325 tBuilder.pushMpls()
326 .copyTtlOut()
327 .setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
328 }
329 NextObjective.Builder nextObjBuilder = DefaultNextObjective
330 .builder()
331 .withType(NextObjective.Type.HASHED) //same as original
332 .withId(nextId)
333 .fromApp(appId)
334 .addTreatment(tBuilder.build());
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700335 ObjectiveContext context = new DefaultObjectiveContext(
336 (objective) -> log.debug("portDown removedFrom NextObj {} on {}",
337 nextId, deviceId),
338 (objective, error) ->
339 log.warn("portDown failed to removeFrom NextObj {} on {}: {}",
340 nextId, deviceId, error));
Saurav Das423fe2b2015-12-04 10:52:59 -0800341 NextObjective nextObjective = nextObjBuilder.
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700342 removeFromExisting(context);
sangho834e4b02015-05-01 09:38:25 -0700343
Saurav Das423fe2b2015-12-04 10:52:59 -0800344 flowObjectiveService.next(deviceId, nextObjective);
sangho834e4b02015-05-01 09:38:25 -0700345 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800346 }
347
348 devicePortMap.get(portDeviceMap.get(port)).remove(port);
349 portDeviceMap.remove(port);
350 }
351
352 /**
Saurav Das4ce45962015-11-24 23:21:05 -0800353 * Returns the next objective of type hashed associated with the neighborset.
354 * If there is no next objective for this neighborset, this method
Saurav Das8a0732e2015-11-20 15:27:53 -0800355 * would create a next objective and return. Optionally metadata can be
356 * passed in for the creation of the next objective.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800357 *
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700358 * @param ns neighborset
Saurav Das8a0732e2015-11-20 15:27:53 -0800359 * @param meta metadata passed into the creation of a Next Objective
360 * @return int if found or -1 if there are errors in the creation of the
361 * neighbor set.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800362 */
Saurav Das8a0732e2015-11-20 15:27:53 -0800363 public int getNextObjectiveId(NeighborSet ns, TrafficSelector meta) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700364 Integer nextId = nsNextObjStore.
365 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700366 if (nextId == null) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700367 log.trace("getNextObjectiveId in device{}: Next objective id "
368 + "not found for {} and creating", deviceId, ns);
369 log.trace("getNextObjectiveId: nsNextObjStore contents for device {}: {}",
370 deviceId,
371 nsNextObjStore.entrySet()
372 .stream()
373 .filter((nsStoreEntry) ->
374 (nsStoreEntry.getKey().deviceId().equals(deviceId)))
375 .collect(Collectors.toList()));
Saurav Das8a0732e2015-11-20 15:27:53 -0800376 createGroupsFromNeighborsets(Collections.singleton(ns), meta);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700377 nextId = nsNextObjStore.
378 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700379 if (nextId == null) {
380 log.warn("getNextObjectiveId: unable to create next objective");
381 return -1;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700382 } else {
383 log.debug("getNextObjectiveId in device{}: Next objective id {} "
Sho SHIMIZUaf973432015-09-11 14:24:50 -0700384 + "created for {}", deviceId, nextId, ns);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700385 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700386 } else {
387 log.trace("getNextObjectiveId in device{}: Next objective id {} "
Sho SHIMIZUaf973432015-09-11 14:24:50 -0700388 + "found for {}", deviceId, nextId, ns);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700389 }
Sho SHIMIZUaf973432015-09-11 14:24:50 -0700390 return nextId;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800391 }
392
sangho0b2b6d12015-05-20 22:16:38 -0700393 /**
Saurav Das4ce45962015-11-24 23:21:05 -0800394 * Returns the next objective of type broadcast associated with the subnet,
395 * or -1 if no such objective exists. Note that this method does NOT create
396 * the next objective as a side-effect. It is expected that is objective is
397 * created at startup from network configuration.
Charles Chanc42e84e2015-10-20 16:24:19 -0700398 *
399 * @param prefix subnet information
400 * @return int if found or -1
401 */
402 public int getSubnetNextObjectiveId(IpPrefix prefix) {
403 Integer nextId = subnetNextObjStore.
404 get(new SubnetNextObjectiveStoreKey(deviceId, prefix));
Charles Chan9f676b62015-10-29 14:58:10 -0700405
406 return (nextId != null) ? nextId : -1;
Charles Chanc42e84e2015-10-20 16:24:19 -0700407 }
408
409 /**
Saurav Das4ce45962015-11-24 23:21:05 -0800410 * Returns the next objective of type simple associated with the port on the
411 * device, given the treatment. Different treatments to the same port result
412 * in different next objectives. If no such objective exists, this method
413 * creates one and returns the id. Optionally metadata can be passed in for
414 * the creation of the objective.
415 *
416 * @param portNum the port number for the simple next objective
417 * @param treatment the actions to apply on the packets (should include outport)
418 * @param meta optional metadata passed into the creation of the next objective
419 * @return int if found or created, -1 if there are errors during the
420 * creation of the next objective.
421 */
422 public int getPortNextObjectiveId(PortNumber portNum, TrafficTreatment treatment,
423 TrafficSelector meta) {
Charles Chane849c192016-01-11 18:28:54 -0800424 Integer nextId = portNextObjStore
425 .get(new PortNextObjectiveStoreKey(deviceId, portNum, treatment));
Saurav Das4ce45962015-11-24 23:21:05 -0800426 if (nextId == null) {
427 log.trace("getPortNextObjectiveId in device{}: Next objective id "
428 + "not found for {} and {} creating", deviceId, portNum);
429 createGroupFromPort(portNum, treatment, meta);
430 nextId = portNextObjStore.get(
431 new PortNextObjectiveStoreKey(deviceId, portNum, treatment));
432 if (nextId == null) {
433 log.warn("getPortNextObjectiveId: unable to create next obj"
Charles Chane849c192016-01-11 18:28:54 -0800434 + "for dev:{} port:{}", deviceId, portNum);
435 return -1;
436 }
437 }
438 return nextId;
439 }
440
441 /**
sangho0b2b6d12015-05-20 22:16:38 -0700442 * Checks if the next objective ID (group) for the neighbor set exists or not.
443 *
444 * @param ns neighbor set to check
445 * @return true if it exists, false otherwise
446 */
447 public boolean hasNextObjectiveId(NeighborSet ns) {
448 Integer nextId = nsNextObjStore.
449 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
450 if (nextId == null) {
451 return false;
452 }
453
454 return true;
455 }
456
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700457 // Empty implementation
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800458 protected void newNeighbor(Link newLink) {
459 }
460
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700461 // Empty implementation
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800462 protected void newPortToExistingNeighbor(Link newLink) {
463 }
464
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700465 // Empty implementation
466 protected Set<NeighborSet>
467 computeImpactedNeighborsetForPortEvent(DeviceId impactedNeighbor,
468 Set<DeviceId> updatedNeighbors) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800469 return null;
470 }
471
472 private void populateNeighborMaps() {
473 Set<Link> outgoingLinks = linkService.getDeviceEgressLinks(deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700474 for (Link link : outgoingLinks) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800475 if (link.type() != Link.Type.DIRECT) {
476 continue;
477 }
478 addNeighborAtPort(link.dst().deviceId(), link.src().port());
479 }
480 }
481
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700482 protected void addNeighborAtPort(DeviceId neighborId,
483 PortNumber portToNeighbor) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800484 // Update DeviceToPort database
485 log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
486 deviceId, neighborId, portToNeighbor);
Saurav Das8a0732e2015-11-20 15:27:53 -0800487 Set<PortNumber> ports = Collections
488 .newSetFromMap(new ConcurrentHashMap<PortNumber, Boolean>());
489 ports.add(portToNeighbor);
490 Set<PortNumber> portnums = devicePortMap.putIfAbsent(neighborId, ports);
491 if (portnums != null) {
492 portnums.add(portToNeighbor);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800493 }
494
495 // Update portToDevice database
Saurav Das8a0732e2015-11-20 15:27:53 -0800496 DeviceId prev = portDeviceMap.putIfAbsent(portToNeighbor, neighborId);
497 if (prev != null) {
498 log.warn("Device: {} port: {} has neighbor: {}. NOT updating "
499 + "to neighbor: {}", deviceId, portToNeighbor, prev, neighborId);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800500 }
501 }
502
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700503 protected Set<Set<DeviceId>> getPowerSetOfNeighbors(Set<DeviceId> neighbors) {
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700504 List<DeviceId> list = new ArrayList<>(neighbors);
505 Set<Set<DeviceId>> sets = new HashSet<>();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800506 // get the number of elements in the neighbors
507 int elements = list.size();
508 // the number of members of a power set is 2^n
509 // including the empty set
510 int powerElements = (1 << elements);
511
512 // run a binary counter for the number of power elements
513 // NOTE: Exclude empty set
514 for (long i = 1; i < powerElements; i++) {
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700515 Set<DeviceId> neighborSubSet = new HashSet<>();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800516 for (int j = 0; j < elements; j++) {
517 if ((i >> j) % 2 == 1) {
518 neighborSubSet.add(list.get(j));
519 }
520 }
521 sets.add(neighborSubSet);
522 }
523 return sets;
524 }
525
526 private boolean isSegmentIdSameAsNodeSegmentId(DeviceId deviceId, int sId) {
Charles Chan0b4e6182015-11-03 10:42:14 -0800527 int segmentId;
528 try {
529 segmentId = deviceConfig.getSegmentId(deviceId);
530 } catch (DeviceConfigNotFoundException e) {
531 log.warn(e.getMessage() + " Aborting isSegmentIdSameAsNodeSegmentId.");
532 return false;
533 }
534
535 return segmentId == sId;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800536 }
537
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700538 protected List<Integer> getSegmentIdsTobePairedWithNeighborSet(Set<DeviceId> neighbors) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800539
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700540 List<Integer> nsSegmentIds = new ArrayList<>();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800541
sanghob35a6192015-04-01 13:05:26 -0700542 // Always pair up with no edge label
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700543 // If (neighbors.size() == 1) {
sanghob35a6192015-04-01 13:05:26 -0700544 nsSegmentIds.add(-1);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700545 // }
sanghob35a6192015-04-01 13:05:26 -0700546
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800547 // Filter out SegmentIds matching with the
548 // nodes in the combo
549 for (Integer sId : allSegmentIds) {
550 if (sId.equals(nodeSegmentId)) {
551 continue;
552 }
553 boolean filterOut = false;
554 // Check if the edge label being set is of
555 // any node in the Neighbor set
556 for (DeviceId deviceId : neighbors) {
557 if (isSegmentIdSameAsNodeSegmentId(deviceId, sId)) {
558 filterOut = true;
559 break;
560 }
561 }
562 if (!filterOut) {
563 nsSegmentIds.add(sId);
564 }
565 }
566 return nsSegmentIds;
567 }
568
sangho1e575652015-05-14 00:39:53 -0700569 /**
570 * Creates Groups from a set of NeighborSet given.
571 *
572 * @param nsSet a set of NeighborSet
Saurav Das8a0732e2015-11-20 15:27:53 -0800573 * @param meta metadata passed into the creation of a Next Objective
sangho1e575652015-05-14 00:39:53 -0700574 */
Saurav Das8a0732e2015-11-20 15:27:53 -0800575 public void createGroupsFromNeighborsets(Set<NeighborSet> nsSet,
576 TrafficSelector meta) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800577 for (NeighborSet ns : nsSet) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700578 int nextId = flowObjectiveService.allocateNextId();
579 NextObjective.Builder nextObjBuilder = DefaultNextObjective
580 .builder().withId(nextId)
581 .withType(NextObjective.Type.HASHED).fromApp(appId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800582 for (DeviceId neighborId : ns.getDeviceIds()) {
583 if (devicePortMap.get(neighborId) == null) {
584 log.warn("Neighbor {} is not in the port map yet for dev:{}",
585 neighborId, deviceId);
sangho834e4b02015-05-01 09:38:25 -0700586 return;
Saurav Das8a0732e2015-11-20 15:27:53 -0800587 } else if (devicePortMap.get(neighborId).size() == 0) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700588 log.warn("There are no ports for "
Saurav Das8a0732e2015-11-20 15:27:53 -0800589 + "the Device {} in the port map yet", neighborId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700590 return;
sangho834e4b02015-05-01 09:38:25 -0700591 }
592
Saurav Das8a0732e2015-11-20 15:27:53 -0800593 MacAddress neighborMac;
Charles Chan0b4e6182015-11-03 10:42:14 -0800594 try {
Saurav Das8a0732e2015-11-20 15:27:53 -0800595 neighborMac = deviceConfig.getDeviceMac(neighborId);
Charles Chan0b4e6182015-11-03 10:42:14 -0800596 } catch (DeviceConfigNotFoundException e) {
597 log.warn(e.getMessage() + " Aborting createGroupsFromNeighborsets.");
598 return;
599 }
600
Saurav Das8a0732e2015-11-20 15:27:53 -0800601 for (PortNumber sp : devicePortMap.get(neighborId)) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700602 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
603 .builder();
Saurav Das8a0732e2015-11-20 15:27:53 -0800604 tBuilder.setEthDst(neighborMac)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700605 .setEthSrc(nodeMacAddr);
606 if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800607 tBuilder.pushMpls()
608 .copyTtlOut()
609 .setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700610 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800611 tBuilder.setOutput(sp);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700612 nextObjBuilder.addTreatment(tBuilder.build());
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800613 }
614 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800615 if (meta != null) {
Saurav Das4ce45962015-11-24 23:21:05 -0800616 nextObjBuilder.withMeta(meta);
Saurav Das8a0732e2015-11-20 15:27:53 -0800617 }
Charles Chan216e3c82016-04-23 14:48:16 -0700618
619 ObjectiveContext context = new DefaultObjectiveContext(
620 (objective) -> log.debug("createGroupsFromNeighborsets installed NextObj {} on {}",
621 nextId, deviceId),
622 (objective, error) ->
623 log.warn("createGroupsFromNeighborsets failed to install NextObj {} on {}: {}",
624 nextId, deviceId, error));
625 NextObjective nextObj = nextObjBuilder.add(context);
626 log.debug("**createGroupsFromNeighborsets: Submited "
Saurav Das8a0732e2015-11-20 15:27:53 -0800627 + "next objective {} in device {}",
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700628 nextId, deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800629 flowObjectiveService.next(deviceId, nextObj);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700630 nsNextObjStore.put(new NeighborSetNextObjectiveStoreKey(deviceId, ns),
631 nextId);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800632 }
633 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700634
Saurav Das4ce45962015-11-24 23:21:05 -0800635 /**
636 * Creates broadcast groups for all ports in the same configured subnet.
Saurav Das4ce45962015-11-24 23:21:05 -0800637 */
Charles Chanc42e84e2015-10-20 16:24:19 -0700638 public void createGroupsFromSubnetConfig() {
Saurav Das7a1ffca2016-03-28 19:00:18 -0700639 Map<Ip4Prefix, List<PortNumber>> subnetPortMap;
640 try {
641 subnetPortMap = this.deviceConfig.getSubnetPortsMap(this.deviceId);
642 } catch (DeviceConfigNotFoundException e) {
643 log.warn(e.getMessage()
644 + " Not creating broadcast groups for device: " + deviceId);
645 return;
646 }
Charles Chanc42e84e2015-10-20 16:24:19 -0700647 // Construct a broadcast group for each subnet
648 subnetPortMap.forEach((subnet, ports) -> {
Charles Chan9f676b62015-10-29 14:58:10 -0700649 SubnetNextObjectiveStoreKey key =
650 new SubnetNextObjectiveStoreKey(deviceId, subnet);
651
652 if (subnetNextObjStore.containsKey(key)) {
653 log.debug("Broadcast group for device {} and subnet {} exists",
654 deviceId, subnet);
655 return;
656 }
657
Charles Chan188ebf52015-12-23 00:15:11 -0800658 VlanId assignedVlanId =
659 srManager.getSubnetAssignedVlanId(this.deviceId, subnet);
660 TrafficSelector metadata =
661 DefaultTrafficSelector.builder().matchVlanId(assignedVlanId).build();
662
Charles Chanc42e84e2015-10-20 16:24:19 -0700663 int nextId = flowObjectiveService.allocateNextId();
664
665 NextObjective.Builder nextObjBuilder = DefaultNextObjective
666 .builder().withId(nextId)
Charles Chan188ebf52015-12-23 00:15:11 -0800667 .withType(NextObjective.Type.BROADCAST).fromApp(appId)
668 .withMeta(metadata);
Charles Chanc42e84e2015-10-20 16:24:19 -0700669
670 ports.forEach(port -> {
671 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Saurav Das4f980082015-11-05 13:39:15 -0800672 tBuilder.popVlan();
Charles Chanc42e84e2015-10-20 16:24:19 -0700673 tBuilder.setOutput(port);
674 nextObjBuilder.addTreatment(tBuilder.build());
675 });
676
677 NextObjective nextObj = nextObjBuilder.add();
678 flowObjectiveService.next(deviceId, nextObj);
679 log.debug("createGroupFromSubnetConfig: Submited "
680 + "next objective {} in device {}",
681 nextId, deviceId);
Charles Chan9f676b62015-10-29 14:58:10 -0700682
Charles Chanc42e84e2015-10-20 16:24:19 -0700683 subnetNextObjStore.put(key, nextId);
684 });
685 }
686
Charles Chane849c192016-01-11 18:28:54 -0800687 /**
Saurav Das4ce45962015-11-24 23:21:05 -0800688 * Create simple next objective for a single port. The treatments can include
689 * all outgoing actions that need to happen on the packet.
690 *
691 * @param portNum the outgoing port on the device
692 * @param treatment the actions to apply on the packets (should include outport)
693 * @param meta optional data to pass to the driver
694 */
695 public void createGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
696 TrafficSelector meta) {
697 int nextId = flowObjectiveService.allocateNextId();
698 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
699 deviceId, portNum, treatment);
700
701 NextObjective.Builder nextObjBuilder = DefaultNextObjective
702 .builder().withId(nextId)
703 .withType(NextObjective.Type.SIMPLE)
704 .addTreatment(treatment)
705 .fromApp(appId)
706 .withMeta(meta);
707
708 NextObjective nextObj = nextObjBuilder.add();
709 flowObjectiveService.next(deviceId, nextObj);
710 log.debug("createGroupFromPort: Submited next objective {} in device {} "
711 + "for port {}", nextId, deviceId, portNum);
712
713 portNextObjStore.put(key, nextId);
714 }
715
sangho1e575652015-05-14 00:39:53 -0700716 /**
717 * Removes groups for the next objective ID given.
718 *
719 * @param objectiveId next objective ID to remove
720 * @return true if succeeds, false otherwise
721 */
722 public boolean removeGroup(int objectiveId) {
723
724 if (nsNextObjStore.containsValue(objectiveId)) {
725 NextObjective.Builder nextObjBuilder = DefaultNextObjective
726 .builder().withId(objectiveId)
727 .withType(NextObjective.Type.HASHED).fromApp(appId);
Charles Chan216e3c82016-04-23 14:48:16 -0700728 ObjectiveContext context = new DefaultObjectiveContext(
729 (objective) -> log.debug("RemoveGroup removes NextObj {} on {}",
730 objectiveId, deviceId),
731 (objective, error) ->
732 log.warn("RemoveGroup failed to remove NextObj {} on {}: {}",
733 objectiveId, deviceId, error));
734 NextObjective nextObjective = nextObjBuilder.remove(context);
Saurav Das8a0732e2015-11-20 15:27:53 -0800735 log.info("**removeGroup: Submited "
736 + "next objective {} in device {}",
737 objectiveId, deviceId);
sangho1e575652015-05-14 00:39:53 -0700738 flowObjectiveService.next(deviceId, nextObjective);
739
740 for (Map.Entry<NeighborSetNextObjectiveStoreKey, Integer> entry: nsNextObjStore.entrySet()) {
741 if (entry.getValue().equals(objectiveId)) {
742 nsNextObjStore.remove(entry.getKey());
743 break;
744 }
745 }
sangho0b2b6d12015-05-20 22:16:38 -0700746 return true;
sangho1e575652015-05-14 00:39:53 -0700747 }
748
749 return false;
750 }
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -0700751
Charles Chane849c192016-01-11 18:28:54 -0800752 /**
753 * Removes all groups from all next objective stores.
754 */
Saurav Das423fe2b2015-12-04 10:52:59 -0800755 public void removeAllGroups() {
756 for (Map.Entry<NeighborSetNextObjectiveStoreKey, Integer> entry:
757 nsNextObjStore.entrySet()) {
758 removeGroup(entry.getValue());
759 }
760 for (Map.Entry<PortNextObjectiveStoreKey, Integer> entry:
761 portNextObjStore.entrySet()) {
762 removeGroup(entry.getValue());
763 }
764 for (Map.Entry<SubnetNextObjectiveStoreKey, Integer> entry:
765 subnetNextObjStore.entrySet()) {
766 removeGroup(entry.getValue());
767 }
768 // should probably clean local stores port-neighbor
769 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800770}