blob: c0129991a9b06686ad837dcf7e0691bc13aa81fe [file] [log] [blame]
Jonathan Hartf5829202015-02-12 09:37:02 -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 */
16package org.onosproject.bgprouter;
17
alshabib910aff12015-04-09 16:55:57 -070018import com.google.common.collect.ConcurrentHashMultiset;
19import com.google.common.collect.HashMultimap;
20import com.google.common.collect.Maps;
21import com.google.common.collect.Multimap;
22import com.google.common.collect.Multiset;
Saurav Dascfd63d22015-04-13 16:08:24 -070023
Jonathan Hartf5829202015-02-12 09:37:02 -080024import org.apache.felix.scr.annotations.Activate;
25import org.apache.felix.scr.annotations.Component;
26import org.apache.felix.scr.annotations.Deactivate;
27import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
29import org.onlab.packet.Ethernet;
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -070030import org.onlab.packet.Ip4Address;
31import org.onlab.packet.Ip6Address;
Jonathan Hart7baba072015-02-23 14:27:59 -080032import org.onlab.packet.IpAddress;
33import org.onlab.packet.IpPrefix;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070034import org.onlab.util.KryoNamespace;
Jonathan Hart54b406b2015-03-06 16:24:14 -080035import org.onosproject.config.NetworkConfigService;
Jonathan Hartf5829202015-02-12 09:37:02 -080036import org.onosproject.core.ApplicationId;
37import org.onosproject.core.CoreService;
38import org.onosproject.net.DeviceId;
39import org.onosproject.net.flow.DefaultFlowRule;
40import org.onosproject.net.flow.DefaultTrafficSelector;
41import org.onosproject.net.flow.DefaultTrafficTreatment;
42import org.onosproject.net.flow.FlowRule;
alshabib10580802015-02-18 18:30:33 -080043import org.onosproject.net.flow.FlowRuleOperations;
Jonathan Hartf5829202015-02-12 09:37:02 -080044import org.onosproject.net.flow.FlowRuleService;
45import org.onosproject.net.flow.TrafficSelector;
46import org.onosproject.net.flow.TrafficTreatment;
alshabib910aff12015-04-09 16:55:57 -070047import org.onosproject.net.flow.criteria.Criteria;
48import org.onosproject.net.flowobjective.DefaultFilteringObjective;
49import org.onosproject.net.flowobjective.FilteringObjective;
50import org.onosproject.net.flowobjective.FlowObjectiveService;
Jonathan Hartf5829202015-02-12 09:37:02 -080051import org.onosproject.net.group.DefaultGroupBucket;
52import org.onosproject.net.group.DefaultGroupDescription;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070053import org.onosproject.net.group.DefaultGroupKey;
Jonathan Hartf5829202015-02-12 09:37:02 -080054import org.onosproject.net.group.Group;
55import org.onosproject.net.group.GroupBucket;
56import org.onosproject.net.group.GroupBuckets;
57import org.onosproject.net.group.GroupDescription;
Jonathan Hart7baba072015-02-23 14:27:59 -080058import org.onosproject.net.group.GroupEvent;
Jonathan Hart7baba072015-02-23 14:27:59 -080059import org.onosproject.net.group.GroupListener;
Jonathan Hartf5829202015-02-12 09:37:02 -080060import org.onosproject.net.group.GroupService;
61import org.onosproject.net.packet.PacketService;
Jonathan Hart7baba072015-02-23 14:27:59 -080062import org.onosproject.routing.FibEntry;
Jonathan Hart2da1e602015-02-18 19:09:24 -080063import org.onosproject.routing.FibListener;
64import org.onosproject.routing.FibUpdate;
65import org.onosproject.routing.RoutingService;
Saurav Dasfbe25c52015-03-04 11:12:00 -080066import org.onosproject.routing.config.BgpSpeaker;
Jonathan Hart2da1e602015-02-18 19:09:24 -080067import org.onosproject.routing.config.Interface;
68import org.onosproject.routing.config.RoutingConfigurationService;
Jonathan Hartf5829202015-02-12 09:37:02 -080069import org.slf4j.Logger;
70import org.slf4j.LoggerFactory;
71
alshabib910aff12015-04-09 16:55:57 -070072import java.util.Collection;
73import java.util.Collections;
74import java.util.HashMap;
alshabib910aff12015-04-09 16:55:57 -070075import java.util.Map;
76import java.util.Set;
77import java.util.stream.Collectors;
Jonathan Hartf5829202015-02-12 09:37:02 -080078
79/**
80 * BgpRouter component.
81 */
82@Component(immediate = true)
83public class BgpRouter {
84
85 private static final Logger log = LoggerFactory.getLogger(BgpRouter.class);
86
87 private static final String BGP_ROUTER_APP = "org.onosproject.bgprouter";
88
Jonathan Hart12ef2052015-03-10 13:58:13 -070089 private static final int PRIORITY_OFFSET = 100;
90 private static final int PRIORITY_MULTIPLIER = 5;
Jonathan Hartf5829202015-02-12 09:37:02 -080091
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected CoreService coreService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected FlowRuleService flowService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected GroupService groupService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected RoutingService routingService;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected RoutingConfigurationService configService;
106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
108 protected PacketService packetService;
109
Saurav Dasfbe25c52015-03-04 11:12:00 -0800110 //
111 // NOTE: Unused reference - needed to guarantee that the
112 // NetworkConfigReader component is activated and the network configuration
113 // is read.
114 //
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected NetworkConfigService networkConfigService;
117
Jonathan Hartf5829202015-02-12 09:37:02 -0800118 private ApplicationId appId;
119
Jonathan Hart7baba072015-02-23 14:27:59 -0800120 // Reference count for how many times a next hop is used by a route
121 private final Multiset<IpAddress> nextHopsCount = ConcurrentHashMultiset.create();
122
123 // Mapping from prefix to its current next hop
124 private final Map<IpPrefix, IpAddress> prefixToNextHop = Maps.newHashMap();
125
126 // Mapping from next hop IP to next hop object containing group info
127 private final Map<IpAddress, NextHop> nextHops = Maps.newHashMap();
128
129 // Stores FIB updates that are waiting for groups to be set up
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700130 private final Multimap<NextHopGroupKey, FibEntry> pendingUpdates = HashMultimap.create();
Jonathan Hartf5829202015-02-12 09:37:02 -0800131
Saurav Dasfbe25c52015-03-04 11:12:00 -0800132 // Device id of data-plane switch - should be learned from config
133 private DeviceId deviceId;
134
135 // Device id of control-plane switch (OVS) connected to BGP Speaker - should be
136 // learned from config
137 private DeviceId ctrlDeviceId;
Jonathan Hartf5829202015-02-12 09:37:02 -0800138
Jonathan Hart7baba072015-02-23 14:27:59 -0800139 private final GroupListener groupListener = new InternalGroupListener();
140
Jonathan Hartf5829202015-02-12 09:37:02 -0800141 private TunnellingConnectivityManager connectivityManager;
142
sangho5eaf0332015-03-09 15:08:12 -0700143 private IcmpHandler icmpHandler;
144
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700145 private KryoNamespace appKryo = new KryoNamespace.Builder()
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700146 .register(IpAddress.Version.class)
147 .register(IpAddress.class)
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700148 .register(Ip4Address.class)
149 .register(Ip6Address.class)
150 .register(byte[].class)
151 .register(NextHopGroupKey.class)
152 .build();
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700153
alshabib910aff12015-04-09 16:55:57 -0700154
155 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
156 protected FlowObjectiveService flowObjectiveService;
157
Jonathan Hartf5829202015-02-12 09:37:02 -0800158 @Activate
159 protected void activate() {
Jonathan Hartf5829202015-02-12 09:37:02 -0800160 appId = coreService.registerApplication(BGP_ROUTER_APP);
Saurav Dasfbe25c52015-03-04 11:12:00 -0800161 getDeviceConfiguration(configService.getBgpSpeakers());
Jonathan Hartf5829202015-02-12 09:37:02 -0800162
Jonathan Hart7baba072015-02-23 14:27:59 -0800163 groupService.addListener(groupListener);
164
Saurav Dascfd63d22015-04-13 16:08:24 -0700165 processIntfFilters(true, configService.getInterfaces());
alshabib10580802015-02-18 18:30:33 -0800166
Jonathan Hartf5829202015-02-12 09:37:02 -0800167 connectivityManager = new TunnellingConnectivityManager(appId,
168 configService,
Jonathan Hart936a7292015-03-06 18:02:57 -0800169 packetService,
170 flowService);
Jonathan Hartf5829202015-02-12 09:37:02 -0800171
sangho5eaf0332015-03-09 15:08:12 -0700172 icmpHandler = new IcmpHandler(configService, packetService);
173
Pingping Line28ae4c2015-03-13 11:37:03 -0700174 routingService.addFibListener(new InternalFibListener());
175 routingService.start();
Jonathan Hartf5829202015-02-12 09:37:02 -0800176
177 connectivityManager.start();
178
sangho5eaf0332015-03-09 15:08:12 -0700179 icmpHandler.start();
180
Jonathan Hartf5829202015-02-12 09:37:02 -0800181 log.info("BgpRouter started");
182 }
183
184 @Deactivate
185 protected void deactivate() {
186 routingService.stop();
187 connectivityManager.stop();
sangho5eaf0332015-03-09 15:08:12 -0700188 icmpHandler.stop();
Saurav Dascfd63d22015-04-13 16:08:24 -0700189 processIntfFilters(false, configService.getInterfaces());
Jonathan Hartf5829202015-02-12 09:37:02 -0800190
Jonathan Hart7baba072015-02-23 14:27:59 -0800191 groupService.removeListener(groupListener);
192
Jonathan Hartf5829202015-02-12 09:37:02 -0800193 log.info("BgpRouter stopped");
194 }
195
Saurav Dasfbe25c52015-03-04 11:12:00 -0800196 private void getDeviceConfiguration(Map<String, BgpSpeaker> bgps) {
197 if (bgps == null || bgps.values().isEmpty()) {
198 log.error("BGP speakers configuration is missing");
199 return;
200 }
201 for (BgpSpeaker s : bgps.values()) {
202 ctrlDeviceId = s.connectPoint().deviceId();
203 if (s.interfaceAddresses() == null || s.interfaceAddresses().isEmpty()) {
204 log.error("BGP Router must have interfaces configured");
205 return;
206 }
207 deviceId = s.interfaceAddresses().get(0).connectPoint().deviceId();
208 break;
209 }
sangho5eaf0332015-03-09 15:08:12 -0700210
Saurav Dasfbe25c52015-03-04 11:12:00 -0800211 log.info("Router dpid: {}", deviceId);
212 log.info("Control Plane OVS dpid: {}", ctrlDeviceId);
213 }
214
Jonathan Hartf5829202015-02-12 09:37:02 -0800215 private void updateFibEntry(Collection<FibUpdate> updates) {
Jonathan Hart5b141422015-03-06 12:59:09 -0800216 Map<FibEntry, Group> toInstall = new HashMap<>(updates.size());
217
Jonathan Hartf5829202015-02-12 09:37:02 -0800218 for (FibUpdate update : updates) {
Jonathan Hart7baba072015-02-23 14:27:59 -0800219 FibEntry entry = update.entry();
Jonathan Hartf5829202015-02-12 09:37:02 -0800220
Jonathan Hart7baba072015-02-23 14:27:59 -0800221 addNextHop(entry);
Jonathan Hartf5829202015-02-12 09:37:02 -0800222
Jonathan Hart7baba072015-02-23 14:27:59 -0800223 Group group;
224 synchronized (pendingUpdates) {
225 NextHop nextHop = nextHops.get(entry.nextHopIp());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700226 group = groupService.getGroup(deviceId,
227 new DefaultGroupKey(
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700228 appKryo.serialize(nextHop.group())));
Jonathan Hartf5829202015-02-12 09:37:02 -0800229
Jonathan Hart7baba072015-02-23 14:27:59 -0800230 if (group == null) {
231 log.debug("Adding pending flow {}", update.entry());
232 pendingUpdates.put(nextHop.group(), update.entry());
233 continue;
234 }
Jonathan Hartf5829202015-02-12 09:37:02 -0800235 }
236
Jonathan Hart5b141422015-03-06 12:59:09 -0800237 toInstall.put(update.entry(), group);
Jonathan Hartf5829202015-02-12 09:37:02 -0800238 }
Jonathan Hart5b141422015-03-06 12:59:09 -0800239
240 installFlows(toInstall);
Jonathan Hartf5829202015-02-12 09:37:02 -0800241 }
242
Jonathan Hart5b141422015-03-06 12:59:09 -0800243 private void installFlows(Map<FibEntry, Group> entriesToInstall) {
244 FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
Jonathan Hart7baba072015-02-23 14:27:59 -0800245
Jonathan Hart5b141422015-03-06 12:59:09 -0800246 for (Map.Entry<FibEntry, Group> entry : entriesToInstall.entrySet()) {
247 FibEntry fibEntry = entry.getKey();
248 Group group = entry.getValue();
249
250 FlowRule flowRule = generateRibFlowRule(fibEntry.prefix(), group);
251
252 builder.add(flowRule);
253 }
254
255 flowService.apply(builder.build());
Jonathan Hart7baba072015-02-23 14:27:59 -0800256 }
257
258 private synchronized void deleteFibEntry(Collection<FibUpdate> withdraws) {
Jonathan Hart5b141422015-03-06 12:59:09 -0800259 FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
260
Jonathan Hart7baba072015-02-23 14:27:59 -0800261 for (FibUpdate update : withdraws) {
262 FibEntry entry = update.entry();
263
Jonathan Hart37d659c2015-03-08 19:20:38 -0700264 Group group = deleteNextHop(entry.prefix());
265 if (group == null) {
266 log.warn("Group not found when deleting {}", entry);
267 return;
268 }
Jonathan Hartf5829202015-02-12 09:37:02 -0800269
Jonathan Hart37d659c2015-03-08 19:20:38 -0700270 FlowRule flowRule = generateRibFlowRule(entry.prefix(), group);
Jonathan Hartf5829202015-02-12 09:37:02 -0800271
Jonathan Hart5b141422015-03-06 12:59:09 -0800272 builder.remove(flowRule);
Jonathan Hartf5829202015-02-12 09:37:02 -0800273 }
Jonathan Hart5b141422015-03-06 12:59:09 -0800274
275 flowService.apply(builder.build());
Jonathan Hartf5829202015-02-12 09:37:02 -0800276 }
277
Jonathan Hart37d659c2015-03-08 19:20:38 -0700278 private FlowRule generateRibFlowRule(IpPrefix prefix, Group group) {
279 TrafficSelector selector = DefaultTrafficSelector.builder()
280 .matchEthType(Ethernet.TYPE_IPV4)
281 .matchIPDst(prefix)
282 .build();
283
284 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
285 .group(group.id())
286 .build();
287
Jonathan Hart12ef2052015-03-10 13:58:13 -0700288
289 int priority = prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET;
290
Jonathan Hart37d659c2015-03-08 19:20:38 -0700291 return new DefaultFlowRule(deviceId, selector, treatment,
Jonathan Hart12ef2052015-03-10 13:58:13 -0700292 priority, appId, 0, true,
Jonathan Hart37d659c2015-03-08 19:20:38 -0700293 FlowRule.Type.IP);
294 }
295
Jonathan Hart7baba072015-02-23 14:27:59 -0800296 private synchronized void addNextHop(FibEntry entry) {
297 prefixToNextHop.put(entry.prefix(), entry.nextHopIp());
298 if (nextHopsCount.count(entry.nextHopIp()) == 0) {
Jonathan Hartf5829202015-02-12 09:37:02 -0800299 // There was no next hop in the multiset
300
Jonathan Hart7baba072015-02-23 14:27:59 -0800301 Interface egressIntf = configService.getMatchingInterface(entry.nextHopIp());
Jonathan Hartf5829202015-02-12 09:37:02 -0800302 if (egressIntf == null) {
Jonathan Hart7baba072015-02-23 14:27:59 -0800303 log.warn("no egress interface found for {}", entry);
Jonathan Hartf5829202015-02-12 09:37:02 -0800304 return;
305 }
306
Jonathan Hart7baba072015-02-23 14:27:59 -0800307 NextHopGroupKey groupKey = new NextHopGroupKey(entry.nextHopIp());
308
309 NextHop nextHop = new NextHop(entry.nextHopIp(), entry.nextHopMac(), groupKey);
Jonathan Hartf5829202015-02-12 09:37:02 -0800310
311 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
312 .setEthSrc(egressIntf.mac())
313 .setEthDst(nextHop.mac())
Jonathan Hart54b406b2015-03-06 16:24:14 -0800314 .pushVlan()
Jonathan Hartf5829202015-02-12 09:37:02 -0800315 .setVlanId(egressIntf.vlan())
alshabibda1644e2015-03-13 14:01:35 -0700316 .setVlanPcp((byte) 0)
Jonathan Hartf5829202015-02-12 09:37:02 -0800317 .setOutput(egressIntf.connectPoint().port())
318 .build();
319
320 GroupBucket bucket = DefaultGroupBucket.createIndirectGroupBucket(treatment);
321
322 GroupDescription groupDescription
323 = new DefaultGroupDescription(deviceId,
324 GroupDescription.Type.INDIRECT,
325 new GroupBuckets(Collections
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700326 .singletonList(bucket)),
327 new DefaultGroupKey(appKryo.serialize(groupKey)),
Jonathan Hartf5829202015-02-12 09:37:02 -0800328 appId);
329
330 groupService.addGroup(groupDescription);
Jonathan Hart7baba072015-02-23 14:27:59 -0800331
332 nextHops.put(nextHop.ip(), nextHop);
333
Jonathan Hartf5829202015-02-12 09:37:02 -0800334 }
Jonathan Hart7baba072015-02-23 14:27:59 -0800335
336 nextHopsCount.add(entry.nextHopIp());
Jonathan Hartf5829202015-02-12 09:37:02 -0800337 }
338
Jonathan Hart37d659c2015-03-08 19:20:38 -0700339 private synchronized Group deleteNextHop(IpPrefix prefix) {
Jonathan Hart7baba072015-02-23 14:27:59 -0800340 IpAddress nextHopIp = prefixToNextHop.remove(prefix);
341 NextHop nextHop = nextHops.get(nextHopIp);
342 if (nextHop == null) {
343 log.warn("No next hop found when removing prefix {}", prefix);
Jonathan Hart37d659c2015-03-08 19:20:38 -0700344 return null;
Jonathan Hart7baba072015-02-23 14:27:59 -0800345 }
346
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700347 Group group = groupService.getGroup(deviceId,
348 new DefaultGroupKey(appKryo.
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700349 serialize(nextHop.group())));
Jonathan Hart37d659c2015-03-08 19:20:38 -0700350
Jonathan Hartf9f2cbb2015-03-12 17:44:03 -0700351 // FIXME disabling group deletes for now until we verify the logic is OK
352 /*if (nextHopsCount.remove(nextHopIp, 1) <= 1) {
Jonathan Hartf5829202015-02-12 09:37:02 -0800353 // There was one or less next hops, so there are now none
354
Jonathan Hart7baba072015-02-23 14:27:59 -0800355 log.debug("removing group for next hop {}", nextHop);
Jonathan Hartf5829202015-02-12 09:37:02 -0800356
Jonathan Hart7baba072015-02-23 14:27:59 -0800357 nextHops.remove(nextHopIp);
358
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700359 groupService.removeGroup(deviceId,
360 new DefaultGroupKey(appKryo.build().serialize(nextHop.group())),
361 appId);
Jonathan Hartf9f2cbb2015-03-12 17:44:03 -0700362 }*/
Jonathan Hart37d659c2015-03-08 19:20:38 -0700363
364 return group;
Jonathan Hartf5829202015-02-12 09:37:02 -0800365 }
366
367 private class InternalFibListener implements FibListener {
368
369 @Override
370 public void update(Collection<FibUpdate> updates,
371 Collection<FibUpdate> withdraws) {
372 BgpRouter.this.deleteFibEntry(withdraws);
373 BgpRouter.this.updateFibEntry(updates);
374 }
375 }
alshabib10580802015-02-18 18:30:33 -0800376
Saurav Dascfd63d22015-04-13 16:08:24 -0700377 private void processIntfFilters(boolean install, Set<Interface> intfs) {
378 log.info("Processing {} router interfaces", intfs.size());
379 for (Interface intf : intfs) {
380 FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
381 fob.withKey(Criteria.matchInPort(intf.connectPoint().port()))
382 .addCondition(Criteria.matchEthDst(intf.mac()))
383 .addCondition(Criteria.matchVlanId(intf.vlan()));
384 intf.ipAddresses().stream()
385 .forEach(ipaddr -> fob.addCondition(
386 Criteria.matchIPDst(ipaddr.subnetAddress())));
387 fob.permit().fromApp(appId);
388 flowObjectiveService.filter(deviceId,
389 Collections.singletonList(fob.add()));
alshabib10580802015-02-18 18:30:33 -0800390 }
Jonathan Hart7baba072015-02-23 14:27:59 -0800391 }
alshabib10580802015-02-18 18:30:33 -0800392
Jonathan Hart7baba072015-02-23 14:27:59 -0800393 private class InternalGroupListener implements GroupListener {
394
395 @Override
396 public void event(GroupEvent event) {
397 Group group = event.subject();
398
399 if (event.type() == GroupEvent.Type.GROUP_ADDED ||
400 event.type() == GroupEvent.Type.GROUP_UPDATED) {
401 synchronized (pendingUpdates) {
Jonathan Hart5b141422015-03-06 12:59:09 -0800402
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700403 NextHopGroupKey nhGroupKey =
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700404 appKryo.deserialize(group.appCookie().key());
Jonathan Hart5b141422015-03-06 12:59:09 -0800405 Map<FibEntry, Group> entriesToInstall =
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700406 pendingUpdates.removeAll(nhGroupKey)
Jonathan Hart5b141422015-03-06 12:59:09 -0800407 .stream()
408 .collect(Collectors
409 .toMap(e -> e, e -> group));
410
411 installFlows(entriesToInstall);
Jonathan Hart7baba072015-02-23 14:27:59 -0800412 }
413 }
414 }
alshabib10580802015-02-18 18:30:33 -0800415 }
Jonathan Hartf5829202015-02-12 09:37:02 -0800416}