blob: 002df227394a5daf464b5a4f192a5d12f7d84db6 [file] [log] [blame]
Yi Tsengef19de12017-04-24 11:33:05 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Yi Tsengef19de12017-04-24 11:33:05 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.driver.pipeline.ofdpa;
18
19import com.google.common.collect.ImmutableList;
20import com.google.common.collect.Lists;
21import com.google.common.collect.Sets;
22import org.onlab.packet.VlanId;
23import org.onosproject.core.GroupId;
24import org.onosproject.net.DeviceId;
25import org.onosproject.net.PortNumber;
26import org.onosproject.net.behaviour.NextGroup;
27import org.onosproject.net.flow.DefaultTrafficTreatment;
28import org.onosproject.net.flow.TrafficSelector;
29import org.onosproject.net.flow.TrafficTreatment;
30import org.onosproject.net.flow.instructions.Instruction;
31import org.onosproject.net.flow.instructions.Instructions;
Saurav Das7bcbe702017-06-13 15:35:54 -070032import org.onosproject.net.flow.instructions.L2ModificationInstruction;
33import org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType;
Yi Tsengef19de12017-04-24 11:33:05 -070034import org.onosproject.net.flowobjective.NextObjective;
35import org.onosproject.net.group.DefaultGroupBucket;
36import org.onosproject.net.group.DefaultGroupKey;
37import org.onosproject.net.group.Group;
38import org.onosproject.net.group.GroupBucket;
39import org.onosproject.net.group.GroupDescription;
40import org.onosproject.net.group.GroupKey;
41import org.onosproject.net.group.GroupService;
42import org.slf4j.Logger;
43
Saurav Dasceccf242017-08-03 18:30:35 -070044import java.util.ArrayList;
Yi Tsengef19de12017-04-24 11:33:05 -070045import java.util.Deque;
46import java.util.List;
47import java.util.Objects;
48import java.util.Set;
49import java.util.concurrent.atomic.AtomicInteger;
50import java.util.stream.Collectors;
51
52import static org.onosproject.driver.pipeline.ofdpa.Ofdpa2Pipeline.isNotMplsBos;
53import static org.onosproject.driver.pipeline.ofdpa.OfdpaGroupHandlerUtility.OfdpaMplsGroupSubType.OFDPA_GROUP_TYPE_SHIFT;
54import static org.onosproject.driver.pipeline.ofdpa.OfdpaGroupHandlerUtility.OfdpaMplsGroupSubType.OFDPA_MPLS_SUBTYPE_SHIFT;
55import static org.onosproject.net.flowobjective.NextObjective.Type.HASHED;
56import static org.slf4j.LoggerFactory.getLogger;
57
58public final class OfdpaGroupHandlerUtility {
59 /*
60 * OFDPA requires group-id's to have a certain form.
61 * L2 Interface Groups have <4bits-0><12bits-vlanId><16bits-portId>
62 * L3 Unicast Groups have <4bits-2><28bits-index>
63 * MPLS Interface Groups have <4bits-9><4bits:0><24bits-index>
64 * L3 ECMP Groups have <4bits-7><28bits-index>
65 * L2 Flood Groups have <4bits-4><12bits-vlanId><16bits-index>
66 * L3 VPN Groups have <4bits-9><4bits-2><24bits-index>
67 */
Ray Milkey9c9cde42018-01-12 14:22:06 -080068 static final int L2_INTERFACE_TYPE = 0x00000000;
Jonghwan Hyun800d9d02018-04-09 09:40:50 -070069 static final int L2_UNFILTERED_TYPE = 0xb0000000;
Ray Milkey9c9cde42018-01-12 14:22:06 -080070 static final int L3_INTERFACE_TYPE = 0x50000000;
71 static final int L3_UNICAST_TYPE = 0x20000000;
72 static final int L3_MULTICAST_TYPE = 0x60000000;
73 static final int MPLS_INTERFACE_TYPE = 0x90000000;
74 static final int MPLS_L3VPN_SUBTYPE = 0x92000000;
75 static final int L3_ECMP_TYPE = 0x70000000;
76 static final int L2_FLOOD_TYPE = 0x40000000;
Yi Tsengef19de12017-04-24 11:33:05 -070077
Ray Milkey9c9cde42018-01-12 14:22:06 -080078 static final int TYPE_MASK = 0x0fffffff;
79 static final int SUBTYPE_MASK = 0x00ffffff;
80 static final int TYPE_VLAN_MASK = 0x0000ffff;
Yi Tsengef19de12017-04-24 11:33:05 -070081
Ray Milkey9c9cde42018-01-12 14:22:06 -080082 static final int THREE_BIT_MASK = 0x0fff;
83 static final int FOUR_BIT_MASK = 0xffff;
84 static final int PORT_LEN = 16;
Yi Tsengef19de12017-04-24 11:33:05 -070085
Ray Milkey9c9cde42018-01-12 14:22:06 -080086 static final int PORT_LOWER_BITS_MASK = 0x3f;
87 static final long PORT_HIGHER_BITS_MASK = ~PORT_LOWER_BITS_MASK;
Yi Tsengef19de12017-04-24 11:33:05 -070088
Ray Milkey9c9cde42018-01-12 14:22:06 -080089 static final String HEX_PREFIX = "0x";
90 private static final Logger log = getLogger(OfdpaGroupHandlerUtility.class);
Yi Tsengef19de12017-04-24 11:33:05 -070091
92 private OfdpaGroupHandlerUtility() {
93 // Utility classes should not have a public or default constructor.
94 }
95
96 /**
97 * Returns the outport in a traffic treatment.
98 *
99 * @param tt the treatment
100 * @return the PortNumber for the outport or null
101 */
Ray Milkey9c9cde42018-01-12 14:22:06 -0800102 static PortNumber readOutPortFromTreatment(TrafficTreatment tt) {
Yi Tsengef19de12017-04-24 11:33:05 -0700103 for (Instruction ins : tt.allInstructions()) {
104 if (ins.type() == Instruction.Type.OUTPUT) {
105 return ((Instructions.OutputInstruction) ins).port();
106 }
107 }
108 return null;
109 }
110
111 /**
Saurav Das7bcbe702017-06-13 15:35:54 -0700112 * Returns the MPLS label-id in a traffic treatment.
113 *
114 * @param tt the traffic treatment
115 * @return an integer representing the MPLS label-id, or -1 if not found
116 */
Ray Milkey9c9cde42018-01-12 14:22:06 -0800117 static int readLabelFromTreatment(TrafficTreatment tt) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700118 for (Instruction ins : tt.allInstructions()) {
119 if (ins.type() == Instruction.Type.L2MODIFICATION) {
120 L2ModificationInstruction insl2 = (L2ModificationInstruction) ins;
121 if (insl2.subtype() == L2SubType.MPLS_LABEL) {
122 return ((L2ModificationInstruction.ModMplsLabelInstruction) insl2)
123 .label().id();
124 }
125 }
126 }
127 return -1;
128 }
129
130 /**
Yi Tsengef19de12017-04-24 11:33:05 -0700131 * Helper enum to handle the different MPLS group
132 * types.
133 */
134 public enum OfdpaMplsGroupSubType {
135 MPLS_INTF((short) 0),
136 L2_VPN((short) 1),
137 L3_VPN((short) 2),
138 MPLS_TUNNEL_LABEL_1((short) 3),
139 MPLS_TUNNEL_LABEL_2((short) 4),
140 MPLS_SWAP_LABEL((short) 5),
141 MPLS_ECMP((short) 8);
142
143 private short value;
144 public static final int OFDPA_GROUP_TYPE_SHIFT = 28;
145 public static final int OFDPA_MPLS_SUBTYPE_SHIFT = 24;
146
147 OfdpaMplsGroupSubType(short value) {
148 this.value = value;
149 }
150
151 /**
152 * Gets the value as an short.
153 *
154 * @return the value as an short
155 */
156 public short getValue() {
157 return this.value;
158 }
159
160 }
161
162 /**
163 * Creates MPLS Label group id given a sub type and
164 * the index.
165 *
166 * @param subType the MPLS Label group sub type
167 * @param index the index of the group
168 * @return the OFDPA group id
169 */
170 public static Integer makeMplsLabelGroupId(OfdpaMplsGroupSubType subType, int index) {
171 index = index & 0x00FFFFFF;
172 return index | (9 << OFDPA_GROUP_TYPE_SHIFT) | (subType.value << OFDPA_MPLS_SUBTYPE_SHIFT);
173 }
174
175 /**
176 * Creates MPLS Forwarding group id given a sub type and
177 * the index.
178 *
179 * @param subType the MPLS forwarding group sub type
180 * @param index the index of the group
181 * @return the OFDPA group id
182 */
183 public static Integer makeMplsForwardingGroupId(OfdpaMplsGroupSubType subType, int index) {
184 index = index & 0x00FFFFFF;
185 return index | (10 << OFDPA_GROUP_TYPE_SHIFT) | (subType.value << OFDPA_MPLS_SUBTYPE_SHIFT);
186 }
187
188 /**
Saurav Das7bcbe702017-06-13 15:35:54 -0700189 * Returns the set of existing output ports in the group represented by
190 * allActiveKeys.
Yi Tsengef19de12017-04-24 11:33:05 -0700191 *
192 * @param allActiveKeys list of group key chain
193 * @param groupService the group service to get group information
194 * @param deviceId the device id to get group
195 * @return a set of output port from the list of group key chain
196 */
197 public static Set<PortNumber> getExistingOutputPorts(List<Deque<GroupKey>> allActiveKeys,
198 GroupService groupService,
199 DeviceId deviceId) {
200 Set<PortNumber> existingPorts = Sets.newHashSet();
201
202 allActiveKeys.forEach(keyChain -> {
203 GroupKey ifaceGroupKey = keyChain.peekLast();
204 Group ifaceGroup = groupService.getGroup(deviceId, ifaceGroupKey);
205 if (ifaceGroup != null && !ifaceGroup.buckets().buckets().isEmpty()) {
206 ifaceGroup.buckets().buckets().forEach(bucket -> {
207 PortNumber portNumber = readOutPortFromTreatment(bucket.treatment());
208 if (portNumber != null) {
209 existingPorts.add(portNumber);
210 }
211 });
212 }
213 });
214 return existingPorts;
215 }
216
217 /**
Saurav Dasceccf242017-08-03 18:30:35 -0700218 * Returns a list of all indices in the allActiveKeys list (that represents
219 * a group) if the list element (a bucket or group-chain) has treatments
220 * that match the given outport and label.
Saurav Das7bcbe702017-06-13 15:35:54 -0700221 *
222 * @param allActiveKeys the representation of the group
223 * @param groupService groups service for querying group information
224 * @param deviceId the device id for the device that contains the group
225 * @param portToMatch the port to match in the group buckets
226 * @param labelToMatch the MPLS label-id to match in the group buckets
Saurav Dasceccf242017-08-03 18:30:35 -0700227 * @return a list of indexes in the allActiveKeys list where the list element
228 * has treatments that match the given portToMatch and labelToMatch.
229 * Could be empty if no list elements were found to match the given
230 * port and label.
Saurav Das7bcbe702017-06-13 15:35:54 -0700231 */
Saurav Dasceccf242017-08-03 18:30:35 -0700232 public static List<Integer> existingPortAndLabel(
233 List<Deque<GroupKey>> allActiveKeys,
Saurav Das7bcbe702017-06-13 15:35:54 -0700234 GroupService groupService,
235 DeviceId deviceId,
236 PortNumber portToMatch,
237 int labelToMatch) {
Saurav Dasceccf242017-08-03 18:30:35 -0700238 List<Integer> indices = new ArrayList<>();
239 int index = 0;
Saurav Das7bcbe702017-06-13 15:35:54 -0700240 for (Deque<GroupKey> keyChain : allActiveKeys) {
241 GroupKey ifaceGroupKey = keyChain.peekLast();
242 Group ifaceGroup = groupService.getGroup(deviceId, ifaceGroupKey);
243 if (ifaceGroup != null && !ifaceGroup.buckets().buckets().isEmpty()) {
244 PortNumber portNumber = readOutPortFromTreatment(
245 ifaceGroup.buckets().buckets().iterator().next().treatment());
246 if (portNumber != null && portNumber.equals(portToMatch)) {
247 // check for label in the 2nd group of this chain
248 GroupKey secondKey = (GroupKey) keyChain.toArray()[1];
249 Group secondGroup = groupService.getGroup(deviceId, secondKey);
250 if (secondGroup != null &&
251 !secondGroup.buckets().buckets().isEmpty()) {
252 int label = readLabelFromTreatment(
253 secondGroup.buckets().buckets()
254 .iterator().next().treatment());
255 if (label == labelToMatch) {
Saurav Dasceccf242017-08-03 18:30:35 -0700256 indices.add(index);
Saurav Das7bcbe702017-06-13 15:35:54 -0700257 }
258 }
259 }
260 }
Saurav Dasceccf242017-08-03 18:30:35 -0700261 index++;
Saurav Das7bcbe702017-06-13 15:35:54 -0700262 }
263
Saurav Dasceccf242017-08-03 18:30:35 -0700264 return indices;
Saurav Das7bcbe702017-06-13 15:35:54 -0700265 }
266
Pier Luigiec6ac422018-01-29 10:30:59 +0100267 /**
268 * Get indices to remove comparing next group with next objective.
269 *
270 * @param allActiveKeys the representation of the group
271 * @param nextObjective the next objective to verify
272 * @param groupService groups service for querying group information
273 * @param deviceId the device id for the device that contains the group
274 * @return a list of indexes in the allActiveKeys to remove.
275 */
276 public static List<Integer> indicesToRemoveFromNextGroup(List<Deque<GroupKey>> allActiveKeys,
277 NextObjective nextObjective,
278 GroupService groupService,
279 DeviceId deviceId) {
280 List<Integer> indicesToRemove = Lists.newArrayList();
281 int index = 0;
282 // Iterate over the chain in the next data
283 for (Deque<GroupKey> keyChain : allActiveKeys) {
284 // Valid chain should have at least two elements
285 if (keyChain.size() >= 2) {
286 // Get last group (l2if) and retrieve port number
287 GroupKey ifaceGroupKey = keyChain.peekLast();
288 Group ifaceGroup = groupService.getGroup(deviceId, ifaceGroupKey);
289 if (ifaceGroup != null && !ifaceGroup.buckets().buckets().isEmpty()) {
290 PortNumber portNumber = readOutPortFromTreatment(
291 ifaceGroup.buckets().buckets().iterator().next().treatment());
292 // If there is not a port number continue
293 if (portNumber != null) {
294 // check for label in the 2nd group of this chain
295 GroupKey secondKey = (GroupKey) keyChain.toArray()[1];
296 Group secondGroup = groupService.getGroup(deviceId, secondKey);
297 // If there is not a second group or there are no buckets continue
298 if (secondGroup != null && !secondGroup.buckets().buckets().isEmpty()) {
299 // Get label or -1
300 int label = readLabelFromTreatment(
301 secondGroup.buckets().buckets()
302 .iterator().next().treatment());
303 // Iterate over the next treatments looking for the port and the label
304 boolean matches = false;
305 for (TrafficTreatment t : nextObjective.next()) {
306 PortNumber tPort = readOutPortFromTreatment(t);
307 int tLabel = readLabelFromTreatment(t);
308 if (tPort != null && tPort.equals(portNumber) && tLabel == label) {
309 // We found it, exit
310 matches = true;
311 break;
312 }
313 }
314 // Not found, we have to remove it
315 if (!matches) {
316 indicesToRemove.add(index);
317 }
318 }
319 }
320 }
321 }
322 index++;
323 }
324 return indicesToRemove;
325 }
326
Saurav Das7bcbe702017-06-13 15:35:54 -0700327 /**
Yi Tsengef19de12017-04-24 11:33:05 -0700328 * The purpose of this function is to verify if the hashed next
329 * objective is supported by the current pipeline.
330 *
331 * @param nextObjective the hashed objective to verify
332 * @return true if the hashed objective is supported. Otherwise false.
333 */
334 public static boolean verifyHashedNextObjective(NextObjective nextObjective) {
335 // if it is not hashed, there is something wrong;
336 if (nextObjective.type() != HASHED) {
337 return false;
338 }
339 // The case non supported is the MPLS-ECMP. For now, we try
340 // to create a MPLS-ECMP for the transport of a VPWS. The
341 // necessary info are contained in the meta selector. In particular
342 // we are looking for the case of BoS==False;
343 TrafficSelector metaSelector = nextObjective.meta();
344 if (metaSelector != null && isNotMplsBos(metaSelector)) {
345 return false;
346 }
347
348 return true;
349 }
350
351 /**
352 * Generates a list of group buckets from given list of group information
353 * and group bucket type.
354 *
355 * @param groupInfos a list of group information
356 * @param bucketType group bucket type
357 * @return list of group bucket generate from group information
358 */
Ray Milkey9c9cde42018-01-12 14:22:06 -0800359 static List<GroupBucket> generateNextGroupBuckets(List<GroupInfo> groupInfos,
Yi Tsengef19de12017-04-24 11:33:05 -0700360 GroupDescription.Type bucketType) {
361 List<GroupBucket> newBuckets = Lists.newArrayList();
362
363 groupInfos.forEach(groupInfo -> {
364 GroupDescription groupDesc = groupInfo.nextGroupDesc();
365 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
366 treatmentBuilder.group(new GroupId(groupDesc.givenGroupId()));
367 GroupBucket newBucket = null;
368 switch (bucketType) {
369 case ALL:
370 newBucket =
371 DefaultGroupBucket.createAllGroupBucket(treatmentBuilder.build());
372 break;
373 case INDIRECT:
374 newBucket =
375 DefaultGroupBucket.createIndirectGroupBucket(treatmentBuilder.build());
376 break;
377 case SELECT:
378 newBucket =
379 DefaultGroupBucket.createSelectGroupBucket(treatmentBuilder.build());
380 break;
381 case FAILOVER:
382 // TODO: support failover bucket type
383 default:
384 log.warn("Unknown bucket type: {}", bucketType);
385 break;
386 }
387
388 if (newBucket != null) {
389 newBuckets.add(newBucket);
390 }
391
392 });
393
394 return ImmutableList.copyOf(newBuckets);
395 }
396
Jonghwan Hyunf810a7a2018-02-12 16:43:45 +0900397 static List<GroupBucket> createL3MulticastBucket(List<GroupInfo> groupInfos) {
398 List<GroupBucket> l3McastBuckets = new ArrayList<>();
399 // For each inner group
400 groupInfos.forEach(groupInfo -> {
401 // Points to L3 interface group if there is one.
402 // Otherwise points to L2 interface group directly.
403 GroupDescription nextGroupDesc = (groupInfo.nextGroupDesc() != null) ?
404 groupInfo.nextGroupDesc() : groupInfo.innerMostGroupDesc();
405 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
406 ttb.group(new GroupId(nextGroupDesc.givenGroupId()));
407 GroupBucket abucket = DefaultGroupBucket.createAllGroupBucket(ttb.build());
408 l3McastBuckets.add(abucket);
409 });
410 // Done return the new list of buckets
411 return l3McastBuckets;
412 }
413
414 static Group retrieveTopLevelGroup(List<Deque<GroupKey>> allActiveKeys,
415 DeviceId deviceId,
416 GroupService groupService,
417 int nextid) {
418 GroupKey topLevelGroupKey;
419 if (!allActiveKeys.isEmpty()) {
420 topLevelGroupKey = allActiveKeys.get(0).peekFirst();
421 } else {
422 log.warn("Could not determine top level group while processing"
423 + "next:{} in dev:{}", nextid, deviceId);
424 return null;
425 }
426 Group topGroup = groupService.getGroup(deviceId, topLevelGroupKey);
427 if (topGroup == null) {
428 log.warn("Could not find top level group while processing "
429 + "next:{} in dev:{}", nextid, deviceId);
430 }
431 return topGroup;
432 }
433
Yi Tsengef19de12017-04-24 11:33:05 -0700434 /**
435 * Extracts VlanId from given group ID.
436 *
437 * @param groupId the group ID
438 * @return vlan id of the group
439 */
440 public static VlanId extractVlanIdFromGroupId(int groupId) {
441 // Extract the 9th to 20th bit from group id as vlan id.
442 short vlanId = (short) ((groupId & 0x0fff0000) >> 16);
443 return VlanId.vlanId(vlanId);
444 }
445
446 public static GroupKey l2FloodGroupKey(VlanId vlanId, DeviceId deviceId) {
447 int hash = Objects.hash(deviceId, vlanId);
448 hash = L2_FLOOD_TYPE | TYPE_MASK & hash;
449 return new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(hash));
450 }
451
452 public static int l2GroupId(VlanId vlanId, long portNum) {
453 return L2_INTERFACE_TYPE | (vlanId.toShort() << 16) | (int) portNum;
454 }
455
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700456 public static int l2UnfilteredGroupId(long portNum) {
457 return L2_UNFILTERED_TYPE | (int) portNum;
458 }
459
Yi Tsengef19de12017-04-24 11:33:05 -0700460 /**
461 * Returns a hash as the L2 Interface Group Key.
462 *
463 * Keep the lower 6-bit for port since port number usually smaller than 64.
464 * Hash other information into remaining 28 bits.
465 *
466 * @param deviceId Device ID
467 * @param vlanId VLAN ID
468 * @param portNumber Port number
469 * @return L2 interface group key
470 */
471 public static int l2InterfaceGroupKey(DeviceId deviceId, VlanId vlanId, long portNumber) {
472 int portLowerBits = (int) portNumber & PORT_LOWER_BITS_MASK;
473 long portHigherBits = portNumber & PORT_HIGHER_BITS_MASK;
474 int hash = Objects.hash(deviceId, vlanId, portHigherBits);
475 return L2_INTERFACE_TYPE | (TYPE_MASK & hash << 6) | portLowerBits;
476 }
477
478 /**
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700479 * Returns a hash as the L2 Unfiltered Interface Group Key.
480 *
481 * Keep the lower 6-bit for port since port number usually smaller than 64.
482 * Hash other information into remaining 28 bits.
483 *
484 * @param deviceId Device ID
485 * @param portNumber Port number
486 * @return L2 unfiltered interface group key
487 */
488 public static int l2UnfilteredGroupKey(DeviceId deviceId, long portNumber) {
489 int portLowerBits = (int) portNumber & PORT_LOWER_BITS_MASK;
490 long portHigherBits = portNumber & PORT_HIGHER_BITS_MASK;
491 int hash = Objects.hash(deviceId, portHigherBits);
492 return L2_UNFILTERED_TYPE | (TYPE_MASK & hash << 6) | portLowerBits;
493 }
494
495 /**
Yi Tsengef19de12017-04-24 11:33:05 -0700496 * Utility class for moving group information around.
497 *
498 * Example: Suppose we are trying to create a group-chain A-B-C-D, where
499 * A is the top level group, and D is the inner-most group, typically L2 Interface.
500 * The innerMostGroupDesc is always D. At various stages of the creation
501 * process the nextGroupDesc may be C or B. The nextGroupDesc exists to
502 * inform the referencing group about which group it needs to point to,
503 * and wait for. In some cases the group chain may simply be A-B. In this case,
504 * both innerMostGroupDesc and nextGroupDesc will be B.
505 */
506 public static class GroupInfo {
507 /**
508 * Description of the inner-most group of the group chain.
509 * It is always an L2 interface group.
510 */
511 private GroupDescription innerMostGroupDesc;
512
513 /**
514 * Description of the next group in the group chain.
515 * It can be L2 interface, L3 interface, L3 unicast, L3 VPN group.
516 * It is possible that nextGroupDesc is the same as the innerMostGroup.
517 */
518 private GroupDescription nextGroupDesc;
519
520 GroupInfo(GroupDescription innerMostGroupDesc, GroupDescription nextGroupDesc) {
521 this.innerMostGroupDesc = innerMostGroupDesc;
522 this.nextGroupDesc = nextGroupDesc;
523 }
524
525 /**
526 * Getter for innerMostGroupDesc.
527 *
528 * @return the inner most group description
529 */
530 public GroupDescription innerMostGroupDesc() {
531 return innerMostGroupDesc;
532 }
533
534 /**
535 * Getter for the next group description.
536 *
537 * @return the next group description
538 */
539 public GroupDescription nextGroupDesc() {
540 return nextGroupDesc;
541 }
542
543 /**
544 * Setter of nextGroupDesc.
545 *
546 * @param nextGroupDesc the given value to set
547 */
548 public void nextGroupDesc(GroupDescription nextGroupDesc) {
549 this.nextGroupDesc = nextGroupDesc;
550 }
551 }
552
553 /**
554 * Represents an entire group-chain that implements a Next-Objective from
555 * the application. The objective is represented as a list of deques, where
556 * each deque is a separate chain of groups.
557 * <p>
558 * For example, an ECMP group with 3 buckets, where each bucket points to
559 * a group chain of L3 Unicast and L2 interface groups will look like this:
560 * <ul>
561 * <li>List[0] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
562 * <li>List[1] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
563 * <li>List[2] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
564 * </ul>
565 * where the first element of each deque is the same, representing the
566 * top level ECMP group, while every other element represents a unique groupKey.
567 * <p>
568 * Also includes information about the next objective that
569 * resulted in these group-chains.
570 *
571 */
572 public static class OfdpaNextGroup implements NextGroup {
573 private final NextObjective nextObj;
574 private final List<Deque<GroupKey>> gkeys;
575
576 public OfdpaNextGroup(List<Deque<GroupKey>> gkeys, NextObjective nextObj) {
577 this.nextObj = nextObj;
578 this.gkeys = gkeys;
579 }
580
581 public NextObjective nextObjective() {
582 return nextObj;
583 }
584
Saurav Dasc88d4662017-05-15 15:34:25 -0700585 public List<Deque<GroupKey>> allKeys() {
586 return gkeys;
587 }
588
Yi Tsengef19de12017-04-24 11:33:05 -0700589 @Override
590 public byte[] data() {
591 return Ofdpa2Pipeline.appKryo.serialize(gkeys);
592 }
593 }
594
595 /**
596 * Represents a group element that is part of a chain of groups.
597 * Stores enough information to create a Group Description to add the group
598 * to the switch by requesting the Group Service. Objects instantiating this
599 * class are meant to be temporary and live as long as it is needed to wait for
600 * referenced groups in the group chain to be created.
601 */
602 public static class GroupChainElem {
603 private GroupDescription groupDescription;
604 private AtomicInteger waitOnGroups;
605 private boolean addBucketToGroup;
606 private DeviceId deviceId;
607
608 public GroupChainElem(GroupDescription groupDescription, int waitOnGroups,
609 boolean addBucketToGroup, DeviceId deviceId) {
610 this.groupDescription = groupDescription;
611 this.waitOnGroups = new AtomicInteger(waitOnGroups);
612 this.addBucketToGroup = addBucketToGroup;
613 this.deviceId = deviceId;
614 }
615
616 /**
617 * This method atomically decrements the counter for the number of
618 * groups this GroupChainElement is waiting on, for notifications from
619 * the Group Service. When this method returns a value of 0, this
620 * GroupChainElement is ready to be processed.
621 *
622 * @return integer indication of the number of notifications being waited on
623 */
624 int decrementAndGetGroupsWaitedOn() {
625 return waitOnGroups.decrementAndGet();
626 }
627
628 public GroupDescription groupDescription() {
629 return groupDescription;
630 }
631
632 public boolean addBucketToGroup() {
633 return addBucketToGroup;
634 }
635
636 @Override
637 public String toString() {
638 return (Integer.toHexString(groupDescription.givenGroupId()) +
639 " groupKey: " + groupDescription.appCookie() +
640 " waiting-on-groups: " + waitOnGroups.get() +
641 " addBucketToGroup: " + addBucketToGroup +
642 " device: " + deviceId);
643 }
644 }
645
646 public static class GroupChecker implements Runnable {
Ray Milkey9c9cde42018-01-12 14:22:06 -0800647 final Logger log = getLogger(getClass());
Yi Tsengef19de12017-04-24 11:33:05 -0700648 private Ofdpa2GroupHandler groupHandler;
649
650 public GroupChecker(Ofdpa2GroupHandler groupHandler) {
651 this.groupHandler = groupHandler;
652 }
653
654 @Override
655 public void run() {
Pier Luigi07532ab2018-01-12 16:03:49 +0100656 // GroupChecker execution needs to be protected
657 // from unhandled exceptions
658 try {
Saurav Das2f2c9d02018-04-07 16:51:09 -0700659 if (groupHandler.pendingGroups.size() != 0) {
660 log.debug("pending groups being checked: {}",
661 groupHandler.pendingGroups.asMap().keySet());
Pier Luigi07532ab2018-01-12 16:03:49 +0100662 }
Saurav Das2f2c9d02018-04-07 16:51:09 -0700663 if (groupHandler.pendingAddNextObjectives.size() != 0) {
Pier Luigi07532ab2018-01-12 16:03:49 +0100664 log.debug("pending add-next-obj being checked: {}",
Saurav Das2f2c9d02018-04-07 16:51:09 -0700665 groupHandler.pendingAddNextObjectives.asMap().keySet());
Pier Luigi07532ab2018-01-12 16:03:49 +0100666 }
Saurav Das2f2c9d02018-04-07 16:51:09 -0700667 if (groupHandler.pendingRemoveNextObjectives.size() != 0) {
668 log.debug("pending remove-next-obj being checked: {}",
669 groupHandler.pendingRemoveNextObjectives.asMap().values());
670 }
671 if (groupHandler.pendingUpdateNextObjectives.size() != 0) {
672 log.debug("pending update-next-obj being checked: {}",
673 groupHandler.pendingUpdateNextObjectives.keySet());
674 }
675
676 Set<GroupKey> keys = groupHandler.pendingGroups.asMap().keySet()
677 .stream()
678 .filter(key -> groupHandler.groupService
679 .getGroup(groupHandler.deviceId, key) != null)
Pier Luigi07532ab2018-01-12 16:03:49 +0100680 .collect(Collectors.toSet());
Saurav Das2f2c9d02018-04-07 16:51:09 -0700681 Set<GroupKey> otherkeys = groupHandler.pendingAddNextObjectives
682 .asMap().keySet().stream()
683 .filter(otherkey -> groupHandler.groupService
684 .getGroup(groupHandler.deviceId, otherkey) != null)
Pier Luigi07532ab2018-01-12 16:03:49 +0100685 .collect(Collectors.toSet());
686 keys.addAll(otherkeys);
Pier Luigi07532ab2018-01-12 16:03:49 +0100687 keys.forEach(key -> groupHandler.processPendingAddGroupsOrNextObjs(key, false));
Saurav Das2f2c9d02018-04-07 16:51:09 -0700688
689 keys = groupHandler.pendingUpdateNextObjectives.keySet()
690 .stream()
691 .filter(key -> groupHandler.groupService
692 .getGroup(groupHandler.deviceId, key) != null)
693 .collect(Collectors.toSet());
694 keys.forEach(key -> groupHandler.processPendingUpdateNextObjs(key));
695
696 Set<GroupKey> k = Sets.newHashSet();
697 groupHandler.pendingRemoveNextObjectives
698 .asMap().values().stream().forEach(keylist -> {
699 k.addAll(keylist.stream()
700 .filter(key -> groupHandler.groupService
701 .getGroup(groupHandler.deviceId, key) == null)
702 .collect(Collectors.toSet()));
703 });
704 k.forEach(key -> groupHandler.processPendingRemoveNextObjs(key));
705
Pier Luigi07532ab2018-01-12 16:03:49 +0100706 } catch (Exception exception) {
707 // Just log. It is safe for now.
708 log.warn("Uncaught exception is detected: {}", exception.getMessage());
709 log.debug("Uncaught exception is detected (full stack trace): ", exception);
710 }
Yi Tsengef19de12017-04-24 11:33:05 -0700711 }
712 }
713}