blob: 2986c50e2d6cacffd50f4c6028da56201a0bd82c [file] [log] [blame]
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -08001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
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;
46import org.onosproject.net.flowobjective.FlowObjectiveService;
47import org.onosproject.net.flowobjective.NextObjective;
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -070048import org.onosproject.net.flowobjective.Objective;
49import org.onosproject.net.flowobjective.ObjectiveContext;
50import org.onosproject.net.flowobjective.ObjectiveError;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070051import org.onosproject.net.group.DefaultGroupKey;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080052import org.onosproject.net.group.GroupKey;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080053import org.onosproject.net.link.LinkService;
Saurav Das423fe2b2015-12-04 10:52:59 -080054import org.onosproject.segmentrouting.SegmentRoutingManager;
Charles Chan0b4e6182015-11-03 10:42:14 -080055import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
56import org.onosproject.segmentrouting.config.DeviceProperties;
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<>();
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070084 protected EventuallyConsistentMap<
85 NeighborSetNextObjectiveStoreKey, Integer> nsNextObjStore = null;
Charles Chanc42e84e2015-10-20 16:24:19 -070086 protected EventuallyConsistentMap<
87 SubnetNextObjectiveStoreKey, Integer> subnetNextObjStore = null;
Saurav Das4ce45962015-11-24 23:21:05 -080088 protected EventuallyConsistentMap<
89 PortNextObjectiveStoreKey, Integer> portNextObjStore = null;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080090
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070091 protected KryoNamespace.Builder kryo = new KryoNamespace.Builder()
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070092 .register(URI.class).register(HashSet.class)
93 .register(DeviceId.class).register(PortNumber.class)
94 .register(NeighborSet.class).register(PolicyGroupIdentifier.class)
95 .register(PolicyGroupParams.class)
96 .register(GroupBucketIdentifier.class)
97 .register(GroupBucketIdentifier.BucketOutputType.class);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080098
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070099 protected DefaultGroupHandler(DeviceId deviceId, ApplicationId appId,
100 DeviceProperties config,
101 LinkService linkService,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700102 FlowObjectiveService flowObjService,
Saurav Das4ce45962015-11-24 23:21:05 -0800103 EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey,
Charles Chanc42e84e2015-10-20 16:24:19 -0700104 Integer> nsNextObjStore,
105 EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
Saurav Das4ce45962015-11-24 23:21:05 -0800106 Integer> subnetNextObjStore,
107 EventuallyConsistentMap<PortNextObjectiveStoreKey,
108 Integer> portNextObjStore) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800109 this.deviceId = checkNotNull(deviceId);
110 this.appId = checkNotNull(appId);
111 this.deviceConfig = checkNotNull(config);
112 this.linkService = checkNotNull(linkService);
Charles Chan0b4e6182015-11-03 10:42:14 -0800113 this.allSegmentIds = checkNotNull(config.getAllDeviceSegmentIds());
114 try {
115 this.nodeSegmentId = config.getSegmentId(deviceId);
116 this.isEdgeRouter = config.isEdgeDevice(deviceId);
117 this.nodeMacAddr = checkNotNull(config.getDeviceMac(deviceId));
118 } catch (DeviceConfigNotFoundException e) {
119 log.warn(e.getMessage()
120 + " Skipping value assignment in DefaultGroupHandler");
121 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700122 this.flowObjectiveService = flowObjService;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700123 this.nsNextObjStore = nsNextObjStore;
Charles Chanc42e84e2015-10-20 16:24:19 -0700124 this.subnetNextObjStore = subnetNextObjStore;
Saurav Das4ce45962015-11-24 23:21:05 -0800125 this.portNextObjStore = portNextObjStore;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800126
127 populateNeighborMaps();
128 }
129
130 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700131 * Creates a group handler object based on the type of device. If device is
132 * of edge type it returns edge group handler, else it returns transit group
133 * handler.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800134 *
135 * @param deviceId device identifier
136 * @param appId application identifier
137 * @param config interface to retrieve the device properties
138 * @param linkService link service object
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700139 * @param flowObjService flow objective service object
Charles Chanc42e84e2015-10-20 16:24:19 -0700140 * @param nsNextObjStore NeighborSet next objective store map
141 * @param subnetNextObjStore subnet next objective store map
Charles Chan0b4e6182015-11-03 10:42:14 -0800142 * @throws DeviceConfigNotFoundException if the device configuration is not found
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800143 * @return default group handler type
144 */
Saurav Das4ce45962015-11-24 23:21:05 -0800145 public static DefaultGroupHandler createGroupHandler(
146 DeviceId deviceId,
147 ApplicationId appId,
148 DeviceProperties config,
149 LinkService linkService,
150 FlowObjectiveService flowObjService,
151 EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey,
152 Integer> nsNextObjStore,
153 EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
154 Integer> subnetNextObjStore,
155 EventuallyConsistentMap<PortNextObjectiveStoreKey,
156 Integer> portNextObjStore)
157 throws DeviceConfigNotFoundException {
Charles Chan0b4e6182015-11-03 10:42:14 -0800158 // handle possible exception in the caller
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800159 if (config.isEdgeDevice(deviceId)) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700160 return new DefaultEdgeGroupHandler(deviceId, appId, config,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700161 linkService,
162 flowObjService,
Charles Chanc42e84e2015-10-20 16:24:19 -0700163 nsNextObjStore,
Saurav Das4ce45962015-11-24 23:21:05 -0800164 subnetNextObjStore,
165 portNextObjStore);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800166 } else {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700167 return new DefaultTransitGroupHandler(deviceId, appId, config,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700168 linkService,
169 flowObjService,
Charles Chanc42e84e2015-10-20 16:24:19 -0700170 nsNextObjStore,
Saurav Das4ce45962015-11-24 23:21:05 -0800171 subnetNextObjStore,
172 portNextObjStore);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800173 }
174 }
175
176 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700177 * Creates the auto created groups for this device based on the current
178 * snapshot of the topology.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800179 */
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700180 // Empty implementations to be overridden by derived classes
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800181 public void createGroups() {
182 }
183
184 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700185 * Performs group creation or update procedures when a new link is
186 * discovered on this device.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800187 *
188 * @param newLink new neighbor link
189 */
Saurav Das8a0732e2015-11-20 15:27:53 -0800190 public void linkUp(Link newLink, boolean isMaster) {
sanghob35a6192015-04-01 13:05:26 -0700191
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800192 if (newLink.type() != Link.Type.DIRECT) {
193 log.warn("linkUp: unknown link type");
194 return;
195 }
196
197 if (!newLink.src().deviceId().equals(deviceId)) {
198 log.warn("linkUp: deviceId{} doesn't match with link src{}",
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700199 deviceId, newLink.src().deviceId());
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800200 return;
201 }
202
Saurav Das8a0732e2015-11-20 15:27:53 -0800203 log.info("* LinkUP: Device {} linkUp at local port {} to neighbor {}", deviceId,
204 newLink.src().port(), newLink.dst().deviceId());
Charles Chan0b4e6182015-11-03 10:42:14 -0800205 MacAddress dstMac;
206 try {
207 dstMac = deviceConfig.getDeviceMac(newLink.dst().deviceId());
208 } catch (DeviceConfigNotFoundException e) {
209 log.warn(e.getMessage() + " Aborting linkUp.");
210 return;
211 }
212
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700213 addNeighborAtPort(newLink.dst().deviceId(),
214 newLink.src().port());
215 /*if (devicePortMap.get(newLink.dst().deviceId()) == null) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800216 // New Neighbor
217 newNeighbor(newLink);
218 } else {
219 // Old Neighbor
220 newPortToExistingNeighbor(newLink);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700221 }*/
222 Set<NeighborSet> nsSet = nsNextObjStore.keySet()
223 .stream()
224 .filter((nsStoreEntry) -> (nsStoreEntry.deviceId().equals(deviceId)))
225 .map((nsStoreEntry) -> (nsStoreEntry.neighborSet()))
226 .filter((ns) -> (ns.getDeviceIds()
227 .contains(newLink.dst().deviceId())))
228 .collect(Collectors.toSet());
229 log.trace("linkUp: nsNextObjStore contents for device {}:",
sangho0b2b6d12015-05-20 22:16:38 -0700230 deviceId,
231 nsSet);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700232 for (NeighborSet ns : nsSet) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700233 Integer nextId = nsNextObjStore.
234 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
Saurav Das4ce45962015-11-24 23:21:05 -0800235 if (nextId != null && isMaster) {
Saurav Das423fe2b2015-12-04 10:52:59 -0800236 // Create the new bucket to be updated
237 TrafficTreatment.Builder tBuilder =
238 DefaultTrafficTreatment.builder();
239 tBuilder.setOutput(newLink.src().port())
240 .setEthDst(dstMac)
241 .setEthSrc(nodeMacAddr);
242 if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
243 tBuilder.pushMpls()
244 .copyTtlOut()
245 .setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
246 }
247 // setup metadata to pass to nextObjective - indicate the vlan on egress
248 // if needed by the switch pipeline. Since hashed next-hops are always to
249 // other neighboring routers, there is no subnet assigned on those ports.
250 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
251 metabuilder.matchVlanId(
252 VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700253
Saurav Das423fe2b2015-12-04 10:52:59 -0800254 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
255 .withId(nextId)
256 .withType(NextObjective.Type.HASHED)
257 .addTreatment(tBuilder.build())
258 .withMeta(metabuilder.build())
259 .fromApp(appId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800260 log.info("**linkUp in device {}: Adding Bucket "
Saurav Das4ce45962015-11-24 23:21:05 -0800261 + "with Port {} to next object id {}",
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700262 deviceId,
263 newLink.src().port(),
Saurav Das4ce45962015-11-24 23:21:05 -0800264 nextId);
265 NextObjective nextObjective = nextObjBuilder.
266 addToExisting(new SRNextObjectiveContext(deviceId));
267 flowObjectiveService.next(deviceId, nextObjective);
Saurav Das423fe2b2015-12-04 10:52:59 -0800268
269 // the addition of a bucket may actually change the neighborset
270 // update the global store
271 /*
272 Set<DeviceId> neighbors = new HashSet<DeviceId>(ns.getDeviceIds());
273 boolean newadd = neighbors.add(newLink.dst().deviceId());
274 if (newadd) {
275 NeighborSet nsnew = new NeighborSet(neighbors, ns.getEdgeLabel());
276 nsNextObjStore.put(new NeighborSetNextObjectiveStoreKey(deviceId, nsnew),
277 nextId);
278 nsNextObjStore.remove(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
279 }*/
Saurav Das4ce45962015-11-24 23:21:05 -0800280 } else if (isMaster) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800281 log.warn("linkUp in device {}, but global store has no record "
282 + "for neighbor-set {}", deviceId, ns);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700283 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800284 }
285 }
286
287 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700288 * Performs group recovery procedures when a port goes down on this device.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800289 *
290 * @param port port number that has gone down
291 */
Saurav Das423fe2b2015-12-04 10:52:59 -0800292 public void portDown(PortNumber port, boolean isMaster) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800293 if (portDeviceMap.get(port) == null) {
294 log.warn("portDown: unknown port");
295 return;
296 }
Charles Chan0b4e6182015-11-03 10:42:14 -0800297
298 MacAddress dstMac;
299 try {
300 dstMac = deviceConfig.getDeviceMac(portDeviceMap.get(port));
301 } catch (DeviceConfigNotFoundException e) {
302 log.warn(e.getMessage() + " Aborting portDown.");
303 return;
304 }
305
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700306 log.debug("Device {} portDown {} to neighbor {}", deviceId, port,
307 portDeviceMap.get(port));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700308 /*Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(portDeviceMap
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700309 .get(port),
310 devicePortMap
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700311 .keySet());*/
312 Set<NeighborSet> nsSet = nsNextObjStore.keySet()
313 .stream()
314 .filter((nsStoreEntry) -> (nsStoreEntry.deviceId().equals(deviceId)))
315 .map((nsStoreEntry) -> (nsStoreEntry.neighborSet()))
316 .filter((ns) -> (ns.getDeviceIds()
317 .contains(portDeviceMap.get(port))))
318 .collect(Collectors.toSet());
Saurav Das423fe2b2015-12-04 10:52:59 -0800319 log.debug("portDown: nsNextObjStore contents for device {}:{}",
320 deviceId, nsSet);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800321 for (NeighborSet ns : nsSet) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700322 Integer nextId = nsNextObjStore.
323 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
Saurav Das423fe2b2015-12-04 10:52:59 -0800324 if (nextId != null && isMaster) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800325 log.info("**portDown in device {}: Removing Bucket "
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700326 + "with Port {} to next object id {}",
327 deviceId,
328 port,
329 nextId);
Saurav Das423fe2b2015-12-04 10:52:59 -0800330 // Create the bucket to be removed
331 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
332 .builder();
333 tBuilder.setOutput(port)
334 .setEthDst(dstMac)
335 .setEthSrc(nodeMacAddr);
336 if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
337 tBuilder.pushMpls()
338 .copyTtlOut()
339 .setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
340 }
341 NextObjective.Builder nextObjBuilder = DefaultNextObjective
342 .builder()
343 .withType(NextObjective.Type.HASHED) //same as original
344 .withId(nextId)
345 .fromApp(appId)
346 .addTreatment(tBuilder.build());
347 NextObjective nextObjective = nextObjBuilder.
348 removeFromExisting(new SRNextObjectiveContext(deviceId));
sangho834e4b02015-05-01 09:38:25 -0700349
Saurav Das423fe2b2015-12-04 10:52:59 -0800350 flowObjectiveService.next(deviceId, nextObjective);
351
352 // the removal of a bucket may actually change the neighborset
353 // update the global store
354 /*
355 Set<DeviceId> neighbors = new HashSet<DeviceId>(ns.getDeviceIds());
356 boolean removed = neighbors.remove(portDeviceMap.get(port));
357 if (removed) {
358 NeighborSet nsnew = new NeighborSet(neighbors, ns.getEdgeLabel());
359 nsNextObjStore.put(new NeighborSetNextObjectiveStoreKey(deviceId, nsnew),
360 nextId);
361 nsNextObjStore.remove(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
362 }*/
sangho834e4b02015-05-01 09:38:25 -0700363 }
364
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800365 }
366
367 devicePortMap.get(portDeviceMap.get(port)).remove(port);
368 portDeviceMap.remove(port);
369 }
370
371 /**
Saurav Das4ce45962015-11-24 23:21:05 -0800372 * Returns the next objective of type hashed associated with the neighborset.
373 * If there is no next objective for this neighborset, this method
Saurav Das8a0732e2015-11-20 15:27:53 -0800374 * would create a next objective and return. Optionally metadata can be
375 * passed in for the creation of the next objective.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800376 *
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700377 * @param ns neighborset
Saurav Das8a0732e2015-11-20 15:27:53 -0800378 * @param meta metadata passed into the creation of a Next Objective
379 * @return int if found or -1 if there are errors in the creation of the
380 * neighbor set.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800381 */
Saurav Das8a0732e2015-11-20 15:27:53 -0800382 public int getNextObjectiveId(NeighborSet ns, TrafficSelector meta) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700383 Integer nextId = nsNextObjStore.
384 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700385 if (nextId == null) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700386 log.trace("getNextObjectiveId in device{}: Next objective id "
387 + "not found for {} and creating", deviceId, ns);
388 log.trace("getNextObjectiveId: nsNextObjStore contents for device {}: {}",
389 deviceId,
390 nsNextObjStore.entrySet()
391 .stream()
392 .filter((nsStoreEntry) ->
393 (nsStoreEntry.getKey().deviceId().equals(deviceId)))
394 .collect(Collectors.toList()));
Saurav Das8a0732e2015-11-20 15:27:53 -0800395 createGroupsFromNeighborsets(Collections.singleton(ns), meta);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700396 nextId = nsNextObjStore.
397 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700398 if (nextId == null) {
399 log.warn("getNextObjectiveId: unable to create next objective");
400 return -1;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700401 } else {
402 log.debug("getNextObjectiveId in device{}: Next objective id {} "
Sho SHIMIZUaf973432015-09-11 14:24:50 -0700403 + "created for {}", deviceId, nextId, ns);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700404 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700405 } else {
406 log.trace("getNextObjectiveId in device{}: Next objective id {} "
Sho SHIMIZUaf973432015-09-11 14:24:50 -0700407 + "found for {}", deviceId, nextId, ns);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700408 }
Sho SHIMIZUaf973432015-09-11 14:24:50 -0700409 return nextId;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800410 }
411
sangho0b2b6d12015-05-20 22:16:38 -0700412 /**
Saurav Das4ce45962015-11-24 23:21:05 -0800413 * Returns the next objective of type broadcast associated with the subnet,
414 * or -1 if no such objective exists. Note that this method does NOT create
415 * the next objective as a side-effect. It is expected that is objective is
416 * created at startup from network configuration.
Charles Chanc42e84e2015-10-20 16:24:19 -0700417 *
418 * @param prefix subnet information
419 * @return int if found or -1
420 */
421 public int getSubnetNextObjectiveId(IpPrefix prefix) {
422 Integer nextId = subnetNextObjStore.
423 get(new SubnetNextObjectiveStoreKey(deviceId, prefix));
Charles Chan9f676b62015-10-29 14:58:10 -0700424
425 return (nextId != null) ? nextId : -1;
Charles Chanc42e84e2015-10-20 16:24:19 -0700426 }
427
428 /**
Saurav Das4ce45962015-11-24 23:21:05 -0800429 * Returns the next objective of type simple associated with the port on the
430 * device, given the treatment. Different treatments to the same port result
431 * in different next objectives. If no such objective exists, this method
432 * creates one and returns the id. Optionally metadata can be passed in for
433 * the creation of the objective.
434 *
435 * @param portNum the port number for the simple next objective
436 * @param treatment the actions to apply on the packets (should include outport)
437 * @param meta optional metadata passed into the creation of the next objective
438 * @return int if found or created, -1 if there are errors during the
439 * creation of the next objective.
440 */
441 public int getPortNextObjectiveId(PortNumber portNum, TrafficTreatment treatment,
442 TrafficSelector meta) {
443 Integer nextId = portNextObjStore.
444 get(new PortNextObjectiveStoreKey(deviceId, portNum, treatment));
445 if (nextId == null) {
446 log.trace("getPortNextObjectiveId in device{}: Next objective id "
447 + "not found for {} and {} creating", deviceId, portNum);
448 createGroupFromPort(portNum, treatment, meta);
449 nextId = portNextObjStore.get(
450 new PortNextObjectiveStoreKey(deviceId, portNum, treatment));
451 if (nextId == null) {
452 log.warn("getPortNextObjectiveId: unable to create next obj"
453 + "for dev:{} port{}", deviceId, portNum);
454 return -1;
455 }
456 }
457 return nextId;
458 }
459
460 /**
sangho0b2b6d12015-05-20 22:16:38 -0700461 * Checks if the next objective ID (group) for the neighbor set exists or not.
462 *
463 * @param ns neighbor set to check
464 * @return true if it exists, false otherwise
465 */
466 public boolean hasNextObjectiveId(NeighborSet ns) {
467 Integer nextId = nsNextObjStore.
468 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
469 if (nextId == null) {
470 return false;
471 }
472
473 return true;
474 }
475
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700476 // Empty implementation
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800477 protected void newNeighbor(Link newLink) {
478 }
479
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700480 // Empty implementation
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800481 protected void newPortToExistingNeighbor(Link newLink) {
482 }
483
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700484 // Empty implementation
485 protected Set<NeighborSet>
486 computeImpactedNeighborsetForPortEvent(DeviceId impactedNeighbor,
487 Set<DeviceId> updatedNeighbors) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800488 return null;
489 }
490
491 private void populateNeighborMaps() {
492 Set<Link> outgoingLinks = linkService.getDeviceEgressLinks(deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700493 for (Link link : outgoingLinks) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800494 if (link.type() != Link.Type.DIRECT) {
495 continue;
496 }
497 addNeighborAtPort(link.dst().deviceId(), link.src().port());
498 }
499 }
500
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700501 protected void addNeighborAtPort(DeviceId neighborId,
502 PortNumber portToNeighbor) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800503 // Update DeviceToPort database
504 log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
505 deviceId, neighborId, portToNeighbor);
Saurav Das8a0732e2015-11-20 15:27:53 -0800506 Set<PortNumber> ports = Collections
507 .newSetFromMap(new ConcurrentHashMap<PortNumber, Boolean>());
508 ports.add(portToNeighbor);
509 Set<PortNumber> portnums = devicePortMap.putIfAbsent(neighborId, ports);
510 if (portnums != null) {
511 portnums.add(portToNeighbor);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800512 }
513
514 // Update portToDevice database
Saurav Das8a0732e2015-11-20 15:27:53 -0800515 DeviceId prev = portDeviceMap.putIfAbsent(portToNeighbor, neighborId);
516 if (prev != null) {
517 log.warn("Device: {} port: {} has neighbor: {}. NOT updating "
518 + "to neighbor: {}", deviceId, portToNeighbor, prev, neighborId);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800519 }
520 }
521
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700522 protected Set<Set<DeviceId>> getPowerSetOfNeighbors(Set<DeviceId> neighbors) {
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700523 List<DeviceId> list = new ArrayList<>(neighbors);
524 Set<Set<DeviceId>> sets = new HashSet<>();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800525 // get the number of elements in the neighbors
526 int elements = list.size();
527 // the number of members of a power set is 2^n
528 // including the empty set
529 int powerElements = (1 << elements);
530
531 // run a binary counter for the number of power elements
532 // NOTE: Exclude empty set
533 for (long i = 1; i < powerElements; i++) {
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700534 Set<DeviceId> neighborSubSet = new HashSet<>();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800535 for (int j = 0; j < elements; j++) {
536 if ((i >> j) % 2 == 1) {
537 neighborSubSet.add(list.get(j));
538 }
539 }
540 sets.add(neighborSubSet);
541 }
542 return sets;
543 }
544
545 private boolean isSegmentIdSameAsNodeSegmentId(DeviceId deviceId, int sId) {
Charles Chan0b4e6182015-11-03 10:42:14 -0800546 int segmentId;
547 try {
548 segmentId = deviceConfig.getSegmentId(deviceId);
549 } catch (DeviceConfigNotFoundException e) {
550 log.warn(e.getMessage() + " Aborting isSegmentIdSameAsNodeSegmentId.");
551 return false;
552 }
553
554 return segmentId == sId;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800555 }
556
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700557 protected List<Integer> getSegmentIdsTobePairedWithNeighborSet(Set<DeviceId> neighbors) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800558
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700559 List<Integer> nsSegmentIds = new ArrayList<>();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800560
sanghob35a6192015-04-01 13:05:26 -0700561 // Always pair up with no edge label
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700562 // If (neighbors.size() == 1) {
sanghob35a6192015-04-01 13:05:26 -0700563 nsSegmentIds.add(-1);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700564 // }
sanghob35a6192015-04-01 13:05:26 -0700565
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800566 // Filter out SegmentIds matching with the
567 // nodes in the combo
568 for (Integer sId : allSegmentIds) {
569 if (sId.equals(nodeSegmentId)) {
570 continue;
571 }
572 boolean filterOut = false;
573 // Check if the edge label being set is of
574 // any node in the Neighbor set
575 for (DeviceId deviceId : neighbors) {
576 if (isSegmentIdSameAsNodeSegmentId(deviceId, sId)) {
577 filterOut = true;
578 break;
579 }
580 }
581 if (!filterOut) {
582 nsSegmentIds.add(sId);
583 }
584 }
585 return nsSegmentIds;
586 }
587
sangho1e575652015-05-14 00:39:53 -0700588 /**
589 * Creates Groups from a set of NeighborSet given.
590 *
591 * @param nsSet a set of NeighborSet
Saurav Das8a0732e2015-11-20 15:27:53 -0800592 * @param meta metadata passed into the creation of a Next Objective
sangho1e575652015-05-14 00:39:53 -0700593 */
Saurav Das8a0732e2015-11-20 15:27:53 -0800594 public void createGroupsFromNeighborsets(Set<NeighborSet> nsSet,
595 TrafficSelector meta) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800596 for (NeighborSet ns : nsSet) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700597 int nextId = flowObjectiveService.allocateNextId();
598 NextObjective.Builder nextObjBuilder = DefaultNextObjective
599 .builder().withId(nextId)
600 .withType(NextObjective.Type.HASHED).fromApp(appId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800601 for (DeviceId neighborId : ns.getDeviceIds()) {
602 if (devicePortMap.get(neighborId) == null) {
603 log.warn("Neighbor {} is not in the port map yet for dev:{}",
604 neighborId, deviceId);
sangho834e4b02015-05-01 09:38:25 -0700605 return;
Saurav Das8a0732e2015-11-20 15:27:53 -0800606 } else if (devicePortMap.get(neighborId).size() == 0) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700607 log.warn("There are no ports for "
Saurav Das8a0732e2015-11-20 15:27:53 -0800608 + "the Device {} in the port map yet", neighborId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700609 return;
sangho834e4b02015-05-01 09:38:25 -0700610 }
611
Saurav Das8a0732e2015-11-20 15:27:53 -0800612 MacAddress neighborMac;
Charles Chan0b4e6182015-11-03 10:42:14 -0800613 try {
Saurav Das8a0732e2015-11-20 15:27:53 -0800614 neighborMac = deviceConfig.getDeviceMac(neighborId);
Charles Chan0b4e6182015-11-03 10:42:14 -0800615 } catch (DeviceConfigNotFoundException e) {
616 log.warn(e.getMessage() + " Aborting createGroupsFromNeighborsets.");
617 return;
618 }
619
Saurav Das8a0732e2015-11-20 15:27:53 -0800620 for (PortNumber sp : devicePortMap.get(neighborId)) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700621 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
622 .builder();
Saurav Das8a0732e2015-11-20 15:27:53 -0800623 tBuilder.setEthDst(neighborMac)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700624 .setEthSrc(nodeMacAddr);
625 if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800626 tBuilder.pushMpls()
627 .copyTtlOut()
628 .setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700629 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800630 tBuilder.setOutput(sp);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700631 nextObjBuilder.addTreatment(tBuilder.build());
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800632 }
633 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800634 if (meta != null) {
Saurav Das4ce45962015-11-24 23:21:05 -0800635 nextObjBuilder.withMeta(meta);
Saurav Das8a0732e2015-11-20 15:27:53 -0800636 }
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -0700637 NextObjective nextObj = nextObjBuilder.
638 add(new SRNextObjectiveContext(deviceId));
Saurav Das8a0732e2015-11-20 15:27:53 -0800639 log.info("**createGroupsFromNeighborsets: Submited "
640 + "next objective {} in device {}",
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700641 nextId, deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800642 flowObjectiveService.next(deviceId, nextObj);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700643 nsNextObjStore.put(new NeighborSetNextObjectiveStoreKey(deviceId, ns),
644 nextId);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800645 }
646 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700647
Saurav Das4ce45962015-11-24 23:21:05 -0800648 /**
649 * Creates broadcast groups for all ports in the same configured subnet.
650 *
651 */
Charles Chanc42e84e2015-10-20 16:24:19 -0700652 public void createGroupsFromSubnetConfig() {
653 Map<Ip4Prefix, List<PortNumber>> subnetPortMap =
654 this.deviceConfig.getSubnetPortsMap(this.deviceId);
Charles Chanc42e84e2015-10-20 16:24:19 -0700655 // Construct a broadcast group for each subnet
656 subnetPortMap.forEach((subnet, ports) -> {
Charles Chan9f676b62015-10-29 14:58:10 -0700657 SubnetNextObjectiveStoreKey key =
658 new SubnetNextObjectiveStoreKey(deviceId, subnet);
659
660 if (subnetNextObjStore.containsKey(key)) {
661 log.debug("Broadcast group for device {} and subnet {} exists",
662 deviceId, subnet);
663 return;
664 }
665
Charles Chanc42e84e2015-10-20 16:24:19 -0700666 int nextId = flowObjectiveService.allocateNextId();
667
668 NextObjective.Builder nextObjBuilder = DefaultNextObjective
669 .builder().withId(nextId)
670 .withType(NextObjective.Type.BROADCAST).fromApp(appId);
671
672 ports.forEach(port -> {
673 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Saurav Das4f980082015-11-05 13:39:15 -0800674 tBuilder.popVlan();
Charles Chanc42e84e2015-10-20 16:24:19 -0700675 tBuilder.setOutput(port);
676 nextObjBuilder.addTreatment(tBuilder.build());
677 });
678
679 NextObjective nextObj = nextObjBuilder.add();
680 flowObjectiveService.next(deviceId, nextObj);
681 log.debug("createGroupFromSubnetConfig: Submited "
682 + "next objective {} in device {}",
683 nextId, deviceId);
Charles Chan9f676b62015-10-29 14:58:10 -0700684
Charles Chanc42e84e2015-10-20 16:24:19 -0700685 subnetNextObjStore.put(key, nextId);
686 });
687 }
688
Saurav Das4ce45962015-11-24 23:21:05 -0800689
690 /**
691 * Create simple next objective for a single port. The treatments can include
692 * all outgoing actions that need to happen on the packet.
693 *
694 * @param portNum the outgoing port on the device
695 * @param treatment the actions to apply on the packets (should include outport)
696 * @param meta optional data to pass to the driver
697 */
698 public void createGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
699 TrafficSelector meta) {
700 int nextId = flowObjectiveService.allocateNextId();
701 PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
702 deviceId, portNum, treatment);
703
704 NextObjective.Builder nextObjBuilder = DefaultNextObjective
705 .builder().withId(nextId)
706 .withType(NextObjective.Type.SIMPLE)
707 .addTreatment(treatment)
708 .fromApp(appId)
709 .withMeta(meta);
710
711 NextObjective nextObj = nextObjBuilder.add();
712 flowObjectiveService.next(deviceId, nextObj);
713 log.debug("createGroupFromPort: Submited next objective {} in device {} "
714 + "for port {}", nextId, deviceId, portNum);
715
716 portNextObjStore.put(key, nextId);
717 }
718
719
sanghob35a6192015-04-01 13:05:26 -0700720 public GroupKey getGroupKey(Object obj) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700721 return new DefaultGroupKey(kryo.build().serialize(obj));
722 }
sanghob35a6192015-04-01 13:05:26 -0700723
sangho1e575652015-05-14 00:39:53 -0700724 /**
725 * Removes groups for the next objective ID given.
726 *
727 * @param objectiveId next objective ID to remove
728 * @return true if succeeds, false otherwise
729 */
730 public boolean removeGroup(int objectiveId) {
731
732 if (nsNextObjStore.containsValue(objectiveId)) {
733 NextObjective.Builder nextObjBuilder = DefaultNextObjective
734 .builder().withId(objectiveId)
735 .withType(NextObjective.Type.HASHED).fromApp(appId);
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -0700736 NextObjective nextObjective = nextObjBuilder.
737 remove(new SRNextObjectiveContext(deviceId));
Saurav Das8a0732e2015-11-20 15:27:53 -0800738 log.info("**removeGroup: Submited "
739 + "next objective {} in device {}",
740 objectiveId, deviceId);
sangho1e575652015-05-14 00:39:53 -0700741 flowObjectiveService.next(deviceId, nextObjective);
742
743 for (Map.Entry<NeighborSetNextObjectiveStoreKey, Integer> entry: nsNextObjStore.entrySet()) {
744 if (entry.getValue().equals(objectiveId)) {
745 nsNextObjStore.remove(entry.getKey());
746 break;
747 }
748 }
sangho0b2b6d12015-05-20 22:16:38 -0700749 return true;
sangho1e575652015-05-14 00:39:53 -0700750 }
751
752 return false;
753 }
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -0700754
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 }
770
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -0700771 protected static class SRNextObjectiveContext implements ObjectiveContext {
772 final DeviceId deviceId;
773
774 SRNextObjectiveContext(DeviceId deviceId) {
775 this.deviceId = deviceId;
776 }
777 @Override
778 public void onSuccess(Objective objective) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800779 log.info("Next objective {} operation successful in device {}",
780 objective.id(), deviceId);
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -0700781 }
782
783 @Override
784 public void onError(Objective objective, ObjectiveError error) {
785 log.warn("Next objective {} operation failed with error: {} in device {}",
Saurav Das8a0732e2015-11-20 15:27:53 -0800786 objective.id(), error, deviceId);
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -0700787 }
788 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800789}