blob: 0098d019135b80defd7da72b27063dd3b9431fe4 [file] [log] [blame]
Brian O'Connor7cbbbb72016-04-09 02:13:23 -07001/*
2 * Copyright 2016-present 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 */
Charles Chan188ebf52015-12-23 00:15:11 -080016package org.onosproject.driver.pipeline;
17
18import com.google.common.cache.Cache;
19import com.google.common.cache.CacheBuilder;
20import com.google.common.cache.RemovalCause;
21import com.google.common.cache.RemovalNotification;
Charles Chan5b9df8d2016-03-28 22:21:40 -070022import com.google.common.collect.ImmutableList;
23import com.google.common.collect.Lists;
Charles Chan188ebf52015-12-23 00:15:11 -080024import org.onlab.osgi.ServiceDirectory;
Charles Chan5b9df8d2016-03-28 22:21:40 -070025import org.onlab.packet.IpPrefix;
Charles Chan5270ed02016-01-30 23:22:37 -080026import org.onlab.packet.MacAddress;
Charles Chan188ebf52015-12-23 00:15:11 -080027import org.onlab.packet.MplsLabel;
28import org.onlab.packet.VlanId;
29import org.onosproject.core.ApplicationId;
30import org.onosproject.core.DefaultGroupId;
Charles Chan32562522016-04-07 14:37:14 -070031import org.onosproject.driver.extensions.OfdpaSetVlanVid;
Charles Chan188ebf52015-12-23 00:15:11 -080032import org.onosproject.net.DeviceId;
33import org.onosproject.net.PortNumber;
34import org.onosproject.net.behaviour.NextGroup;
35import org.onosproject.net.behaviour.PipelinerContext;
36import org.onosproject.net.flow.DefaultTrafficTreatment;
37import org.onosproject.net.flow.TrafficSelector;
38import org.onosproject.net.flow.TrafficTreatment;
39import org.onosproject.net.flow.criteria.Criterion;
40import org.onosproject.net.flow.criteria.VlanIdCriterion;
41import org.onosproject.net.flow.instructions.Instruction;
42import org.onosproject.net.flow.instructions.Instructions;
43import org.onosproject.net.flow.instructions.L2ModificationInstruction;
44import org.onosproject.net.flowobjective.FlowObjectiveStore;
45import org.onosproject.net.flowobjective.NextObjective;
46import org.onosproject.net.flowobjective.ObjectiveError;
47import org.onosproject.net.group.DefaultGroupBucket;
48import org.onosproject.net.group.DefaultGroupDescription;
49import org.onosproject.net.group.DefaultGroupKey;
50import org.onosproject.net.group.Group;
51import org.onosproject.net.group.GroupBucket;
52import org.onosproject.net.group.GroupBuckets;
53import org.onosproject.net.group.GroupDescription;
54import org.onosproject.net.group.GroupEvent;
55import org.onosproject.net.group.GroupKey;
56import org.onosproject.net.group.GroupListener;
57import org.onosproject.net.group.GroupService;
Saurav Das8be4e3a2016-03-11 17:19:07 -080058import org.onosproject.store.service.AtomicCounter;
59import org.onosproject.store.service.StorageService;
Charles Chan188ebf52015-12-23 00:15:11 -080060import org.slf4j.Logger;
61
62import java.util.ArrayDeque;
63import java.util.ArrayList;
64import java.util.Collection;
65import java.util.Collections;
66import java.util.Deque;
67import java.util.List;
Charles Chand0fd5dc2016-02-16 23:14:49 -080068import java.util.Objects;
Charles Chan188ebf52015-12-23 00:15:11 -080069import java.util.Set;
70import java.util.concurrent.ConcurrentHashMap;
71import java.util.concurrent.CopyOnWriteArrayList;
72import java.util.concurrent.Executors;
73import java.util.concurrent.ScheduledExecutorService;
74import java.util.concurrent.TimeUnit;
75import java.util.concurrent.atomic.AtomicInteger;
76import java.util.stream.Collectors;
77
78import static org.onlab.util.Tools.groupedThreads;
79import static org.slf4j.LoggerFactory.getLogger;
80
81/**
82 * Group handler for OFDPA2 pipeline.
83 */
Charles Chan361154b2016-03-24 10:23:39 -070084public class Ofdpa2GroupHandler {
Charles Chan188ebf52015-12-23 00:15:11 -080085 /*
86 * OFDPA requires group-id's to have a certain form.
87 * L2 Interface Groups have <4bits-0><12bits-vlanid><16bits-portid>
88 * L3 Unicast Groups have <4bits-2><28bits-index>
89 * MPLS Interface Groups have <4bits-9><4bits:0><24bits-index>
90 * L3 ECMP Groups have <4bits-7><28bits-index>
91 * L2 Flood Groups have <4bits-4><12bits-vlanid><16bits-index>
92 * L3 VPN Groups have <4bits-9><4bits-2><24bits-index>
93 */
Charles Chan425854b2016-04-11 15:32:12 -070094 protected static final int L2_INTERFACE_TYPE = 0x00000000;
95 protected static final int L3_INTERFACE_TYPE = 0x50000000;
96 protected static final int L3_UNICAST_TYPE = 0x20000000;
97 protected static final int L3_MULTICAST_TYPE = 0x60000000;
98 protected static final int MPLS_INTERFACE_TYPE = 0x90000000;
99 protected static final int MPLS_L3VPN_SUBTYPE = 0x92000000;
100 protected static final int L3_ECMP_TYPE = 0x70000000;
101 protected static final int L2_FLOOD_TYPE = 0x40000000;
Charles Chane849c192016-01-11 18:28:54 -0800102
Charles Chan425854b2016-04-11 15:32:12 -0700103 protected static final int TYPE_MASK = 0x0fffffff;
104 protected static final int SUBTYPE_MASK = 0x00ffffff;
105 protected static final int TYPE_VLAN_MASK = 0x0000ffff;
Charles Chane849c192016-01-11 18:28:54 -0800106
Charles Chan425854b2016-04-11 15:32:12 -0700107 protected static final int PORT_LOWER_BITS_MASK = 0x3f;
108 protected static final long PORT_HIGHER_BITS_MASK = ~PORT_LOWER_BITS_MASK;
Charles Chan188ebf52015-12-23 00:15:11 -0800109
110 private final Logger log = getLogger(getClass());
111 private ServiceDirectory serviceDirectory;
112 protected GroupService groupService;
Saurav Das8be4e3a2016-03-11 17:19:07 -0800113 protected StorageService storageService;
Charles Chan188ebf52015-12-23 00:15:11 -0800114
Charles Chan425854b2016-04-11 15:32:12 -0700115 protected DeviceId deviceId;
Charles Chan188ebf52015-12-23 00:15:11 -0800116 private FlowObjectiveStore flowObjectiveStore;
Charles Chanfc5c7802016-05-17 13:13:55 -0700117 private Cache<GroupKey, List<OfdpaNextGroup>> pendingAddNextObjectives;
118 private Cache<NextObjective, List<GroupKey>> pendingRemoveNextObjectives;
Charles Chan188ebf52015-12-23 00:15:11 -0800119 private ConcurrentHashMap<GroupKey, Set<GroupChainElem>> pendingGroups;
120 private ScheduledExecutorService groupChecker =
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700121 Executors.newScheduledThreadPool(2, groupedThreads("onos/pipeliner", "ofdpa2-%d", log));
Charles Chan188ebf52015-12-23 00:15:11 -0800122
123 // index number for group creation
Saurav Das8be4e3a2016-03-11 17:19:07 -0800124 private AtomicCounter nextIndex;
Charles Chan188ebf52015-12-23 00:15:11 -0800125
Charles Chan188ebf52015-12-23 00:15:11 -0800126 // local store for pending bucketAdds - by design there can only be one
127 // pending bucket for a group
128 protected ConcurrentHashMap<Integer, NextObjective> pendingBuckets = new ConcurrentHashMap<>();
129
130 protected void init(DeviceId deviceId, PipelinerContext context) {
131 this.deviceId = deviceId;
132 this.flowObjectiveStore = context.store();
133 this.serviceDirectory = context.directory();
134 this.groupService = serviceDirectory.get(GroupService.class);
Saurav Das8be4e3a2016-03-11 17:19:07 -0800135 this.storageService = serviceDirectory.get(StorageService.class);
Madan Jampanid5714e02016-04-19 14:15:20 -0700136 this.nextIndex = storageService.getAtomicCounter("group-id-index-counter");
Charles Chan188ebf52015-12-23 00:15:11 -0800137
Charles Chanfc5c7802016-05-17 13:13:55 -0700138 pendingAddNextObjectives = CacheBuilder.newBuilder()
Charles Chan188ebf52015-12-23 00:15:11 -0800139 .expireAfterWrite(20, TimeUnit.SECONDS)
140 .removalListener((
141 RemovalNotification<GroupKey, List<OfdpaNextGroup>> notification) -> {
142 if (notification.getCause() == RemovalCause.EXPIRED) {
143 notification.getValue().forEach(ofdpaNextGrp ->
Charles Chan361154b2016-03-24 10:23:39 -0700144 Ofdpa2Pipeline.fail(ofdpaNextGrp.nextObj,
Charles Chan188ebf52015-12-23 00:15:11 -0800145 ObjectiveError.GROUPINSTALLATIONFAILED));
Charles Chanfc5c7802016-05-17 13:13:55 -0700146 }
147 }).build();
Charles Chan188ebf52015-12-23 00:15:11 -0800148
Charles Chanfc5c7802016-05-17 13:13:55 -0700149 pendingRemoveNextObjectives = CacheBuilder.newBuilder()
150 .expireAfterWrite(20, TimeUnit.SECONDS)
151 .removalListener((
152 RemovalNotification<NextObjective, List<GroupKey>> notification) -> {
153 if (notification.getCause() == RemovalCause.EXPIRED) {
154 Ofdpa2Pipeline.fail(notification.getKey(),
155 ObjectiveError.GROUPREMOVALFAILED);
Charles Chan188ebf52015-12-23 00:15:11 -0800156 }
157 }).build();
158 pendingGroups = new ConcurrentHashMap<>();
159 groupChecker.scheduleAtFixedRate(new GroupChecker(), 0, 500, TimeUnit.MILLISECONDS);
160
161 groupService.addListener(new InnerGroupListener());
162 }
163
Saurav Das8be4e3a2016-03-11 17:19:07 -0800164 //////////////////////////////////////
165 // Group Creation
166 //////////////////////////////////////
167
Charles Chan188ebf52015-12-23 00:15:11 -0800168 protected void addGroup(NextObjective nextObjective) {
169 switch (nextObjective.type()) {
170 case SIMPLE:
171 Collection<TrafficTreatment> treatments = nextObjective.next();
172 if (treatments.size() != 1) {
173 log.error("Next Objectives of type Simple should only have a "
174 + "single Traffic Treatment. Next Objective Id:{}",
175 nextObjective.id());
Charles Chan361154b2016-03-24 10:23:39 -0700176 Ofdpa2Pipeline.fail(nextObjective, ObjectiveError.BADPARAMS);
Charles Chan188ebf52015-12-23 00:15:11 -0800177 return;
178 }
179 processSimpleNextObjective(nextObjective);
180 break;
181 case BROADCAST:
182 processBroadcastNextObjective(nextObjective);
183 break;
184 case HASHED:
185 processHashedNextObjective(nextObjective);
186 break;
187 case FAILOVER:
Charles Chan361154b2016-03-24 10:23:39 -0700188 Ofdpa2Pipeline.fail(nextObjective, ObjectiveError.UNSUPPORTED);
Charles Chan188ebf52015-12-23 00:15:11 -0800189 log.warn("Unsupported next objective type {}", nextObjective.type());
190 break;
191 default:
Charles Chan361154b2016-03-24 10:23:39 -0700192 Ofdpa2Pipeline.fail(nextObjective, ObjectiveError.UNKNOWN);
Charles Chan188ebf52015-12-23 00:15:11 -0800193 log.warn("Unknown next objective type {}", nextObjective.type());
194 }
195 }
196
197 /**
198 * As per the OFDPA 2.0 TTP, packets are sent out of ports by using
199 * a chain of groups. The simple Next Objective passed
200 * in by the application has to be broken up into a group chain
201 * comprising of an L3 Unicast Group that points to an L2 Interface
202 * Group which in-turn points to an output port. In some cases, the simple
203 * next Objective can just be an L2 interface without the need for chaining.
204 *
205 * @param nextObj the nextObjective of type SIMPLE
206 */
207 private void processSimpleNextObjective(NextObjective nextObj) {
208 TrafficTreatment treatment = nextObj.next().iterator().next();
209 // determine if plain L2 or L3->L2
210 boolean plainL2 = true;
211 for (Instruction ins : treatment.allInstructions()) {
212 if (ins.type() == Instruction.Type.L2MODIFICATION) {
213 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
214 if (l2ins.subtype() == L2ModificationInstruction.L2SubType.ETH_DST ||
215 l2ins.subtype() == L2ModificationInstruction.L2SubType.ETH_SRC) {
216 plainL2 = false;
217 break;
218 }
219 }
220 }
221
222 if (plainL2) {
223 createL2InterfaceGroup(nextObj);
224 return;
225 }
226
227 // break up simple next objective to GroupChain objects
228 GroupInfo groupInfo = createL2L3Chain(treatment, nextObj.id(),
Saurav Das8be4e3a2016-03-11 17:19:07 -0800229 nextObj.appId(), false,
230 nextObj.meta());
Charles Chan188ebf52015-12-23 00:15:11 -0800231 if (groupInfo == null) {
232 log.error("Could not process nextObj={} in dev:{}", nextObj.id(), deviceId);
233 return;
234 }
235 // create object for local and distributed storage
236 Deque<GroupKey> gkeyChain = new ArrayDeque<>();
Charles Chan5b9df8d2016-03-28 22:21:40 -0700237 gkeyChain.addFirst(groupInfo.innerMostGroupDesc.appCookie());
238 gkeyChain.addFirst(groupInfo.nextGroupDesc.appCookie());
Charles Chan188ebf52015-12-23 00:15:11 -0800239 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(
240 Collections.singletonList(gkeyChain),
241 nextObj);
242
243 // store l3groupkey with the ofdpaNextGroup for the nextObjective that depends on it
Charles Chan5b9df8d2016-03-28 22:21:40 -0700244 updatePendingNextObjective(groupInfo.nextGroupDesc.appCookie(), ofdpaGrp);
Charles Chan188ebf52015-12-23 00:15:11 -0800245
246 // now we are ready to send the l2 groupDescription (inner), as all the stores
247 // that will get async replies have been updated. By waiting to update
248 // the stores, we prevent nasty race conditions.
Charles Chan5b9df8d2016-03-28 22:21:40 -0700249 groupService.addGroup(groupInfo.innerMostGroupDesc);
Charles Chan188ebf52015-12-23 00:15:11 -0800250 }
251
Charles Chan188ebf52015-12-23 00:15:11 -0800252 /**
253 * Creates a simple L2 Interface Group.
254 *
255 * @param nextObj the next Objective
256 */
257 private void createL2InterfaceGroup(NextObjective nextObj) {
Charles Chan5b9df8d2016-03-28 22:21:40 -0700258 VlanId assignedVlan = Ofdpa2Pipeline.readVlanFromSelector(nextObj.meta());
259 if (assignedVlan == null) {
260 log.warn("VLAN ID required by simple next obj is missing. Abort.");
261 Ofdpa2Pipeline.fail(nextObj, ObjectiveError.BADPARAMS);
Charles Chan188ebf52015-12-23 00:15:11 -0800262 return;
263 }
264
Charles Chan5b9df8d2016-03-28 22:21:40 -0700265 List<GroupInfo> groupInfos = prepareL2InterfaceGroup(nextObj, assignedVlan);
Charles Chan188ebf52015-12-23 00:15:11 -0800266
Charles Chan5b9df8d2016-03-28 22:21:40 -0700267 // There is only one L2 interface group in this case
268 GroupDescription l2InterfaceGroupDesc = groupInfos.get(0).innerMostGroupDesc;
Charles Chan188ebf52015-12-23 00:15:11 -0800269
Charles Chan5b9df8d2016-03-28 22:21:40 -0700270 // Put all dependency information into allGroupKeys
271 List<Deque<GroupKey>> allGroupKeys = Lists.newArrayList();
272 Deque<GroupKey> gkeyChain = new ArrayDeque<>();
273 gkeyChain.addFirst(l2InterfaceGroupDesc.appCookie());
274 allGroupKeys.add(gkeyChain);
Charles Chan188ebf52015-12-23 00:15:11 -0800275
Charles Chan5b9df8d2016-03-28 22:21:40 -0700276 // Point the next objective to this group
277 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
278 updatePendingNextObjective(l2InterfaceGroupDesc.appCookie(), ofdpaGrp);
279
280 // Start installing the inner-most group
281 groupService.addGroup(l2InterfaceGroupDesc);
Charles Chan188ebf52015-12-23 00:15:11 -0800282 }
283
284 /**
285 * Creates one of two possible group-chains from the treatment
286 * passed in. Depending on the MPLS boolean, this method either creates
Charles Chan425854b2016-04-11 15:32:12 -0700287 * an L3Unicast Group --&gt; L2Interface Group, if mpls is false;
288 * or MPLSInterface Group --&gt; L2Interface Group, if mpls is true;
Charles Chan188ebf52015-12-23 00:15:11 -0800289 * The returned 'inner' group description is always the L2 Interface group.
290 *
291 * @param treatment that needs to be broken up to create the group chain
292 * @param nextId of the next objective that needs this group chain
293 * @param appId of the application that sent this next objective
294 * @param mpls determines if L3Unicast or MPLSInterface group is created
295 * @param meta metadata passed in by the application as part of the nextObjective
296 * @return GroupInfo containing the GroupDescription of the
297 * L2Interface group(inner) and the GroupDescription of the (outer)
298 * L3Unicast/MPLSInterface group. May return null if there is an
299 * error in processing the chain
300 */
Charles Chan425854b2016-04-11 15:32:12 -0700301 protected GroupInfo createL2L3Chain(TrafficTreatment treatment, int nextId,
Charles Chanf9e98652016-09-07 16:54:23 -0700302 ApplicationId appId, boolean mpls,
303 TrafficSelector meta) {
304 return createL2L3ChainInternal(treatment, nextId, appId, mpls, meta, true);
305 }
306
307 /**
308 * Internal implementation of createL2L3Chain.
309 * <p>
310 * The is_present bit in set_vlan_vid action is required to be 0 in OFDPA i12.
311 * Since it is non-OF spec, we need an extension treatment for that.
312 * The useSetVlanExtension must be set to false for OFDPA i12.
313 * </p>
314 *
315 * @param treatment that needs to be broken up to create the group chain
316 * @param nextId of the next objective that needs this group chain
317 * @param appId of the application that sent this next objective
318 * @param mpls determines if L3Unicast or MPLSInterface group is created
319 * @param meta metadata passed in by the application as part of the nextObjective
320 * @param useSetVlanExtension use the setVlanVid extension that has is_present bit set to 0.
321 * @return GroupInfo containing the GroupDescription of the
322 * L2Interface group(inner) and the GroupDescription of the (outer)
323 * L3Unicast/MPLSInterface group. May return null if there is an
324 * error in processing the chain
325 */
326 protected GroupInfo createL2L3ChainInternal(TrafficTreatment treatment, int nextId,
Saurav Das8be4e3a2016-03-11 17:19:07 -0800327 ApplicationId appId, boolean mpls,
Charles Chanf9e98652016-09-07 16:54:23 -0700328 TrafficSelector meta, boolean useSetVlanExtension) {
Charles Chan188ebf52015-12-23 00:15:11 -0800329 // for the l2interface group, get vlan and port info
330 // for the outer group, get the src/dst mac, and vlan info
331 TrafficTreatment.Builder outerTtb = DefaultTrafficTreatment.builder();
332 TrafficTreatment.Builder innerTtb = DefaultTrafficTreatment.builder();
333 VlanId vlanid = null;
334 long portNum = 0;
335 boolean setVlan = false, popVlan = false;
Charles Chand0fd5dc2016-02-16 23:14:49 -0800336 MacAddress srcMac = MacAddress.ZERO;
Charles Chan5270ed02016-01-30 23:22:37 -0800337 MacAddress dstMac = MacAddress.ZERO;
Charles Chan188ebf52015-12-23 00:15:11 -0800338 for (Instruction ins : treatment.allInstructions()) {
339 if (ins.type() == Instruction.Type.L2MODIFICATION) {
340 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
341 switch (l2ins.subtype()) {
342 case ETH_DST:
Charles Chan5270ed02016-01-30 23:22:37 -0800343 dstMac = ((L2ModificationInstruction.ModEtherInstruction) l2ins).mac();
344 outerTtb.setEthDst(dstMac);
Charles Chan188ebf52015-12-23 00:15:11 -0800345 break;
346 case ETH_SRC:
Charles Chand0fd5dc2016-02-16 23:14:49 -0800347 srcMac = ((L2ModificationInstruction.ModEtherInstruction) l2ins).mac();
348 outerTtb.setEthSrc(srcMac);
Charles Chan188ebf52015-12-23 00:15:11 -0800349 break;
350 case VLAN_ID:
351 vlanid = ((L2ModificationInstruction.ModVlanIdInstruction) l2ins).vlanId();
Charles Chanf9e98652016-09-07 16:54:23 -0700352 if (useSetVlanExtension) {
353 OfdpaSetVlanVid ofdpaSetVlanVid = new OfdpaSetVlanVid(vlanid);
354 outerTtb.extension(ofdpaSetVlanVid, deviceId);
355 } else {
356 outerTtb.setVlanId(vlanid);
357 }
Charles Chan188ebf52015-12-23 00:15:11 -0800358 setVlan = true;
359 break;
360 case VLAN_POP:
361 innerTtb.popVlan();
362 popVlan = true;
363 break;
364 case DEC_MPLS_TTL:
365 case MPLS_LABEL:
366 case MPLS_POP:
367 case MPLS_PUSH:
368 case VLAN_PCP:
369 case VLAN_PUSH:
370 default:
371 break;
372 }
373 } else if (ins.type() == Instruction.Type.OUTPUT) {
374 portNum = ((Instructions.OutputInstruction) ins).port().toLong();
375 innerTtb.add(ins);
376 } else {
377 log.warn("Driver does not handle this type of TrafficTreatment"
378 + " instruction in nextObjectives: {}", ins.type());
379 }
380 }
381
382 if (vlanid == null && meta != null) {
383 // use metadata if available
384 Criterion vidCriterion = meta.getCriterion(Criterion.Type.VLAN_VID);
385 if (vidCriterion != null) {
386 vlanid = ((VlanIdCriterion) vidCriterion).vlanId();
387 }
388 // if vlan is not set, use the vlan in metadata for outerTtb
389 if (vlanid != null && !setVlan) {
Charles Chanf9e98652016-09-07 16:54:23 -0700390 if (useSetVlanExtension) {
391 OfdpaSetVlanVid ofdpaSetVlanVid = new OfdpaSetVlanVid(vlanid);
392 outerTtb.extension(ofdpaSetVlanVid, deviceId);
393 } else {
394 outerTtb.setVlanId(vlanid);
395 }
Charles Chan188ebf52015-12-23 00:15:11 -0800396 }
397 }
398
399 if (vlanid == null) {
400 log.error("Driver cannot process an L2/L3 group chain without "
401 + "egress vlan information for dev: {} port:{}",
402 deviceId, portNum);
403 return null;
404 }
405
406 if (!setVlan && !popVlan) {
407 // untagged outgoing port
408 TrafficTreatment.Builder temp = DefaultTrafficTreatment.builder();
409 temp.popVlan();
410 innerTtb.build().allInstructions().forEach(i -> temp.add(i));
411 innerTtb = temp;
412 }
413
414 // assemble information for ofdpa l2interface group
Charles Chane849c192016-01-11 18:28:54 -0800415 int l2groupId = L2_INTERFACE_TYPE | (vlanid.toShort() << 16) | (int) portNum;
Saurav Das8be4e3a2016-03-11 17:19:07 -0800416 // a globally unique groupkey that is different for ports in the same device,
Charles Chan188ebf52015-12-23 00:15:11 -0800417 // but different for the same portnumber on different devices. Also different
418 // for the various group-types created out of the same next objective.
Charles Chane849c192016-01-11 18:28:54 -0800419 int l2gk = l2InterfaceGroupKey(deviceId, vlanid, portNum);
Charles Chan361154b2016-03-24 10:23:39 -0700420 final GroupKey l2groupkey = new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(l2gk));
Charles Chan188ebf52015-12-23 00:15:11 -0800421
422 // assemble information for outer group
423 GroupDescription outerGrpDesc = null;
424 if (mpls) {
425 // outer group is MPLSInteface
Saurav Das8be4e3a2016-03-11 17:19:07 -0800426 int mplsInterfaceIndex = getNextAvailableIndex();
427 int mplsgroupId = MPLS_INTERFACE_TYPE | (SUBTYPE_MASK & mplsInterfaceIndex);
428 final GroupKey mplsgroupkey = new DefaultGroupKey(
Charles Chan361154b2016-03-24 10:23:39 -0700429 Ofdpa2Pipeline.appKryo.serialize(mplsInterfaceIndex));
Charles Chan188ebf52015-12-23 00:15:11 -0800430 outerTtb.group(new DefaultGroupId(l2groupId));
431 // create the mpls-interface group description to wait for the
432 // l2 interface group to be processed
433 GroupBucket mplsinterfaceGroupBucket =
434 DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build());
435 outerGrpDesc = new DefaultGroupDescription(
436 deviceId,
437 GroupDescription.Type.INDIRECT,
438 new GroupBuckets(Collections.singletonList(
439 mplsinterfaceGroupBucket)),
440 mplsgroupkey,
441 mplsgroupId,
442 appId);
443 log.debug("Trying MPLS-Interface: device:{} gid:{} gkey:{} nextid:{}",
444 deviceId, Integer.toHexString(mplsgroupId),
445 mplsgroupkey, nextId);
446 } else {
447 // outer group is L3Unicast
Saurav Das8be4e3a2016-03-11 17:19:07 -0800448 int l3unicastIndex = getNextAvailableIndex();
449 int l3groupId = L3_UNICAST_TYPE | (TYPE_MASK & l3unicastIndex);
450 final GroupKey l3groupkey = new DefaultGroupKey(
Charles Chan361154b2016-03-24 10:23:39 -0700451 Ofdpa2Pipeline.appKryo.serialize(l3unicastIndex));
Charles Chan188ebf52015-12-23 00:15:11 -0800452 outerTtb.group(new DefaultGroupId(l2groupId));
453 // create the l3unicast group description to wait for the
454 // l2 interface group to be processed
455 GroupBucket l3unicastGroupBucket =
456 DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build());
457 outerGrpDesc = new DefaultGroupDescription(
458 deviceId,
459 GroupDescription.Type.INDIRECT,
460 new GroupBuckets(Collections.singletonList(
461 l3unicastGroupBucket)),
462 l3groupkey,
463 l3groupId,
464 appId);
465 log.debug("Trying L3Unicast: device:{} gid:{} gkey:{} nextid:{}",
466 deviceId, Integer.toHexString(l3groupId),
467 l3groupkey, nextId);
468 }
469
470 // store l2groupkey with the groupChainElem for the outer-group that depends on it
471 GroupChainElem gce = new GroupChainElem(outerGrpDesc, 1, false);
472 updatePendingGroups(l2groupkey, gce);
473
474 // create group description for the inner l2interfacegroup
Charles Chan5b9df8d2016-03-28 22:21:40 -0700475 GroupBucket l2InterfaceGroupBucket =
Charles Chan188ebf52015-12-23 00:15:11 -0800476 DefaultGroupBucket.createIndirectGroupBucket(innerTtb.build());
477 GroupDescription l2groupDescription =
478 new DefaultGroupDescription(
479 deviceId,
480 GroupDescription.Type.INDIRECT,
481 new GroupBuckets(Collections.singletonList(
Charles Chan5b9df8d2016-03-28 22:21:40 -0700482 l2InterfaceGroupBucket)),
Charles Chan188ebf52015-12-23 00:15:11 -0800483 l2groupkey,
484 l2groupId,
485 appId);
486 log.debug("Trying L2Interface: device:{} gid:{} gkey:{} nextId:{}",
487 deviceId, Integer.toHexString(l2groupId),
488 l2groupkey, nextId);
489 return new GroupInfo(l2groupDescription, outerGrpDesc);
490
491 }
492
493 /**
494 * As per the OFDPA 2.0 TTP, packets are sent out of ports by using
495 * a chain of groups. The broadcast Next Objective passed in by the application
496 * has to be broken up into a group chain comprising of an
497 * L2 Flood group whose buckets point to L2 Interface groups.
498 *
499 * @param nextObj the nextObjective of type BROADCAST
500 */
501 private void processBroadcastNextObjective(NextObjective nextObj) {
Charles Chan5b9df8d2016-03-28 22:21:40 -0700502 VlanId assignedVlan = Ofdpa2Pipeline.readVlanFromSelector(nextObj.meta());
503 if (assignedVlan == null) {
504 log.warn("VLAN ID required by broadcast next obj is missing. Abort.");
505 Ofdpa2Pipeline.fail(nextObj, ObjectiveError.BADPARAMS);
Charles Chan188ebf52015-12-23 00:15:11 -0800506 return;
507 }
Charles Chan188ebf52015-12-23 00:15:11 -0800508
Charles Chan5b9df8d2016-03-28 22:21:40 -0700509 List<GroupInfo> groupInfos = prepareL2InterfaceGroup(nextObj, assignedVlan);
510
511 IpPrefix ipDst = Ofdpa2Pipeline.readIpDstFromSelector(nextObj.meta());
512 if (ipDst != null) {
513 if (ipDst.isMulticast()) {
514 createL3MulticastGroup(nextObj, assignedVlan, groupInfos);
515 } else {
516 log.warn("Broadcast NextObj with non-multicast IP address {}", nextObj);
517 Ofdpa2Pipeline.fail(nextObj, ObjectiveError.BADPARAMS);
518 return;
519 }
520 } else {
521 createL2FloodGroup(nextObj, assignedVlan, groupInfos);
522 }
523 }
524
525 private List<GroupInfo> prepareL2InterfaceGroup(NextObjective nextObj, VlanId assignedVlan) {
526 ImmutableList.Builder<GroupInfo> groupInfoBuilder = ImmutableList.builder();
527
528 // break up broadcast next objective to multiple groups
529 Collection<TrafficTreatment> buckets = nextObj.next();
530
Charles Chan188ebf52015-12-23 00:15:11 -0800531 // each treatment is converted to an L2 interface group
Charles Chan188ebf52015-12-23 00:15:11 -0800532 for (TrafficTreatment treatment : buckets) {
533 TrafficTreatment.Builder newTreatment = DefaultTrafficTreatment.builder();
534 PortNumber portNum = null;
Charles Chan5b9df8d2016-03-28 22:21:40 -0700535 VlanId egressVlan = null;
Charles Chan188ebf52015-12-23 00:15:11 -0800536 // ensure that the only allowed treatments are pop-vlan and output
537 for (Instruction ins : treatment.allInstructions()) {
538 if (ins.type() == Instruction.Type.L2MODIFICATION) {
539 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
540 switch (l2ins.subtype()) {
541 case VLAN_POP:
542 newTreatment.add(l2ins);
543 break;
Charles Chan5b9df8d2016-03-28 22:21:40 -0700544 case VLAN_ID:
545 egressVlan = ((L2ModificationInstruction.ModVlanIdInstruction) l2ins).vlanId();
546 break;
Charles Chan188ebf52015-12-23 00:15:11 -0800547 default:
548 log.debug("action {} not permitted for broadcast nextObj",
549 l2ins.subtype());
550 break;
551 }
552 } else if (ins.type() == Instruction.Type.OUTPUT) {
553 portNum = ((Instructions.OutputInstruction) ins).port();
554 newTreatment.add(ins);
555 } else {
Charles Chane849c192016-01-11 18:28:54 -0800556 log.debug("TrafficTreatment of type {} not permitted in " +
557 " broadcast nextObjective", ins.type());
Charles Chan188ebf52015-12-23 00:15:11 -0800558 }
559 }
560
Charles Chan188ebf52015-12-23 00:15:11 -0800561 // assemble info for l2 interface group
Charles Chan5b9df8d2016-03-28 22:21:40 -0700562 VlanId l2InterfaceGroupVlan =
563 (egressVlan != null && !assignedVlan.equals(egressVlan)) ?
564 egressVlan : assignedVlan;
565 int l2gk = l2InterfaceGroupKey(deviceId, l2InterfaceGroupVlan, portNum.toLong());
566 final GroupKey l2InterfaceGroupKey =
567 new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(l2gk));
568 int l2InterfaceGroupId = L2_INTERFACE_TYPE | (l2InterfaceGroupVlan.toShort() << 16) |
Charles Chan188ebf52015-12-23 00:15:11 -0800569 (int) portNum.toLong();
Charles Chan5b9df8d2016-03-28 22:21:40 -0700570 GroupBucket l2InterfaceGroupBucket =
Charles Chan188ebf52015-12-23 00:15:11 -0800571 DefaultGroupBucket.createIndirectGroupBucket(newTreatment.build());
Charles Chan5b9df8d2016-03-28 22:21:40 -0700572 GroupDescription l2InterfaceGroupDescription =
Charles Chan188ebf52015-12-23 00:15:11 -0800573 new DefaultGroupDescription(
574 deviceId,
575 GroupDescription.Type.INDIRECT,
576 new GroupBuckets(Collections.singletonList(
Charles Chan5b9df8d2016-03-28 22:21:40 -0700577 l2InterfaceGroupBucket)),
578 l2InterfaceGroupKey,
579 l2InterfaceGroupId,
Charles Chan188ebf52015-12-23 00:15:11 -0800580 nextObj.appId());
581 log.debug("Trying L2-Interface: device:{} gid:{} gkey:{} nextid:{}",
Charles Chan5b9df8d2016-03-28 22:21:40 -0700582 deviceId, Integer.toHexString(l2InterfaceGroupId),
583 l2InterfaceGroupKey, nextObj.id());
Charles Chan188ebf52015-12-23 00:15:11 -0800584
Charles Chan5b9df8d2016-03-28 22:21:40 -0700585 groupInfoBuilder.add(new GroupInfo(l2InterfaceGroupDescription,
586 l2InterfaceGroupDescription));
Charles Chan188ebf52015-12-23 00:15:11 -0800587 }
Charles Chan5b9df8d2016-03-28 22:21:40 -0700588 return groupInfoBuilder.build();
589 }
Charles Chan188ebf52015-12-23 00:15:11 -0800590
Charles Chan5b9df8d2016-03-28 22:21:40 -0700591 private void createL2FloodGroup(NextObjective nextObj, VlanId vlanId, List<GroupInfo> groupInfos) {
Charles Chan188ebf52015-12-23 00:15:11 -0800592 // assemble info for l2 flood group
Saurav Das0fd79d92016-03-07 10:58:36 -0800593 // since there can be only one flood group for a vlan, its index is always the same - 0
594 Integer l2floodgroupId = L2_FLOOD_TYPE | (vlanId.toShort() << 16);
Saurav Das8be4e3a2016-03-11 17:19:07 -0800595 int l2floodgk = getNextAvailableIndex();
Charles Chan361154b2016-03-24 10:23:39 -0700596 final GroupKey l2floodgroupkey = new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(l2floodgk));
Charles Chan5b9df8d2016-03-28 22:21:40 -0700597
Charles Chan188ebf52015-12-23 00:15:11 -0800598 // collection of group buckets pointing to all the l2 interface groups
Charles Chan5b9df8d2016-03-28 22:21:40 -0700599 List<GroupBucket> l2floodBuckets = Lists.newArrayList();
600 groupInfos.forEach(groupInfo -> {
601 GroupDescription l2intGrpDesc = groupInfo.nextGroupDesc;
Charles Chan188ebf52015-12-23 00:15:11 -0800602 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
603 ttb.group(new DefaultGroupId(l2intGrpDesc.givenGroupId()));
604 GroupBucket abucket = DefaultGroupBucket.createAllGroupBucket(ttb.build());
605 l2floodBuckets.add(abucket);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700606 });
Charles Chan188ebf52015-12-23 00:15:11 -0800607 // create the l2flood group-description to wait for all the
608 // l2interface groups to be processed
609 GroupDescription l2floodGroupDescription =
610 new DefaultGroupDescription(
611 deviceId,
612 GroupDescription.Type.ALL,
613 new GroupBuckets(l2floodBuckets),
614 l2floodgroupkey,
615 l2floodgroupId,
616 nextObj.appId());
Charles Chan188ebf52015-12-23 00:15:11 -0800617 log.debug("Trying L2-Flood: device:{} gid:{} gkey:{} nextid:{}",
618 deviceId, Integer.toHexString(l2floodgroupId),
619 l2floodgroupkey, nextObj.id());
620
Charles Chan5b9df8d2016-03-28 22:21:40 -0700621 // Put all dependency information into allGroupKeys
622 List<Deque<GroupKey>> allGroupKeys = Lists.newArrayList();
623 groupInfos.forEach(groupInfo -> {
624 Deque<GroupKey> gkeyChain = new ArrayDeque<>();
625 // In this case we should have L2 interface group only
626 gkeyChain.addFirst(groupInfo.nextGroupDesc.appCookie());
627 gkeyChain.addFirst(l2floodgroupkey);
628 allGroupKeys.add(gkeyChain);
629 });
Charles Chan188ebf52015-12-23 00:15:11 -0800630
Charles Chan5b9df8d2016-03-28 22:21:40 -0700631 // Point the next objective to this group
632 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
Charles Chan188ebf52015-12-23 00:15:11 -0800633 updatePendingNextObjective(l2floodgroupkey, ofdpaGrp);
634
Charles Chan5b9df8d2016-03-28 22:21:40 -0700635 GroupChainElem gce = new GroupChainElem(l2floodGroupDescription,
636 groupInfos.size(), false);
637 groupInfos.forEach(groupInfo -> {
638 // Point this group to the next group
639 updatePendingGroups(groupInfo.nextGroupDesc.appCookie(), gce);
640 // Start installing the inner-most group
641 groupService.addGroup(groupInfo.innerMostGroupDesc);
642 });
643 }
644
645 private void createL3MulticastGroup(NextObjective nextObj, VlanId vlanId, List<GroupInfo> groupInfos) {
646 List<GroupBucket> l3McastBuckets = new ArrayList<>();
647 groupInfos.forEach(groupInfo -> {
648 // Points to L3 interface group if there is one.
649 // Otherwise points to L2 interface group directly.
650 GroupDescription nextGroupDesc = (groupInfo.nextGroupDesc != null) ?
651 groupInfo.nextGroupDesc : groupInfo.innerMostGroupDesc;
652 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
653 ttb.group(new DefaultGroupId(nextGroupDesc.givenGroupId()));
654 GroupBucket abucket = DefaultGroupBucket.createAllGroupBucket(ttb.build());
655 l3McastBuckets.add(abucket);
656 });
657
658 int l3MulticastIndex = getNextAvailableIndex();
659 int l3MulticastGroupId = L3_MULTICAST_TYPE | vlanId.toShort() << 16 | (TYPE_VLAN_MASK & l3MulticastIndex);
660 final GroupKey l3MulticastGroupKey = new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(l3MulticastIndex));
661
662 GroupDescription l3MulticastGroupDesc = new DefaultGroupDescription(deviceId,
663 GroupDescription.Type.ALL,
664 new GroupBuckets(l3McastBuckets),
665 l3MulticastGroupKey,
666 l3MulticastGroupId,
667 nextObj.appId());
668
669 // Put all dependency information into allGroupKeys
670 List<Deque<GroupKey>> allGroupKeys = Lists.newArrayList();
671 groupInfos.forEach(groupInfo -> {
672 Deque<GroupKey> gkeyChain = new ArrayDeque<>();
673 gkeyChain.addFirst(groupInfo.innerMostGroupDesc.appCookie());
674 // Add L3 interface group to the chain if there is one.
675 if (!groupInfo.nextGroupDesc.equals(groupInfo.innerMostGroupDesc)) {
676 gkeyChain.addFirst(groupInfo.nextGroupDesc.appCookie());
677 }
678 gkeyChain.addFirst(l3MulticastGroupKey);
679 allGroupKeys.add(gkeyChain);
680 });
681
682 // Point the next objective to this group
683 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
684 updatePendingNextObjective(l3MulticastGroupKey, ofdpaGrp);
685
686 GroupChainElem outerGce = new GroupChainElem(l3MulticastGroupDesc,
687 groupInfos.size(), false);
688 groupInfos.forEach(groupInfo -> {
689 // Point this group (L3 multicast) to the next group
690 updatePendingGroups(groupInfo.nextGroupDesc.appCookie(), outerGce);
691
692 // Point next group to inner-most group, if any
693 if (!groupInfo.nextGroupDesc.equals(groupInfo.innerMostGroupDesc)) {
694 GroupChainElem innerGce = new GroupChainElem(groupInfo.nextGroupDesc,
695 1, false);
696 updatePendingGroups(groupInfo.innerMostGroupDesc.appCookie(), innerGce);
697 }
698
699 // Start installing the inner-most group
700 groupService.addGroup(groupInfo.innerMostGroupDesc);
701 });
Charles Chan188ebf52015-12-23 00:15:11 -0800702 }
703
Charles Chan188ebf52015-12-23 00:15:11 -0800704 /**
705 * As per the OFDPA 2.0 TTP, packets are sent out of ports by using
706 * a chain of groups. The hashed Next Objective passed in by the application
707 * has to be broken up into a group chain comprising of an
708 * L3 ECMP group as the top level group. Buckets of this group can point
709 * to a variety of groups in a group chain, depending on the whether
710 * MPLS labels are being pushed or not.
711 * <p>
712 * NOTE: We do not create MPLS ECMP groups as they are unimplemented in
713 * OF-DPA 2.0 (even though it is in the spec). Therefore we do not
714 * check the nextObjective meta to see what is matching before being
715 * sent to this nextObjective.
716 *
717 * @param nextObj the nextObjective of type HASHED
718 */
719 private void processHashedNextObjective(NextObjective nextObj) {
720 // storage for all group keys in the chain of groups created
721 List<Deque<GroupKey>> allGroupKeys = new ArrayList<>();
722 List<GroupInfo> unsentGroups = new ArrayList<>();
723 createHashBucketChains(nextObj, allGroupKeys, unsentGroups);
724
725 // now we can create the outermost L3 ECMP group
726 List<GroupBucket> l3ecmpGroupBuckets = new ArrayList<>();
727 for (GroupInfo gi : unsentGroups) {
728 // create ECMP bucket to point to the outer group
729 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
Charles Chan5b9df8d2016-03-28 22:21:40 -0700730 ttb.group(new DefaultGroupId(gi.nextGroupDesc.givenGroupId()));
Charles Chan188ebf52015-12-23 00:15:11 -0800731 GroupBucket sbucket = DefaultGroupBucket
732 .createSelectGroupBucket(ttb.build());
733 l3ecmpGroupBuckets.add(sbucket);
734 }
Saurav Das8be4e3a2016-03-11 17:19:07 -0800735 int l3ecmpIndex = getNextAvailableIndex();
736 int l3ecmpGroupId = L3_ECMP_TYPE | (TYPE_MASK & l3ecmpIndex);
737 GroupKey l3ecmpGroupKey = new DefaultGroupKey(
Charles Chan361154b2016-03-24 10:23:39 -0700738 Ofdpa2Pipeline.appKryo.serialize(l3ecmpIndex));
Charles Chan188ebf52015-12-23 00:15:11 -0800739 GroupDescription l3ecmpGroupDesc =
740 new DefaultGroupDescription(
741 deviceId,
742 GroupDescription.Type.SELECT,
743 new GroupBuckets(l3ecmpGroupBuckets),
744 l3ecmpGroupKey,
745 l3ecmpGroupId,
746 nextObj.appId());
747 GroupChainElem l3ecmpGce = new GroupChainElem(l3ecmpGroupDesc,
748 l3ecmpGroupBuckets.size(),
749 false);
750
751 // create objects for local and distributed storage
752 allGroupKeys.forEach(gkeyChain -> gkeyChain.addFirst(l3ecmpGroupKey));
753 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
754
755 // store l3ecmpGroupKey with the ofdpaGroupChain for the nextObjective
756 // that depends on it
757 updatePendingNextObjective(l3ecmpGroupKey, ofdpaGrp);
758
759 log.debug("Trying L3ECMP: device:{} gid:{} gkey:{} nextId:{}",
760 deviceId, Integer.toHexString(l3ecmpGroupId),
761 l3ecmpGroupKey, nextObj.id());
762 // finally we are ready to send the innermost groups
763 for (GroupInfo gi : unsentGroups) {
764 log.debug("Sending innermost group {} in group chain on device {} ",
Charles Chan5b9df8d2016-03-28 22:21:40 -0700765 Integer.toHexString(gi.innerMostGroupDesc.givenGroupId()), deviceId);
766 updatePendingGroups(gi.nextGroupDesc.appCookie(), l3ecmpGce);
767 groupService.addGroup(gi.innerMostGroupDesc);
Charles Chan188ebf52015-12-23 00:15:11 -0800768 }
769
770 }
771
772 /**
773 * Creates group chains for all buckets in a hashed group, and stores the
774 * GroupInfos and GroupKeys for all the groups in the lists passed in, which
775 * should be empty.
776 * <p>
777 * Does not create the top level ECMP group. Does not actually send the
778 * groups to the groupService.
779 *
780 * @param nextObj the Next Objective with buckets that need to be converted
781 * to group chains
782 * @param allGroupKeys a list to store groupKey for each bucket-group-chain
783 * @param unsentGroups a list to store GroupInfo for each bucket-group-chain
784 */
785 private void createHashBucketChains(NextObjective nextObj,
Saurav Das8be4e3a2016-03-11 17:19:07 -0800786 List<Deque<GroupKey>> allGroupKeys,
787 List<GroupInfo> unsentGroups) {
Charles Chan188ebf52015-12-23 00:15:11 -0800788 // break up hashed next objective to multiple groups
789 Collection<TrafficTreatment> buckets = nextObj.next();
790
791 for (TrafficTreatment bucket : buckets) {
792 //figure out how many labels are pushed in each bucket
793 int labelsPushed = 0;
794 MplsLabel innermostLabel = null;
795 for (Instruction ins : bucket.allInstructions()) {
796 if (ins.type() == Instruction.Type.L2MODIFICATION) {
797 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
798 if (l2ins.subtype() == L2ModificationInstruction.L2SubType.MPLS_PUSH) {
799 labelsPushed++;
800 }
801 if (l2ins.subtype() == L2ModificationInstruction.L2SubType.MPLS_LABEL) {
802 if (innermostLabel == null) {
Ray Milkey125572b2016-02-22 16:48:17 -0800803 innermostLabel = ((L2ModificationInstruction.ModMplsLabelInstruction) l2ins).label();
Charles Chan188ebf52015-12-23 00:15:11 -0800804 }
805 }
806 }
807 }
808
809 Deque<GroupKey> gkeyChain = new ArrayDeque<>();
810 // XXX we only deal with 0 and 1 label push right now
811 if (labelsPushed == 0) {
812 GroupInfo nolabelGroupInfo = createL2L3Chain(bucket, nextObj.id(),
813 nextObj.appId(), false,
814 nextObj.meta());
815 if (nolabelGroupInfo == null) {
816 log.error("Could not process nextObj={} in dev:{}",
817 nextObj.id(), deviceId);
818 return;
819 }
Charles Chan5b9df8d2016-03-28 22:21:40 -0700820 gkeyChain.addFirst(nolabelGroupInfo.innerMostGroupDesc.appCookie());
821 gkeyChain.addFirst(nolabelGroupInfo.nextGroupDesc.appCookie());
Charles Chan188ebf52015-12-23 00:15:11 -0800822
823 // we can't send the inner group description yet, as we have to
824 // create the dependent ECMP group first. So we store..
825 unsentGroups.add(nolabelGroupInfo);
826
827 } else if (labelsPushed == 1) {
828 GroupInfo onelabelGroupInfo = createL2L3Chain(bucket, nextObj.id(),
829 nextObj.appId(), true,
830 nextObj.meta());
831 if (onelabelGroupInfo == null) {
832 log.error("Could not process nextObj={} in dev:{}",
833 nextObj.id(), deviceId);
834 return;
835 }
836 // we need to add another group to this chain - the L3VPN group
837 TrafficTreatment.Builder l3vpnTtb = DefaultTrafficTreatment.builder();
838 l3vpnTtb.pushMpls()
839 .setMpls(innermostLabel)
840 .setMplsBos(true)
841 .copyTtlOut()
842 .group(new DefaultGroupId(
Charles Chan5b9df8d2016-03-28 22:21:40 -0700843 onelabelGroupInfo.nextGroupDesc.givenGroupId()));
Charles Chan188ebf52015-12-23 00:15:11 -0800844 GroupBucket l3vpnGrpBkt =
845 DefaultGroupBucket.createIndirectGroupBucket(l3vpnTtb.build());
Saurav Das8be4e3a2016-03-11 17:19:07 -0800846 int l3vpnIndex = getNextAvailableIndex();
847 int l3vpngroupId = MPLS_L3VPN_SUBTYPE | (SUBTYPE_MASK & l3vpnIndex);
848 GroupKey l3vpngroupkey = new DefaultGroupKey(
Charles Chan361154b2016-03-24 10:23:39 -0700849 Ofdpa2Pipeline.appKryo.serialize(l3vpnIndex));
Charles Chan188ebf52015-12-23 00:15:11 -0800850 GroupDescription l3vpnGroupDesc =
851 new DefaultGroupDescription(
852 deviceId,
853 GroupDescription.Type.INDIRECT,
854 new GroupBuckets(Collections.singletonList(
855 l3vpnGrpBkt)),
856 l3vpngroupkey,
857 l3vpngroupId,
858 nextObj.appId());
859 GroupChainElem l3vpnGce = new GroupChainElem(l3vpnGroupDesc, 1, false);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700860 updatePendingGroups(onelabelGroupInfo.nextGroupDesc.appCookie(), l3vpnGce);
Charles Chan188ebf52015-12-23 00:15:11 -0800861
Charles Chan5b9df8d2016-03-28 22:21:40 -0700862 gkeyChain.addFirst(onelabelGroupInfo.innerMostGroupDesc.appCookie());
863 gkeyChain.addFirst(onelabelGroupInfo.nextGroupDesc.appCookie());
Charles Chan188ebf52015-12-23 00:15:11 -0800864 gkeyChain.addFirst(l3vpngroupkey);
865
866 //now we can replace the outerGrpDesc with the one we just created
Charles Chan5b9df8d2016-03-28 22:21:40 -0700867 onelabelGroupInfo.nextGroupDesc = l3vpnGroupDesc;
Charles Chan188ebf52015-12-23 00:15:11 -0800868
869 // we can't send the innermost group yet, as we have to create
870 // the dependent ECMP group first. So we store ...
871 unsentGroups.add(onelabelGroupInfo);
872
873 log.debug("Trying L3VPN: device:{} gid:{} gkey:{} nextId:{}",
874 deviceId, Integer.toHexString(l3vpngroupId),
875 l3vpngroupkey, nextObj.id());
876
877 } else {
878 log.warn("Driver currently does not handle more than 1 MPLS "
879 + "labels. Not processing nextObjective {}", nextObj.id());
880 return;
881 }
882
883 // all groups in this chain
884 allGroupKeys.add(gkeyChain);
885 }
886 }
887
Saurav Das8be4e3a2016-03-11 17:19:07 -0800888 //////////////////////////////////////
889 // Group Editing
890 //////////////////////////////////////
891
Charles Chan188ebf52015-12-23 00:15:11 -0800892 /**
893 * Adds a bucket to the top level group of a group-chain, and creates the chain.
894 *
895 * @param nextObjective the next group to add a bucket to
896 * @param next the representation of the existing group-chain for this next objective
897 */
898 protected void addBucketToGroup(NextObjective nextObjective, NextGroup next) {
899 if (nextObjective.type() != NextObjective.Type.HASHED) {
900 log.warn("AddBuckets not applied to nextType:{} in dev:{} for next:{}",
901 nextObjective.type(), deviceId, nextObjective.id());
902 return;
903 }
904 if (nextObjective.next().size() > 1) {
905 log.warn("Only one bucket can be added at a time");
906 return;
907 }
908 // storage for all group keys in the chain of groups created
909 List<Deque<GroupKey>> allGroupKeys = new ArrayList<>();
910 List<GroupInfo> unsentGroups = new ArrayList<>();
911 createHashBucketChains(nextObjective, allGroupKeys, unsentGroups);
912
913 // now we can create the outermost L3 ECMP group bucket to add
914 GroupInfo gi = unsentGroups.get(0); // only one bucket, so only one group-chain
915 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
Charles Chan5b9df8d2016-03-28 22:21:40 -0700916 ttb.group(new DefaultGroupId(gi.nextGroupDesc.givenGroupId()));
Charles Chan188ebf52015-12-23 00:15:11 -0800917 GroupBucket sbucket = DefaultGroupBucket.createSelectGroupBucket(ttb.build());
918
919 // recreate the original L3 ECMP group id and description
Charles Chane849c192016-01-11 18:28:54 -0800920 int l3ecmpGroupId = L3_ECMP_TYPE | nextObjective.id() << 12;
Charles Chan361154b2016-03-24 10:23:39 -0700921 GroupKey l3ecmpGroupKey = new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(l3ecmpGroupId));
Charles Chan188ebf52015-12-23 00:15:11 -0800922
923 // Although GroupDescriptions are not necessary for adding buckets to
924 // existing groups, we use one in the GroupChainElem. When the latter is
925 // processed, the info will be extracted for the bucketAdd call to groupService
926 GroupDescription l3ecmpGroupDesc =
927 new DefaultGroupDescription(
928 deviceId,
929 GroupDescription.Type.SELECT,
930 new GroupBuckets(Collections.singletonList(sbucket)),
931 l3ecmpGroupKey,
932 l3ecmpGroupId,
933 nextObjective.appId());
934 GroupChainElem l3ecmpGce = new GroupChainElem(l3ecmpGroupDesc, 1, true);
935
936 // update original NextGroup with new bucket-chain
937 // don't need to update pendingNextObjectives -- group already exists
938 Deque<GroupKey> newBucketChain = allGroupKeys.get(0);
939 newBucketChain.addFirst(l3ecmpGroupKey);
Charles Chan361154b2016-03-24 10:23:39 -0700940 List<Deque<GroupKey>> allOriginalKeys = Ofdpa2Pipeline.appKryo.deserialize(next.data());
Charles Chan188ebf52015-12-23 00:15:11 -0800941 allOriginalKeys.add(newBucketChain);
942 flowObjectiveStore.putNextGroup(nextObjective.id(),
943 new OfdpaNextGroup(allOriginalKeys, nextObjective));
944
945 log.debug("Adding to L3ECMP: device:{} gid:{} gkey:{} nextId:{}",
946 deviceId, Integer.toHexString(l3ecmpGroupId),
947 l3ecmpGroupKey, nextObjective.id());
948 // send the innermost group
949 log.debug("Sending innermost group {} in group chain on device {} ",
Charles Chan5b9df8d2016-03-28 22:21:40 -0700950 Integer.toHexString(gi.innerMostGroupDesc.givenGroupId()), deviceId);
951 updatePendingGroups(gi.nextGroupDesc.appCookie(), l3ecmpGce);
952 groupService.addGroup(gi.innerMostGroupDesc);
Charles Chan188ebf52015-12-23 00:15:11 -0800953
954 }
955
956 /**
957 * Removes the bucket in the top level group of a possible group-chain. Does
958 * not remove the groups in a group-chain pointed to by this bucket, as they
959 * may be in use (referenced by other groups) elsewhere.
960 *
961 * @param nextObjective the next group to remove a bucket from
962 * @param next the representation of the existing group-chain for this next objective
963 */
964 protected void removeBucketFromGroup(NextObjective nextObjective, NextGroup next) {
965 if (nextObjective.type() != NextObjective.Type.HASHED) {
966 log.warn("RemoveBuckets not applied to nextType:{} in dev:{} for next:{}",
967 nextObjective.type(), deviceId, nextObjective.id());
968 return;
969 }
970 Collection<TrafficTreatment> treatments = nextObjective.next();
971 TrafficTreatment treatment = treatments.iterator().next();
972 // find the bucket to remove by noting the outport, and figuring out the
973 // top-level group in the group-chain that indirectly references the port
974 PortNumber outport = null;
975 for (Instruction ins : treatment.allInstructions()) {
976 if (ins instanceof Instructions.OutputInstruction) {
977 outport = ((Instructions.OutputInstruction) ins).port();
978 break;
979 }
980 }
981 if (outport == null) {
982 log.error("next objective {} has no outport", nextObjective.id());
983 return;
984 }
985
Charles Chan361154b2016-03-24 10:23:39 -0700986 List<Deque<GroupKey>> allgkeys = Ofdpa2Pipeline.appKryo.deserialize(next.data());
Charles Chan188ebf52015-12-23 00:15:11 -0800987 Deque<GroupKey> foundChain = null;
988 int index = 0;
989 for (Deque<GroupKey> gkeys : allgkeys) {
990 GroupKey groupWithPort = gkeys.peekLast();
991 Group group = groupService.getGroup(deviceId, groupWithPort);
992 if (group == null) {
993 log.warn("Inconsistent group chain");
994 continue;
995 }
996 // last group in group chain should have a single bucket pointing to port
997 List<Instruction> lastIns = group.buckets().buckets().iterator()
998 .next().treatment().allInstructions();
999 for (Instruction i : lastIns) {
1000 if (i instanceof Instructions.OutputInstruction) {
1001 PortNumber lastport = ((Instructions.OutputInstruction) i).port();
1002 if (lastport.equals(outport)) {
1003 foundChain = gkeys;
1004 break;
1005 }
1006 }
1007 }
1008 if (foundChain != null) {
1009 break;
1010 }
1011 index++;
1012 }
1013 if (foundChain != null) {
1014 //first groupkey is the one we want to modify
1015 GroupKey modGroupKey = foundChain.peekFirst();
1016 Group modGroup = groupService.getGroup(deviceId, modGroupKey);
1017 //second groupkey is the one we wish to remove the reference to
1018 GroupKey pointedGroupKey = null;
1019 int i = 0;
1020 for (GroupKey gk : foundChain) {
1021 if (i++ == 1) {
1022 pointedGroupKey = gk;
1023 break;
1024 }
1025 }
1026 Group pointedGroup = groupService.getGroup(deviceId, pointedGroupKey);
1027 GroupBucket bucket = DefaultGroupBucket.createSelectGroupBucket(
1028 DefaultTrafficTreatment.builder()
1029 .group(pointedGroup.id())
1030 .build());
1031 GroupBuckets removeBuckets = new GroupBuckets(Collections
1032 .singletonList(bucket));
1033 log.debug("Removing buckets from group id {} for next id {} in device {}",
1034 modGroup.id(), nextObjective.id(), deviceId);
1035 groupService.removeBucketsFromGroup(deviceId, modGroupKey,
1036 removeBuckets, modGroupKey,
1037 nextObjective.appId());
1038 //update store
1039 allgkeys.remove(index);
1040 flowObjectiveStore.putNextGroup(nextObjective.id(),
1041 new OfdpaNextGroup(allgkeys, nextObjective));
1042 } else {
1043 log.warn("Could not find appropriate group-chain for removing bucket"
1044 + " for next id {} in dev:{}", nextObjective.id(), deviceId);
1045 }
1046 }
1047
1048 /**
1049 * Removes all groups in multiple possible group-chains that represent the next
1050 * objective.
1051 *
1052 * @param nextObjective the next objective to remove
1053 * @param next the NextGroup that represents the existing group-chain for
1054 * this next objective
1055 */
1056 protected void removeGroup(NextObjective nextObjective, NextGroup next) {
Charles Chan361154b2016-03-24 10:23:39 -07001057 List<Deque<GroupKey>> allgkeys = Ofdpa2Pipeline.appKryo.deserialize(next.data());
Charles Chanfc5c7802016-05-17 13:13:55 -07001058
1059 List<GroupKey> groupKeys = allgkeys.stream()
1060 .map(Deque::getFirst).collect(Collectors.toList());
1061 pendingRemoveNextObjectives.put(nextObjective, groupKeys);
1062
Charles Chan188ebf52015-12-23 00:15:11 -08001063 allgkeys.forEach(groupChain -> groupChain.forEach(groupKey ->
1064 groupService.removeGroup(deviceId, groupKey, nextObjective.appId())));
1065 flowObjectiveStore.removeNextGroup(nextObjective.id());
1066 }
1067
Saurav Das8be4e3a2016-03-11 17:19:07 -08001068 //////////////////////////////////////
1069 // Helper Methods and Classes
1070 //////////////////////////////////////
1071
1072 private void updatePendingNextObjective(GroupKey key, OfdpaNextGroup value) {
1073 List<OfdpaNextGroup> nextList = new CopyOnWriteArrayList<OfdpaNextGroup>();
1074 nextList.add(value);
Charles Chanfc5c7802016-05-17 13:13:55 -07001075 List<OfdpaNextGroup> ret = pendingAddNextObjectives.asMap()
Saurav Das8be4e3a2016-03-11 17:19:07 -08001076 .putIfAbsent(key, nextList);
1077 if (ret != null) {
1078 ret.add(value);
1079 }
1080 }
1081
Charles Chan425854b2016-04-11 15:32:12 -07001082 protected void updatePendingGroups(GroupKey gkey, GroupChainElem gce) {
Saurav Das8be4e3a2016-03-11 17:19:07 -08001083 Set<GroupChainElem> gceSet = Collections.newSetFromMap(
1084 new ConcurrentHashMap<GroupChainElem, Boolean>());
1085 gceSet.add(gce);
1086 Set<GroupChainElem> retval = pendingGroups.putIfAbsent(gkey, gceSet);
1087 if (retval != null) {
1088 retval.add(gce);
1089 }
1090 }
1091
Charles Chan188ebf52015-12-23 00:15:11 -08001092 /**
1093 * Processes next element of a group chain. Assumption is that if this
1094 * group points to another group, the latter has already been created
1095 * and this driver has received notification for it. A second assumption is
1096 * that if there is another group waiting for this group then the appropriate
1097 * stores already have the information to act upon the notification for the
1098 * creation of this group.
1099 * <p>
1100 * The processing of the GroupChainElement depends on the number of groups
1101 * this element is waiting on. For all group types other than SIMPLE, a
1102 * GroupChainElement could be waiting on multiple groups.
1103 *
1104 * @param gce the group chain element to be processed next
1105 */
1106 private void processGroupChain(GroupChainElem gce) {
1107 int waitOnGroups = gce.decrementAndGetGroupsWaitedOn();
1108 if (waitOnGroups != 0) {
1109 log.debug("GCE: {} not ready to be processed", gce);
1110 return;
1111 }
1112 log.debug("GCE: {} ready to be processed", gce);
1113 if (gce.addBucketToGroup) {
1114 groupService.addBucketsToGroup(gce.groupDescription.deviceId(),
1115 gce.groupDescription.appCookie(),
1116 gce.groupDescription.buckets(),
1117 gce.groupDescription.appCookie(),
1118 gce.groupDescription.appId());
1119 } else {
1120 groupService.addGroup(gce.groupDescription);
1121 }
1122 }
1123
1124 private class GroupChecker implements Runnable {
1125 @Override
1126 public void run() {
1127 Set<GroupKey> keys = pendingGroups.keySet().stream()
1128 .filter(key -> groupService.getGroup(deviceId, key) != null)
1129 .collect(Collectors.toSet());
Charles Chanfc5c7802016-05-17 13:13:55 -07001130 Set<GroupKey> otherkeys = pendingAddNextObjectives.asMap().keySet().stream()
Charles Chan188ebf52015-12-23 00:15:11 -08001131 .filter(otherkey -> groupService.getGroup(deviceId, otherkey) != null)
1132 .collect(Collectors.toSet());
1133 keys.addAll(otherkeys);
1134
Sho SHIMIZUa09e1bb2016-08-01 14:25:25 -07001135 keys.forEach(key ->
Charles Chanfc5c7802016-05-17 13:13:55 -07001136 processPendingAddGroupsOrNextObjs(key, false));
Charles Chan188ebf52015-12-23 00:15:11 -08001137 }
1138 }
1139
Saurav Das8be4e3a2016-03-11 17:19:07 -08001140 private class InnerGroupListener implements GroupListener {
1141 @Override
1142 public void event(GroupEvent event) {
1143 log.trace("received group event of type {}", event.type());
Charles Chanfc5c7802016-05-17 13:13:55 -07001144 switch (event.type()) {
1145 case GROUP_ADDED:
1146 processPendingAddGroupsOrNextObjs(event.subject().appCookie(), true);
1147 break;
1148 case GROUP_REMOVED:
1149 processPendingRemoveNextObjs(event.subject().appCookie());
1150 break;
1151 default:
1152 break;
Saurav Das8be4e3a2016-03-11 17:19:07 -08001153 }
1154 }
1155 }
1156
Charles Chanfc5c7802016-05-17 13:13:55 -07001157 private void processPendingAddGroupsOrNextObjs(GroupKey key, boolean added) {
Charles Chan188ebf52015-12-23 00:15:11 -08001158 //first check for group chain
1159 Set<GroupChainElem> gceSet = pendingGroups.remove(key);
1160 if (gceSet != null) {
1161 for (GroupChainElem gce : gceSet) {
Charles Chan216e3c82016-04-23 14:48:16 -07001162 log.debug("Group service {} group key {} in device {}. "
Saurav Das0fd79d92016-03-07 10:58:36 -08001163 + "Processing next group in group chain with group id 0x{}",
Charles Chan188ebf52015-12-23 00:15:11 -08001164 (added) ? "ADDED" : "processed",
1165 key, deviceId,
1166 Integer.toHexString(gce.groupDescription.givenGroupId()));
1167 processGroupChain(gce);
1168 }
1169 } else {
1170 // otherwise chain complete - check for waiting nextObjectives
Charles Chanfc5c7802016-05-17 13:13:55 -07001171 List<OfdpaNextGroup> nextGrpList = pendingAddNextObjectives.getIfPresent(key);
Charles Chan188ebf52015-12-23 00:15:11 -08001172 if (nextGrpList != null) {
Charles Chanfc5c7802016-05-17 13:13:55 -07001173 pendingAddNextObjectives.invalidate(key);
Charles Chan188ebf52015-12-23 00:15:11 -08001174 nextGrpList.forEach(nextGrp -> {
Charles Chan216e3c82016-04-23 14:48:16 -07001175 log.debug("Group service {} group key {} in device:{}. "
Saurav Das0fd79d92016-03-07 10:58:36 -08001176 + "Done implementing next objective: {} <<-->> gid:0x{}",
Charles Chan188ebf52015-12-23 00:15:11 -08001177 (added) ? "ADDED" : "processed",
1178 key, deviceId, nextGrp.nextObjective().id(),
1179 Integer.toHexString(groupService.getGroup(deviceId, key)
1180 .givenGroupId()));
Charles Chan361154b2016-03-24 10:23:39 -07001181 Ofdpa2Pipeline.pass(nextGrp.nextObjective());
Charles Chan188ebf52015-12-23 00:15:11 -08001182 flowObjectiveStore.putNextGroup(nextGrp.nextObjective().id(), nextGrp);
1183 // check if addBuckets waiting for this completion
1184 NextObjective pendBkt = pendingBuckets
1185 .remove(nextGrp.nextObjective().id());
1186 if (pendBkt != null) {
1187 addBucketToGroup(pendBkt, nextGrp);
1188 }
1189 });
1190 }
1191 }
1192 }
1193
Charles Chanfc5c7802016-05-17 13:13:55 -07001194 private void processPendingRemoveNextObjs(GroupKey key) {
1195 pendingRemoveNextObjectives.asMap().forEach((nextObjective, groupKeys) -> {
1196 if (groupKeys.isEmpty()) {
1197 pendingRemoveNextObjectives.invalidate(nextObjective);
1198 Ofdpa2Pipeline.pass(nextObjective);
1199 } else {
1200 groupKeys.remove(key);
1201 }
1202 });
1203 }
1204
Charles Chan425854b2016-04-11 15:32:12 -07001205 protected int getNextAvailableIndex() {
Saurav Das8be4e3a2016-03-11 17:19:07 -08001206 return (int) nextIndex.incrementAndGet();
1207 }
1208
Charles Chane849c192016-01-11 18:28:54 -08001209 /**
1210 * Returns a hash as the L2 Interface Group Key.
1211 *
1212 * Keep the lower 6-bit for port since port number usually smaller than 64.
1213 * Hash other information into remaining 28 bits.
1214 *
1215 * @param deviceId Device ID
1216 * @param vlanId VLAN ID
1217 * @param portNumber Port number
1218 * @return L2 interface group key
1219 */
Charles Chan425854b2016-04-11 15:32:12 -07001220 protected int l2InterfaceGroupKey(
Charles Chane849c192016-01-11 18:28:54 -08001221 DeviceId deviceId, VlanId vlanId, long portNumber) {
1222 int portLowerBits = (int) portNumber & PORT_LOWER_BITS_MASK;
1223 long portHigherBits = portNumber & PORT_HIGHER_BITS_MASK;
Charles Chand0fd5dc2016-02-16 23:14:49 -08001224 int hash = Objects.hash(deviceId, vlanId, portHigherBits);
Charles Chane849c192016-01-11 18:28:54 -08001225 return L2_INTERFACE_TYPE | (TYPE_MASK & hash << 6) | portLowerBits;
1226 }
1227
Charles Chan188ebf52015-12-23 00:15:11 -08001228 /**
1229 * Utility class for moving group information around.
1230 */
Charles Chan425854b2016-04-11 15:32:12 -07001231 protected class GroupInfo {
Charles Chan5b9df8d2016-03-28 22:21:40 -07001232 /**
1233 * Description of the inner-most group of the group chain.
1234 * It is always an L2 interface group.
1235 */
1236 private GroupDescription innerMostGroupDesc;
Charles Chan188ebf52015-12-23 00:15:11 -08001237
Charles Chan5b9df8d2016-03-28 22:21:40 -07001238 /**
1239 * Description of the next group in the group chain.
1240 * It can be L2 interface, L3 interface, L3 unicast, L3 VPN group.
1241 * It is possible that nextGroup is the same as the innerMostGroup.
1242 */
1243 private GroupDescription nextGroupDesc;
1244
1245 GroupInfo(GroupDescription innerMostGroupDesc, GroupDescription nextGroupDesc) {
1246 this.innerMostGroupDesc = innerMostGroupDesc;
1247 this.nextGroupDesc = nextGroupDesc;
Charles Chan188ebf52015-12-23 00:15:11 -08001248 }
1249 }
1250
1251 /**
1252 * Represents an entire group-chain that implements a Next-Objective from
1253 * the application. The objective is represented as a list of deques, where
1254 * each deque is a separate chain of groups.
1255 * <p>
1256 * For example, an ECMP group with 3 buckets, where each bucket points to
1257 * a group chain of L3 Unicast and L2 interface groups will look like this:
1258 * <ul>
1259 * <li>List[0] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
1260 * <li>List[1] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
1261 * <li>List[2] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
1262 * </ul>
1263 * where the first element of each deque is the same, representing the
1264 * top level ECMP group, while every other element represents a unique groupKey.
1265 * <p>
1266 * Also includes information about the next objective that
1267 * resulted in this group-chain.
1268 *
1269 */
1270 protected class OfdpaNextGroup implements NextGroup {
1271 private final NextObjective nextObj;
1272 private final List<Deque<GroupKey>> gkeys;
1273
1274 public OfdpaNextGroup(List<Deque<GroupKey>> gkeys, NextObjective nextObj) {
1275 this.gkeys = gkeys;
1276 this.nextObj = nextObj;
1277 }
1278
Charles Chan188ebf52015-12-23 00:15:11 -08001279 public List<Deque<GroupKey>> groupKey() {
1280 return gkeys;
1281 }
1282
1283 public NextObjective nextObjective() {
1284 return nextObj;
1285 }
1286
1287 @Override
1288 public byte[] data() {
Charles Chan361154b2016-03-24 10:23:39 -07001289 return Ofdpa2Pipeline.appKryo.serialize(gkeys);
Charles Chan188ebf52015-12-23 00:15:11 -08001290 }
1291 }
1292
1293 /**
1294 * Represents a group element that is part of a chain of groups.
1295 * Stores enough information to create a Group Description to add the group
1296 * to the switch by requesting the Group Service. Objects instantiating this
1297 * class are meant to be temporary and live as long as it is needed to wait for
1298 * preceding groups in the group chain to be created.
1299 */
Charles Chan425854b2016-04-11 15:32:12 -07001300 protected class GroupChainElem {
Charles Chan188ebf52015-12-23 00:15:11 -08001301 private GroupDescription groupDescription;
1302 private AtomicInteger waitOnGroups;
1303 private boolean addBucketToGroup;
1304
1305 GroupChainElem(GroupDescription groupDescription, int waitOnGroups,
1306 boolean addBucketToGroup) {
1307 this.groupDescription = groupDescription;
1308 this.waitOnGroups = new AtomicInteger(waitOnGroups);
1309 this.addBucketToGroup = addBucketToGroup;
1310 }
1311
1312 /**
1313 * This methods atomically decrements the counter for the number of
1314 * groups this GroupChainElement is waiting on, for notifications from
1315 * the Group Service. When this method returns a value of 0, this
1316 * GroupChainElement is ready to be processed.
1317 *
1318 * @return integer indication of the number of notifications being waited on
1319 */
1320 int decrementAndGetGroupsWaitedOn() {
1321 return waitOnGroups.decrementAndGet();
1322 }
1323
1324 @Override
1325 public String toString() {
1326 return (Integer.toHexString(groupDescription.givenGroupId()) +
1327 " groupKey: " + groupDescription.appCookie() +
1328 " waiting-on-groups: " + waitOnGroups.get() +
1329 " addBucketToGroup: " + addBucketToGroup +
1330 " device: " + deviceId);
1331 }
1332 }
1333}