blob: a9ebd249f760b1c89a469cd7ea672c608425ae0d [file] [log] [blame]
Brian O'Connor7cbbbb72016-04-09 02:13:23 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Brian O'Connor7cbbbb72016-04-09 02:13:23 -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 */
Yi Tsengef19de12017-04-24 11:33:05 -070016package org.onosproject.driver.pipeline.ofdpa;
Charles Chan188ebf52015-12-23 00:15:11 -080017
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;
Yi Tseng78f51f42017-02-02 13:54:58 -080024import com.google.common.collect.Sets;
Charles Chan188ebf52015-12-23 00:15:11 -080025import org.onlab.osgi.ServiceDirectory;
Charles Chan5b9df8d2016-03-28 22:21:40 -070026import org.onlab.packet.IpPrefix;
Charles Chan5270ed02016-01-30 23:22:37 -080027import org.onlab.packet.MacAddress;
Charles Chan188ebf52015-12-23 00:15:11 -080028import org.onlab.packet.MplsLabel;
29import org.onlab.packet.VlanId;
30import org.onosproject.core.ApplicationId;
Yi Tseng78f51f42017-02-02 13:54:58 -080031import org.onosproject.core.GroupId;
Charles Chan367c1c12018-10-19 16:23:28 -070032import org.onosproject.driver.extensions.Ofdpa3AllowVlanTranslationType;
33import org.onosproject.driver.extensions.OfdpaSetAllowVlanTranslation;
Charles Chan32562522016-04-07 14:37:14 -070034import org.onosproject.driver.extensions.OfdpaSetVlanVid;
Charles Chan188ebf52015-12-23 00:15:11 -080035import org.onosproject.net.DeviceId;
36import org.onosproject.net.PortNumber;
37import org.onosproject.net.behaviour.NextGroup;
38import org.onosproject.net.behaviour.PipelinerContext;
39import org.onosproject.net.flow.DefaultTrafficTreatment;
40import org.onosproject.net.flow.TrafficSelector;
41import org.onosproject.net.flow.TrafficTreatment;
42import org.onosproject.net.flow.criteria.Criterion;
Charles Chan367c1c12018-10-19 16:23:28 -070043import org.onosproject.net.flow.criteria.PortCriterion;
Pier Ventre42287df2016-11-09 14:17:26 -080044import org.onosproject.net.flow.criteria.TunnelIdCriterion;
Charles Chan188ebf52015-12-23 00:15:11 -080045import org.onosproject.net.flow.criteria.VlanIdCriterion;
46import org.onosproject.net.flow.instructions.Instruction;
47import org.onosproject.net.flow.instructions.Instructions;
Saurav Das9df5b7c2017-08-14 16:44:43 -070048import org.onosproject.net.flow.instructions.Instructions.GroupInstruction;
Charles Chan188ebf52015-12-23 00:15:11 -080049import org.onosproject.net.flow.instructions.L2ModificationInstruction;
Yi Tseng47f82dc2017-03-05 22:48:39 -080050import org.onosproject.net.flowobjective.DefaultNextObjective;
Charles Chan367c1c12018-10-19 16:23:28 -070051import org.onosproject.net.flowobjective.DefaultNextTreatment;
Charles Chan188ebf52015-12-23 00:15:11 -080052import org.onosproject.net.flowobjective.FlowObjectiveStore;
Charles Chan367c1c12018-10-19 16:23:28 -070053import org.onosproject.net.flowobjective.IdNextTreatment;
Charles Chan188ebf52015-12-23 00:15:11 -080054import org.onosproject.net.flowobjective.NextObjective;
Charles Chan367c1c12018-10-19 16:23:28 -070055import org.onosproject.net.flowobjective.NextTreatment;
Saurav Dasc88d4662017-05-15 15:34:25 -070056import org.onosproject.net.flowobjective.Objective.Operation;
Yi Tseng47f82dc2017-03-05 22:48:39 -080057import org.onosproject.net.flowobjective.ObjectiveContext;
Charles Chan188ebf52015-12-23 00:15:11 -080058import org.onosproject.net.flowobjective.ObjectiveError;
59import org.onosproject.net.group.DefaultGroupBucket;
60import org.onosproject.net.group.DefaultGroupDescription;
61import org.onosproject.net.group.DefaultGroupKey;
62import org.onosproject.net.group.Group;
63import org.onosproject.net.group.GroupBucket;
64import org.onosproject.net.group.GroupBuckets;
65import org.onosproject.net.group.GroupDescription;
66import org.onosproject.net.group.GroupEvent;
67import org.onosproject.net.group.GroupKey;
68import org.onosproject.net.group.GroupListener;
69import org.onosproject.net.group.GroupService;
Saurav Das8be4e3a2016-03-11 17:19:07 -080070import org.onosproject.store.service.AtomicCounter;
71import org.onosproject.store.service.StorageService;
Charles Chan188ebf52015-12-23 00:15:11 -080072import org.slf4j.Logger;
73
74import java.util.ArrayDeque;
75import java.util.ArrayList;
Saurav Dasceccf242017-08-03 18:30:35 -070076import java.util.Arrays;
Charles Chan188ebf52015-12-23 00:15:11 -080077import java.util.Collection;
78import java.util.Collections;
79import java.util.Deque;
80import java.util.List;
Charles Chand0fd5dc2016-02-16 23:14:49 -080081import java.util.Objects;
Charles Chan367c1c12018-10-19 16:23:28 -070082import java.util.Optional;
Charles Chan188ebf52015-12-23 00:15:11 -080083import java.util.Set;
84import java.util.concurrent.ConcurrentHashMap;
85import java.util.concurrent.CopyOnWriteArrayList;
86import java.util.concurrent.Executors;
87import java.util.concurrent.ScheduledExecutorService;
88import java.util.concurrent.TimeUnit;
Charles Chan188ebf52015-12-23 00:15:11 -080089import java.util.stream.Collectors;
90
91import static org.onlab.util.Tools.groupedThreads;
Yi Tsengef19de12017-04-24 11:33:05 -070092import static org.onosproject.driver.pipeline.ofdpa.Ofdpa2Pipeline.*;
pier9469f3e2019-04-17 17:05:08 +020093import static org.onosproject.driver.pipeline.ofdpa.OfdpaPipelineUtility.*;
Yi Tsengef19de12017-04-24 11:33:05 -070094import static org.onosproject.driver.pipeline.ofdpa.OfdpaGroupHandlerUtility.*;
jayakumarthazhath655b9a82018-10-01 00:51:54 +053095import static org.onosproject.driver.pipeline.ofdpa.OfdpaGroupHandlerUtility.L2_MULTICAST_TYPE;
96import static org.onosproject.driver.pipeline.ofdpa.OfdpaGroupHandlerUtility.l2MulticastGroupKey;
Charles Chan367c1c12018-10-19 16:23:28 -070097import static org.onosproject.net.flow.criteria.Criterion.Type.IN_PORT;
Pier Ventre42287df2016-11-09 14:17:26 -080098import static org.onosproject.net.flow.criteria.Criterion.Type.TUNNEL_ID;
Pier Ventre140a8942016-11-02 07:26:38 -070099import static org.onosproject.net.flow.criteria.Criterion.Type.VLAN_VID;
Yi Tseng78f51f42017-02-02 13:54:58 -0800100import static org.onosproject.net.group.GroupDescription.Type.ALL;
Andreas Pantelopoulos7a8488c2018-01-22 16:00:28 -0800101import static org.onosproject.net.group.GroupDescription.Type.INDIRECT;
Yi Tseng78f51f42017-02-02 13:54:58 -0800102import static org.onosproject.net.group.GroupDescription.Type.SELECT;
Charles Chan188ebf52015-12-23 00:15:11 -0800103import static org.slf4j.LoggerFactory.getLogger;
104
105/**
Saurav Das961beb22017-03-29 19:09:17 -0700106 * Group handler that emulates Broadcom OF-DPA TTP.
Charles Chan188ebf52015-12-23 00:15:11 -0800107 */
Charles Chan361154b2016-03-24 10:23:39 -0700108public class Ofdpa2GroupHandler {
Yi Tsengef19de12017-04-24 11:33:05 -0700109 protected final Logger log = getLogger(getClass());
Yi Tsengef19de12017-04-24 11:33:05 -0700110 // Services, Stores
Charles Chan188ebf52015-12-23 00:15:11 -0800111 protected GroupService groupService;
Saurav Das8be4e3a2016-03-11 17:19:07 -0800112 protected StorageService storageService;
Yi Tsengef19de12017-04-24 11:33:05 -0700113 protected FlowObjectiveStore flowObjectiveStore;
Charles Chan188ebf52015-12-23 00:15:11 -0800114 // index number for group creation
Saurav Das8be4e3a2016-03-11 17:19:07 -0800115 private AtomicCounter nextIndex;
Yi Tsengef19de12017-04-24 11:33:05 -0700116 protected DeviceId deviceId;
Saurav Das2f2c9d02018-04-07 16:51:09 -0700117 Cache<GroupKey, List<OfdpaGroupHandlerUtility.OfdpaNextGroup>> pendingAddNextObjectives;
118 Cache<NextObjective, List<GroupKey>> pendingRemoveNextObjectives;
119 Cache<GroupKey, Set<OfdpaGroupHandlerUtility.GroupChainElem>> pendingGroups;
120 ConcurrentHashMap<GroupKey, Set<NextObjective>> pendingUpdateNextObjectives;
Yi Tseng47f82dc2017-03-05 22:48:39 -0800121 // local store for pending bucketAdds - by design there can be multiple
Charles Chan188ebf52015-12-23 00:15:11 -0800122 // pending bucket for a group
Yi Tseng47f82dc2017-03-05 22:48:39 -0800123 protected ConcurrentHashMap<Integer, Set<NextObjective>> pendingBuckets =
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700124 new ConcurrentHashMap<>();
Yi Tsengef19de12017-04-24 11:33:05 -0700125 private ScheduledExecutorService groupCheckerExecutor =
126 Executors.newScheduledThreadPool(2, groupedThreads("onos/pipeliner", "ofdpa-%d", log));
Charles Chan40132b32017-01-22 00:19:37 -0800127 /**
128 * Determines whether this pipeline support copy ttl instructions or not.
129 *
130 * @return true if copy ttl instructions are supported
131 */
132 protected boolean supportCopyTtl() {
133 return true;
134 }
135
136 /**
137 * Determines whether this pipeline support set mpls bos instruction or not.
138 *
139 * @return true if set mpls bos instruction is supported
140 */
141 protected boolean supportSetMplsBos() {
142 return true;
143 }
144
Charles Chan0f43e472017-02-14 14:00:16 -0800145 /**
146 * Determines whether this pipeline requires popping VLAN before pushing MPLS.
147 * <p>
148 * If required, pop vlan before push mpls and add an arbitrary vlan back afterward.
149 * MPLS interface group will substitute the arbitrary VLAN with expected VLAN later on.
150 *
151 * @return true if this pipeline requires popping VLAN before pushing MPLS
152 */
153 protected boolean requireVlanPopBeforeMplsPush() {
154 return false;
155 }
156
Charles Chan367c1c12018-10-19 16:23:28 -0700157 /**
158 * Determines whether this pipeline requires AllowVlanTransition in L2 unfiltered group.
159 *
160 * @return true if the AllowVlanTransition action is required
161 */
162 protected boolean requireAllowVlanTransition() {
163 return true;
164 }
165
Alex Yashchuk4caa8e82017-12-08 17:40:05 +0200166 public void init(DeviceId deviceId, PipelinerContext context) {
Yi Tsengef19de12017-04-24 11:33:05 -0700167 ServiceDirectory serviceDirectory = context.directory();
Charles Chan188ebf52015-12-23 00:15:11 -0800168 this.deviceId = deviceId;
169 this.flowObjectiveStore = context.store();
Charles Chan188ebf52015-12-23 00:15:11 -0800170 this.groupService = serviceDirectory.get(GroupService.class);
Saurav Das8be4e3a2016-03-11 17:19:07 -0800171 this.storageService = serviceDirectory.get(StorageService.class);
Madan Jampanid5714e02016-04-19 14:15:20 -0700172 this.nextIndex = storageService.getAtomicCounter("group-id-index-counter");
Charles Chan188ebf52015-12-23 00:15:11 -0800173
Charles Chanfc5c7802016-05-17 13:13:55 -0700174 pendingAddNextObjectives = CacheBuilder.newBuilder()
Charles Chan188ebf52015-12-23 00:15:11 -0800175 .expireAfterWrite(20, TimeUnit.SECONDS)
Yi Tsengef19de12017-04-24 11:33:05 -0700176 .removalListener((RemovalNotification<GroupKey, List<OfdpaNextGroup>> notification) -> {
177 if (notification.getCause() == RemovalCause.EXPIRED &&
178 Objects.nonNull(notification.getValue())) {
179 notification.getValue()
180 .forEach(ofdpaNextGrp ->
181 fail(ofdpaNextGrp.nextObjective(),
182 ObjectiveError.GROUPINSTALLATIONFAILED));
Charles Chanfc5c7802016-05-17 13:13:55 -0700183 }
184 }).build();
Charles Chan188ebf52015-12-23 00:15:11 -0800185
Charles Chanfc5c7802016-05-17 13:13:55 -0700186 pendingRemoveNextObjectives = CacheBuilder.newBuilder()
187 .expireAfterWrite(20, TimeUnit.SECONDS)
Yi Tsengef19de12017-04-24 11:33:05 -0700188 .removalListener((RemovalNotification<NextObjective, List<GroupKey>> notification) -> {
Charles Chanfc5c7802016-05-17 13:13:55 -0700189 if (notification.getCause() == RemovalCause.EXPIRED) {
Yi Tsengef19de12017-04-24 11:33:05 -0700190 fail(notification.getKey(),
191 ObjectiveError.GROUPREMOVALFAILED);
Charles Chan188ebf52015-12-23 00:15:11 -0800192 }
193 }).build();
Saurav Das961beb22017-03-29 19:09:17 -0700194 pendingGroups = CacheBuilder.newBuilder()
195 .expireAfterWrite(20, TimeUnit.SECONDS)
Yi Tsengef19de12017-04-24 11:33:05 -0700196 .removalListener((RemovalNotification<GroupKey, Set<GroupChainElem>> notification) -> {
Saurav Das961beb22017-03-29 19:09:17 -0700197 if (notification.getCause() == RemovalCause.EXPIRED) {
198 log.error("Unable to install group with key {} and pending GCEs: {}",
199 notification.getKey(), notification.getValue());
200 }
201 }).build();
Yi Tseng78f51f42017-02-02 13:54:58 -0800202 pendingUpdateNextObjectives = new ConcurrentHashMap<>();
Yi Tsengef19de12017-04-24 11:33:05 -0700203 GroupChecker groupChecker = new GroupChecker(this);
204 groupCheckerExecutor.scheduleAtFixedRate(groupChecker, 0, 500, TimeUnit.MILLISECONDS);
Charles Chan188ebf52015-12-23 00:15:11 -0800205 groupService.addListener(new InnerGroupListener());
206 }
207
Saurav Das8be4e3a2016-03-11 17:19:07 -0800208 //////////////////////////////////////
209 // Group Creation
210 //////////////////////////////////////
211
Yi Tsengef19de12017-04-24 11:33:05 -0700212 /**
213 * Adds a list of group chain by given NextObjective.
214 *
215 * @param nextObjective the NextObjective
216 */
Charles Chan188ebf52015-12-23 00:15:11 -0800217 protected void addGroup(NextObjective nextObjective) {
218 switch (nextObjective.type()) {
219 case SIMPLE:
220 Collection<TrafficTreatment> treatments = nextObjective.next();
221 if (treatments.size() != 1) {
222 log.error("Next Objectives of type Simple should only have a "
223 + "single Traffic Treatment. Next Objective Id:{}",
224 nextObjective.id());
Yi Tsengef19de12017-04-24 11:33:05 -0700225 fail(nextObjective, ObjectiveError.BADPARAMS);
Charles Chan188ebf52015-12-23 00:15:11 -0800226 return;
227 }
228 processSimpleNextObjective(nextObjective);
229 break;
230 case BROADCAST:
231 processBroadcastNextObjective(nextObjective);
232 break;
233 case HASHED:
Pier Ventre140a8942016-11-02 07:26:38 -0700234 if (!verifyHashedNextObjective(nextObjective)) {
235 log.error("Next Objectives of type hashed not supported. Next Objective Id:{}",
236 nextObjective.id());
Yi Tsengef19de12017-04-24 11:33:05 -0700237 fail(nextObjective, ObjectiveError.BADPARAMS);
Pier Ventre140a8942016-11-02 07:26:38 -0700238 return;
239 }
Charles Chan367c1c12018-10-19 16:23:28 -0700240 if (isL2Hash(nextObjective)) {
241 processL2HashedNextObjective(nextObjective);
242 return;
243 }
244 processEcmpHashedNextObjective(nextObjective);
Charles Chan188ebf52015-12-23 00:15:11 -0800245 break;
246 case FAILOVER:
Yi Tsengef19de12017-04-24 11:33:05 -0700247 fail(nextObjective, ObjectiveError.UNSUPPORTED);
Charles Chan188ebf52015-12-23 00:15:11 -0800248 log.warn("Unsupported next objective type {}", nextObjective.type());
249 break;
250 default:
Yi Tsengef19de12017-04-24 11:33:05 -0700251 fail(nextObjective, ObjectiveError.UNKNOWN);
Charles Chan188ebf52015-12-23 00:15:11 -0800252 log.warn("Unknown next objective type {}", nextObjective.type());
253 }
254 }
255
256 /**
jayakumarthazhath655b9a82018-10-01 00:51:54 +0530257 * Similar to processBroadcastNextObjective but handles L2 Multicast Next Objectives.
258 *
259 * @param nextObj NextObjective of L2_MULTICAST with chained NextObjectives for single homed access ports
260 */
261 private void processL2MulticastNextObjective(NextObjective nextObj) {
262
263 VlanId assignedVlan = readVlanFromSelector(nextObj.meta());
264 if (assignedVlan == null) {
265 log.warn("VLAN ID required by L2 multicast next objective is missing. Aborting group creation.");
266 fail(nextObj, ObjectiveError.BADPARAMS);
267 return;
268 }
269
270 // Group info should contain only single homed hosts for a given vlanId
271 List<GroupInfo> groupInfos = prepareL2InterfaceGroup(nextObj, assignedVlan);
272 createL2MulticastGroup(nextObj, assignedVlan, groupInfos);
273 }
274
275 private void createL2MulticastGroup(NextObjective nextObj, VlanId vlanId, List<GroupInfo> groupInfos) {
276 // Realize & represent L2 multicast group in OFDPA driver layer
277 // TODO : Need to identify significance of OfdpaNextGroup.
278 Integer l2MulticastGroupId = L2_MULTICAST_TYPE | (vlanId.toShort() << 16);
279 final GroupKey l2MulticastGroupKey = l2MulticastGroupKey(vlanId, deviceId);
280 List<Deque<GroupKey>> l2MulticastAllGroup = Lists.newArrayList();
281 groupInfos.forEach(groupInfo -> {
282 Deque<GroupKey> groupKeyChain = new ArrayDeque<>();
283 groupKeyChain.addFirst(groupInfo.innerMostGroupDesc().appCookie());
284 groupKeyChain.addFirst(l2MulticastGroupKey);
285 l2MulticastAllGroup.add(groupKeyChain);
286 });
287 OfdpaNextGroup ofdpaL2MulticastGroup = new OfdpaNextGroup(l2MulticastAllGroup, nextObj);
288 updatePendingNextObjective(l2MulticastGroupKey, ofdpaL2MulticastGroup);
289 // Group Chain Hierarchy creation using group service and thus in device level
290 List<GroupBucket> l2McastBuckets = new ArrayList<>();
291 groupInfos.forEach(groupInfo -> {
292 // Points to L2 interface group directly.
293 TrafficTreatment.Builder trafficTreatment = DefaultTrafficTreatment.builder();
294 trafficTreatment.group(new GroupId(groupInfo.innerMostGroupDesc().givenGroupId()));
295 GroupBucket bucket = DefaultGroupBucket.createAllGroupBucket(trafficTreatment.build());
296 l2McastBuckets.add(bucket);
297 });
298 GroupDescription l2MulticastGroupDescription =
299 new DefaultGroupDescription(
300 deviceId,
301 ALL,
302 new GroupBuckets(l2McastBuckets),
303 l2MulticastGroupKey,
304 l2MulticastGroupId,
305 nextObj.appId());
306 GroupChainElem l2MulticastGce = new GroupChainElem(l2MulticastGroupDescription,
307 groupInfos.size(), false, deviceId);
308 groupInfos.forEach(groupInfo -> {
309 updatePendingGroups(groupInfo.innerMostGroupDesc().appCookie(), l2MulticastGce);
310 groupService.addGroup(groupInfo.innerMostGroupDesc());
311 });
312 }
313
314 /**
Charles Chan188ebf52015-12-23 00:15:11 -0800315 * As per the OFDPA 2.0 TTP, packets are sent out of ports by using
Saurav Dasa4020382018-02-14 14:14:54 -0800316 * a chain of groups. The simple Next Objective passed in by the application
317 * is broken up into a group chain. The following chains can be created
318 * depending on the parameters in the Next Objective.
319 * 1. L2 Interface group (no chaining)
320 * 2. L3 Unicast group -> L2 Interface group
321 * 3. MPLS Interface group -> L2 Interface group
322 * 4. MPLS Swap group -> MPLS Interface group -> L2 Interface group
323 * 5. PW initiation group chain
Charles Chan188ebf52015-12-23 00:15:11 -0800324 *
325 * @param nextObj the nextObjective of type SIMPLE
326 */
327 private void processSimpleNextObjective(NextObjective nextObj) {
328 TrafficTreatment treatment = nextObj.next().iterator().next();
Andreas Pantelopoulos7a8488c2018-01-22 16:00:28 -0800329 // determine if plain L2 or L3->L2 or MPLS Swap -> MPLS Interface -> L2
Charles Chan188ebf52015-12-23 00:15:11 -0800330 boolean plainL2 = true;
Andreas Pantelopoulos7a8488c2018-01-22 16:00:28 -0800331 boolean mplsSwap = false;
332 MplsLabel mplsLabel = null;
Charles Chan188ebf52015-12-23 00:15:11 -0800333 for (Instruction ins : treatment.allInstructions()) {
334 if (ins.type() == Instruction.Type.L2MODIFICATION) {
335 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
336 if (l2ins.subtype() == L2ModificationInstruction.L2SubType.ETH_DST ||
337 l2ins.subtype() == L2ModificationInstruction.L2SubType.ETH_SRC) {
338 plainL2 = false;
Charles Chan188ebf52015-12-23 00:15:11 -0800339 }
Andreas Pantelopoulos7a8488c2018-01-22 16:00:28 -0800340 // mpls label in simple next objectives is used only to indicate
341 // a MPLS Swap group before the MPLS Interface Group
342 if (l2ins.subtype() == L2ModificationInstruction.L2SubType.MPLS_LABEL) {
343 mplsSwap = true;
344 mplsLabel = ((L2ModificationInstruction.ModMplsLabelInstruction) l2ins).label();
345 }
Charles Chan188ebf52015-12-23 00:15:11 -0800346 }
347 }
Charles Chan188ebf52015-12-23 00:15:11 -0800348 if (plainL2) {
349 createL2InterfaceGroup(nextObj);
350 return;
351 }
Saurav Dasa4020382018-02-14 14:14:54 -0800352 // In order to understand if it is a pseudowire related
Pier Ventre42287df2016-11-09 14:17:26 -0800353 // next objective we look for the tunnel id in the meta.
354 boolean isPw = false;
Pier Ventre140a8942016-11-02 07:26:38 -0700355 if (nextObj.meta() != null) {
Pier Ventre42287df2016-11-09 14:17:26 -0800356 TunnelIdCriterion tunnelIdCriterion = (TunnelIdCriterion) nextObj
357 .meta()
358 .getCriterion(TUNNEL_ID);
359 if (tunnelIdCriterion != null) {
360 isPw = true;
361 }
Pier Ventre140a8942016-11-02 07:26:38 -0700362 }
Andreas Pantelopoulos25db42f2018-02-08 12:42:06 -0800363 if (mplsSwap && !isPw) {
Saurav Dasa4020382018-02-14 14:14:54 -0800364 log.debug("Creating a MPLS Swap -> MPLS Interface -> L2 Interface group chain.");
Andreas Pantelopoulos7a8488c2018-01-22 16:00:28 -0800365 // break up simple next objective to GroupChain objects
366 GroupInfo groupInfo = createL2L3Chain(treatment, nextObj.id(),
367 nextObj.appId(), true,
368 nextObj.meta());
369 if (groupInfo == null) {
370 log.error("Could not process nextObj={} in dev:{}", nextObj.id(), deviceId);
371 fail(nextObj, ObjectiveError.BADPARAMS);
372 return;
373 }
Andreas Pantelopoulos7a8488c2018-01-22 16:00:28 -0800374 Deque<GroupKey> gkeyChain = new ArrayDeque<>();
Saurav Dasa4020382018-02-14 14:14:54 -0800375 gkeyChain.addFirst(groupInfo.innerMostGroupDesc().appCookie()); // l2 interface
376 gkeyChain.addFirst(groupInfo.nextGroupDesc().appCookie()); // mpls interface
Andreas Pantelopoulos7a8488c2018-01-22 16:00:28 -0800377 // creating the mpls swap group and adding it to the chain
Andreas Pantelopoulos7a8488c2018-01-22 16:00:28 -0800378 int nextGid = groupInfo.nextGroupDesc().givenGroupId();
379 int index = getNextAvailableIndex();
Saurav Dasa4020382018-02-14 14:14:54 -0800380 GroupDescription swapGroupDescription = createMplsSwap(
Andreas Pantelopoulos7a8488c2018-01-22 16:00:28 -0800381 nextGid,
382 OfdpaMplsGroupSubType.MPLS_SWAP_LABEL,
383 index,
384 mplsLabel,
385 nextObj.appId()
386 );
Saurav Dasa4020382018-02-14 14:14:54 -0800387 // ensure swap group is added after L2L3 chain
388 GroupKey swapGroupKey = swapGroupDescription.appCookie();
389 GroupChainElem swapChainElem = new GroupChainElem(swapGroupDescription,
390 1, false, deviceId);
391 updatePendingGroups(groupInfo.nextGroupDesc().appCookie(), swapChainElem);
392 gkeyChain.addFirst(swapGroupKey);
Saurav Dasa4020382018-02-14 14:14:54 -0800393 // ensure nextObjective waits on the outermost groupKey
394 List<Deque<GroupKey>> allGroupKeys = Lists.newArrayList();
395 allGroupKeys.add(gkeyChain);
396 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
397 updatePendingNextObjective(swapGroupKey, ofdpaGrp);
Andreas Pantelopoulos7a8488c2018-01-22 16:00:28 -0800398 // now we are ready to send the l2 groupDescription (inner), as all the stores
399 // that will get async replies have been updated. By waiting to update
400 // the stores, we prevent nasty race conditions.
401 groupService.addGroup(groupInfo.innerMostGroupDesc());
402 } else if (!isPw) {
Saurav Dasa4020382018-02-14 14:14:54 -0800403 boolean isMpls = false;
404 if (nextObj.meta() != null) {
405 isMpls = isNotMplsBos(nextObj.meta());
406 }
407 log.debug("Creating a {} -> L2 Interface group chain.",
408 (isMpls) ? "MPLS Interface" : "L3 Unicast");
Pier Ventre42287df2016-11-09 14:17:26 -0800409 // break up simple next objective to GroupChain objects
410 GroupInfo groupInfo = createL2L3Chain(treatment, nextObj.id(),
411 nextObj.appId(), isMpls,
412 nextObj.meta());
413 if (groupInfo == null) {
414 log.error("Could not process nextObj={} in dev:{}", nextObj.id(), deviceId);
Andreas Pantelopoulos7a8488c2018-01-22 16:00:28 -0800415 fail(nextObj, ObjectiveError.BADPARAMS);
Pier Ventre42287df2016-11-09 14:17:26 -0800416 return;
417 }
418 // create object for local and distributed storage
419 Deque<GroupKey> gkeyChain = new ArrayDeque<>();
Yi Tsengef19de12017-04-24 11:33:05 -0700420 gkeyChain.addFirst(groupInfo.innerMostGroupDesc().appCookie());
421 gkeyChain.addFirst(groupInfo.nextGroupDesc().appCookie());
Saurav Dasa4020382018-02-14 14:14:54 -0800422 List<Deque<GroupKey>> allGroupKeys = Lists.newArrayList();
423 allGroupKeys.add(gkeyChain);
424 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
Pier Ventre42287df2016-11-09 14:17:26 -0800425 // store l3groupkey with the ofdpaNextGroup for the nextObjective that depends on it
Yi Tsengef19de12017-04-24 11:33:05 -0700426 updatePendingNextObjective(groupInfo.nextGroupDesc().appCookie(), ofdpaGrp);
Pier Ventre42287df2016-11-09 14:17:26 -0800427 // now we are ready to send the l2 groupDescription (inner), as all the stores
428 // that will get async replies have been updated. By waiting to update
429 // the stores, we prevent nasty race conditions.
Yi Tsengef19de12017-04-24 11:33:05 -0700430 groupService.addGroup(groupInfo.innerMostGroupDesc());
Pier Ventre42287df2016-11-09 14:17:26 -0800431 } else {
432 // We handle the pseudo wire with a different a procedure.
433 // This procedure is meant to handle both initiation and
434 // termination of the pseudo wire.
435 processPwNextObjective(nextObj);
Charles Chan188ebf52015-12-23 00:15:11 -0800436 }
Charles Chan188ebf52015-12-23 00:15:11 -0800437 }
438
Charles Chan188ebf52015-12-23 00:15:11 -0800439 /**
440 * Creates a simple L2 Interface Group.
Charles Chan188ebf52015-12-23 00:15:11 -0800441 * @param nextObj the next Objective
442 */
443 private void createL2InterfaceGroup(NextObjective nextObj) {
Yi Tsengef19de12017-04-24 11:33:05 -0700444 VlanId assignedVlan = readVlanFromSelector(nextObj.meta());
Charles Chan5b9df8d2016-03-28 22:21:40 -0700445 if (assignedVlan == null) {
446 log.warn("VLAN ID required by simple next obj is missing. Abort.");
Yi Tsengef19de12017-04-24 11:33:05 -0700447 fail(nextObj, ObjectiveError.BADPARAMS);
Charles Chan188ebf52015-12-23 00:15:11 -0800448 return;
449 }
Charles Chan5b9df8d2016-03-28 22:21:40 -0700450 List<GroupInfo> groupInfos = prepareL2InterfaceGroup(nextObj, assignedVlan);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700451 // There is only one L2 interface group in this case
Yi Tsengef19de12017-04-24 11:33:05 -0700452 GroupDescription l2InterfaceGroupDesc = groupInfos.get(0).innerMostGroupDesc();
Charles Chan5b9df8d2016-03-28 22:21:40 -0700453 // Put all dependency information into allGroupKeys
454 List<Deque<GroupKey>> allGroupKeys = Lists.newArrayList();
455 Deque<GroupKey> gkeyChain = new ArrayDeque<>();
456 gkeyChain.addFirst(l2InterfaceGroupDesc.appCookie());
457 allGroupKeys.add(gkeyChain);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700458 // Point the next objective to this group
459 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
460 updatePendingNextObjective(l2InterfaceGroupDesc.appCookie(), ofdpaGrp);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700461 // Start installing the inner-most group
jayakumarthazhath655b9a82018-10-01 00:51:54 +0530462 groupService.addGroup(l2InterfaceGroupDesc); }
Charles Chan188ebf52015-12-23 00:15:11 -0800463
464 /**
Andreas Pantelopoulos7a8488c2018-01-22 16:00:28 -0800465 * Creates an Mpls group of type swap.
466 *
467 * @param nextGroupId the next group in the chain
468 * @param subtype the mpls swap label group subtype
469 * @param index the index of the group
470 * @param mplsLabel the mpls label to swap
471 * @param applicationId the application id
472 * @return the group description
473 */
474 protected GroupDescription createMplsSwap(int nextGroupId,
475 OfdpaMplsGroupSubType subtype,
476 int index,
477 MplsLabel mplsLabel,
478 ApplicationId applicationId) {
Andreas Pantelopoulos7a8488c2018-01-22 16:00:28 -0800479 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
Andreas Pantelopoulos7a8488c2018-01-22 16:00:28 -0800480 treatment.setMpls(mplsLabel);
Andreas Pantelopoulos7a8488c2018-01-22 16:00:28 -0800481 // We point the group to the next group.
482 treatment.group(new GroupId(nextGroupId));
483 GroupBucket groupBucket = DefaultGroupBucket
484 .createIndirectGroupBucket(treatment.build());
485 // Finally we build the group description.
486 int groupId = makeMplsLabelGroupId(subtype, index);
487 GroupKey groupKey = new DefaultGroupKey(
Saurav Dasa4020382018-02-14 14:14:54 -0800488 Ofdpa2Pipeline.appKryo.serialize(index));
Andreas Pantelopoulos7a8488c2018-01-22 16:00:28 -0800489 return new DefaultGroupDescription(
490 deviceId,
491 INDIRECT,
492 new GroupBuckets(Collections.singletonList(groupBucket)),
493 groupKey,
494 groupId,
Saurav Dasa4020382018-02-14 14:14:54 -0800495 applicationId);
Andreas Pantelopoulos7a8488c2018-01-22 16:00:28 -0800496 }
497
498 /**
Charles Chan188ebf52015-12-23 00:15:11 -0800499 * Creates one of two possible group-chains from the treatment
500 * passed in. Depending on the MPLS boolean, this method either creates
Charles Chan425854b2016-04-11 15:32:12 -0700501 * an L3Unicast Group --&gt; L2Interface Group, if mpls is false;
502 * or MPLSInterface Group --&gt; L2Interface Group, if mpls is true;
Charles Chan188ebf52015-12-23 00:15:11 -0800503 * The returned 'inner' group description is always the L2 Interface group.
504 *
505 * @param treatment that needs to be broken up to create the group chain
506 * @param nextId of the next objective that needs this group chain
507 * @param appId of the application that sent this next objective
508 * @param mpls determines if L3Unicast or MPLSInterface group is created
509 * @param meta metadata passed in by the application as part of the nextObjective
510 * @return GroupInfo containing the GroupDescription of the
511 * L2Interface group(inner) and the GroupDescription of the (outer)
512 * L3Unicast/MPLSInterface group. May return null if there is an
513 * error in processing the chain
514 */
Charles Chan425854b2016-04-11 15:32:12 -0700515 protected GroupInfo createL2L3Chain(TrafficTreatment treatment, int nextId,
Charles Chanf9e98652016-09-07 16:54:23 -0700516 ApplicationId appId, boolean mpls,
517 TrafficSelector meta) {
518 return createL2L3ChainInternal(treatment, nextId, appId, mpls, meta, true);
519 }
520
521 /**
522 * Internal implementation of createL2L3Chain.
523 * <p>
524 * The is_present bit in set_vlan_vid action is required to be 0 in OFDPA i12.
525 * Since it is non-OF spec, we need an extension treatment for that.
526 * The useSetVlanExtension must be set to false for OFDPA i12.
527 * </p>
528 *
529 * @param treatment that needs to be broken up to create the group chain
530 * @param nextId of the next objective that needs this group chain
531 * @param appId of the application that sent this next objective
532 * @param mpls determines if L3Unicast or MPLSInterface group is created
533 * @param meta metadata passed in by the application as part of the nextObjective
534 * @param useSetVlanExtension use the setVlanVid extension that has is_present bit set to 0.
535 * @return GroupInfo containing the GroupDescription of the
536 * L2Interface group(inner) and the GroupDescription of the (outer)
537 * L3Unicast/MPLSInterface group. May return null if there is an
538 * error in processing the chain
539 */
540 protected GroupInfo createL2L3ChainInternal(TrafficTreatment treatment, int nextId,
Pier Ventre42287df2016-11-09 14:17:26 -0800541 ApplicationId appId, boolean mpls,
542 TrafficSelector meta, boolean useSetVlanExtension) {
Charles Chan188ebf52015-12-23 00:15:11 -0800543 // for the l2interface group, get vlan and port info
544 // for the outer group, get the src/dst mac, and vlan info
545 TrafficTreatment.Builder outerTtb = DefaultTrafficTreatment.builder();
546 TrafficTreatment.Builder innerTtb = DefaultTrafficTreatment.builder();
547 VlanId vlanid = null;
548 long portNum = 0;
549 boolean setVlan = false, popVlan = false;
Yi Tseng78f51f42017-02-02 13:54:58 -0800550 MacAddress srcMac;
551 MacAddress dstMac;
Charles Chan188ebf52015-12-23 00:15:11 -0800552 for (Instruction ins : treatment.allInstructions()) {
553 if (ins.type() == Instruction.Type.L2MODIFICATION) {
554 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
555 switch (l2ins.subtype()) {
556 case ETH_DST:
Charles Chan5270ed02016-01-30 23:22:37 -0800557 dstMac = ((L2ModificationInstruction.ModEtherInstruction) l2ins).mac();
558 outerTtb.setEthDst(dstMac);
Charles Chan188ebf52015-12-23 00:15:11 -0800559 break;
560 case ETH_SRC:
Charles Chand0fd5dc2016-02-16 23:14:49 -0800561 srcMac = ((L2ModificationInstruction.ModEtherInstruction) l2ins).mac();
562 outerTtb.setEthSrc(srcMac);
Charles Chan188ebf52015-12-23 00:15:11 -0800563 break;
564 case VLAN_ID:
565 vlanid = ((L2ModificationInstruction.ModVlanIdInstruction) l2ins).vlanId();
Charles Chanf9e98652016-09-07 16:54:23 -0700566 if (useSetVlanExtension) {
567 OfdpaSetVlanVid ofdpaSetVlanVid = new OfdpaSetVlanVid(vlanid);
568 outerTtb.extension(ofdpaSetVlanVid, deviceId);
569 } else {
570 outerTtb.setVlanId(vlanid);
571 }
Charles Chan188ebf52015-12-23 00:15:11 -0800572 setVlan = true;
573 break;
574 case VLAN_POP:
575 innerTtb.popVlan();
576 popVlan = true;
577 break;
578 case DEC_MPLS_TTL:
579 case MPLS_LABEL:
580 case MPLS_POP:
581 case MPLS_PUSH:
582 case VLAN_PCP:
583 case VLAN_PUSH:
584 default:
585 break;
586 }
587 } else if (ins.type() == Instruction.Type.OUTPUT) {
588 portNum = ((Instructions.OutputInstruction) ins).port().toLong();
589 innerTtb.add(ins);
590 } else {
Saurav Das7bcbe702017-06-13 15:35:54 -0700591 log.debug("Driver does not handle this type of TrafficTreatment"
Saurav Dasa4020382018-02-14 14:14:54 -0800592 + " instruction in l2l3chain: {} - {}", ins.type(),
593 ins);
Charles Chan188ebf52015-12-23 00:15:11 -0800594 }
595 }
Charles Chan188ebf52015-12-23 00:15:11 -0800596 if (vlanid == null && meta != null) {
597 // use metadata if available
Pier Ventre140a8942016-11-02 07:26:38 -0700598 Criterion vidCriterion = meta.getCriterion(VLAN_VID);
Charles Chan188ebf52015-12-23 00:15:11 -0800599 if (vidCriterion != null) {
600 vlanid = ((VlanIdCriterion) vidCriterion).vlanId();
601 }
602 // if vlan is not set, use the vlan in metadata for outerTtb
603 if (vlanid != null && !setVlan) {
Charles Chanf9e98652016-09-07 16:54:23 -0700604 if (useSetVlanExtension) {
605 OfdpaSetVlanVid ofdpaSetVlanVid = new OfdpaSetVlanVid(vlanid);
606 outerTtb.extension(ofdpaSetVlanVid, deviceId);
607 } else {
608 outerTtb.setVlanId(vlanid);
609 }
Charles Chan188ebf52015-12-23 00:15:11 -0800610 }
611 }
Charles Chan188ebf52015-12-23 00:15:11 -0800612 if (vlanid == null) {
613 log.error("Driver cannot process an L2/L3 group chain without "
614 + "egress vlan information for dev: {} port:{}",
615 deviceId, portNum);
616 return null;
617 }
Charles Chan188ebf52015-12-23 00:15:11 -0800618 if (!setVlan && !popVlan) {
619 // untagged outgoing port
620 TrafficTreatment.Builder temp = DefaultTrafficTreatment.builder();
621 temp.popVlan();
Yi Tsengef19de12017-04-24 11:33:05 -0700622 innerTtb.build().allInstructions().forEach(temp::add);
Charles Chan188ebf52015-12-23 00:15:11 -0800623 innerTtb = temp;
624 }
Charles Chan188ebf52015-12-23 00:15:11 -0800625 // assemble information for ofdpa l2interface group
Yi Tsengef19de12017-04-24 11:33:05 -0700626 int l2groupId = l2GroupId(vlanid, portNum);
Saurav Das8be4e3a2016-03-11 17:19:07 -0800627 // a globally unique groupkey that is different for ports in the same device,
Charles Chan188ebf52015-12-23 00:15:11 -0800628 // but different for the same portnumber on different devices. Also different
629 // for the various group-types created out of the same next objective.
Charles Chane849c192016-01-11 18:28:54 -0800630 int l2gk = l2InterfaceGroupKey(deviceId, vlanid, portNum);
Yi Tsengef19de12017-04-24 11:33:05 -0700631 final GroupKey l2groupkey = new DefaultGroupKey(appKryo.serialize(l2gk));
Charles Chan188ebf52015-12-23 00:15:11 -0800632 // assemble information for outer group
Yi Tsengef19de12017-04-24 11:33:05 -0700633 GroupDescription outerGrpDesc;
Charles Chan188ebf52015-12-23 00:15:11 -0800634 if (mpls) {
Yi Tsengef19de12017-04-24 11:33:05 -0700635 // outer group is MPLS Interface
Saurav Das8be4e3a2016-03-11 17:19:07 -0800636 int mplsInterfaceIndex = getNextAvailableIndex();
Yi Tsengef19de12017-04-24 11:33:05 -0700637 int mplsGroupId = MPLS_INTERFACE_TYPE | (SUBTYPE_MASK & mplsInterfaceIndex);
638 final GroupKey mplsGroupKey = new DefaultGroupKey(
639 appKryo.serialize(mplsInterfaceIndex));
Yi Tsengfa394de2017-02-01 11:26:40 -0800640 outerTtb.group(new GroupId(l2groupId));
Charles Chan188ebf52015-12-23 00:15:11 -0800641 // create the mpls-interface group description to wait for the
642 // l2 interface group to be processed
643 GroupBucket mplsinterfaceGroupBucket =
644 DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build());
645 outerGrpDesc = new DefaultGroupDescription(
646 deviceId,
647 GroupDescription.Type.INDIRECT,
648 new GroupBuckets(Collections.singletonList(
649 mplsinterfaceGroupBucket)),
Yi Tsengef19de12017-04-24 11:33:05 -0700650 mplsGroupKey,
651 mplsGroupId,
Charles Chan188ebf52015-12-23 00:15:11 -0800652 appId);
653 log.debug("Trying MPLS-Interface: device:{} gid:{} gkey:{} nextid:{}",
Yi Tsengef19de12017-04-24 11:33:05 -0700654 deviceId, Integer.toHexString(mplsGroupId),
655 mplsGroupKey, nextId);
Charles Chan188ebf52015-12-23 00:15:11 -0800656 } else {
657 // outer group is L3Unicast
Saurav Das8be4e3a2016-03-11 17:19:07 -0800658 int l3unicastIndex = getNextAvailableIndex();
659 int l3groupId = L3_UNICAST_TYPE | (TYPE_MASK & l3unicastIndex);
660 final GroupKey l3groupkey = new DefaultGroupKey(
Yi Tsengef19de12017-04-24 11:33:05 -0700661 appKryo.serialize(l3unicastIndex));
Yi Tsengfa394de2017-02-01 11:26:40 -0800662 outerTtb.group(new GroupId(l2groupId));
Charles Chan188ebf52015-12-23 00:15:11 -0800663 // create the l3unicast group description to wait for the
664 // l2 interface group to be processed
665 GroupBucket l3unicastGroupBucket =
666 DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build());
667 outerGrpDesc = new DefaultGroupDescription(
668 deviceId,
669 GroupDescription.Type.INDIRECT,
Yi Tsengef19de12017-04-24 11:33:05 -0700670 new GroupBuckets(Collections.singletonList(l3unicastGroupBucket)),
Charles Chan188ebf52015-12-23 00:15:11 -0800671 l3groupkey,
672 l3groupId,
673 appId);
674 log.debug("Trying L3Unicast: device:{} gid:{} gkey:{} nextid:{}",
675 deviceId, Integer.toHexString(l3groupId),
676 l3groupkey, nextId);
677 }
Charles Chan188ebf52015-12-23 00:15:11 -0800678 // store l2groupkey with the groupChainElem for the outer-group that depends on it
Yi Tsengef19de12017-04-24 11:33:05 -0700679 GroupChainElem gce = new GroupChainElem(outerGrpDesc, 1, false, deviceId);
Charles Chan188ebf52015-12-23 00:15:11 -0800680 updatePendingGroups(l2groupkey, gce);
Yi Tsengef19de12017-04-24 11:33:05 -0700681 // create group description for the inner l2 interface group
Charles Chan5b9df8d2016-03-28 22:21:40 -0700682 GroupBucket l2InterfaceGroupBucket =
Charles Chan188ebf52015-12-23 00:15:11 -0800683 DefaultGroupBucket.createIndirectGroupBucket(innerTtb.build());
684 GroupDescription l2groupDescription =
Yi Tsengef19de12017-04-24 11:33:05 -0700685 new DefaultGroupDescription(deviceId,
686 GroupDescription.Type.INDIRECT,
687 new GroupBuckets(Collections.singletonList(l2InterfaceGroupBucket)),
688 l2groupkey,
689 l2groupId,
690 appId);
Charles Chan188ebf52015-12-23 00:15:11 -0800691 log.debug("Trying L2Interface: device:{} gid:{} gkey:{} nextId:{}",
692 deviceId, Integer.toHexString(l2groupId),
693 l2groupkey, nextId);
694 return new GroupInfo(l2groupDescription, outerGrpDesc);
Charles Chan188ebf52015-12-23 00:15:11 -0800695 }
696
697 /**
698 * As per the OFDPA 2.0 TTP, packets are sent out of ports by using
699 * a chain of groups. The broadcast Next Objective passed in by the application
700 * has to be broken up into a group chain comprising of an
Saurav Das1a129a02016-11-18 15:21:57 -0800701 * L2 Flood group or L3 Multicast group, whose buckets point to L2 Interface groups.
Charles Chan188ebf52015-12-23 00:15:11 -0800702 *
703 * @param nextObj the nextObjective of type BROADCAST
704 */
705 private void processBroadcastNextObjective(NextObjective nextObj) {
Yi Tsengef19de12017-04-24 11:33:05 -0700706 VlanId assignedVlan = readVlanFromSelector(nextObj.meta());
Charles Chan5b9df8d2016-03-28 22:21:40 -0700707 if (assignedVlan == null) {
708 log.warn("VLAN ID required by broadcast next obj is missing. Abort.");
Yi Tsengef19de12017-04-24 11:33:05 -0700709 fail(nextObj, ObjectiveError.BADPARAMS);
Charles Chan188ebf52015-12-23 00:15:11 -0800710 return;
711 }
Charles Chan188ebf52015-12-23 00:15:11 -0800712
jayakumarthazhath655b9a82018-10-01 00:51:54 +0530713 // Handling L2 multicast cases.
714 MacAddress dstMac = readEthDstFromSelector(nextObj.meta());
715 if (dstMac != null && dstMac.isMulticast()) {
716 processL2MulticastNextObjective(nextObj);
Jayakumar Thazhath812aa762018-10-24 01:22:04 -0400717 return;
jayakumarthazhath655b9a82018-10-01 00:51:54 +0530718 }
Charles Chan5b9df8d2016-03-28 22:21:40 -0700719
Charles Chan367c1c12018-10-19 16:23:28 -0700720 // FIXME Improve the logic
721 // If L2 load balancer is not involved, use L2IG. Otherwise, use L2UG.
722 // The purpose is to make sure existing XConnect logic can still work on a configured port.
723 List<GroupInfo> groupInfos;
724 if (nextObj.nextTreatments().stream().allMatch(n -> n.type() == NextTreatment.Type.TREATMENT)) {
725 groupInfos = prepareL2InterfaceGroup(nextObj, assignedVlan);
726 log.debug("prepareL2InterfaceGroup");
727 } else {
728 groupInfos = prepareL2UnfilteredGroup(nextObj);
729 log.debug("prepareL2UnfilteredGroup");
730 }
731
pier0023ca92018-11-29 10:32:40 -0800732 if (groupInfos == null || groupInfos.isEmpty()) {
733 log.warn("No buckets for Broadcast NextObj {}", nextObj);
734 fail(nextObj, ObjectiveError.GROUPMISSING);
735 return;
736 }
737
Yi Tsengef19de12017-04-24 11:33:05 -0700738 IpPrefix ipDst = readIpDstFromSelector(nextObj.meta());
Charles Chan5b9df8d2016-03-28 22:21:40 -0700739 if (ipDst != null) {
740 if (ipDst.isMulticast()) {
741 createL3MulticastGroup(nextObj, assignedVlan, groupInfos);
742 } else {
743 log.warn("Broadcast NextObj with non-multicast IP address {}", nextObj);
Yi Tsengef19de12017-04-24 11:33:05 -0700744 fail(nextObj, ObjectiveError.BADPARAMS);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700745 }
746 } else {
747 createL2FloodGroup(nextObj, assignedVlan, groupInfos);
748 }
749 }
750
Charles Chan367c1c12018-10-19 16:23:28 -0700751 private List<GroupInfo> prepareL2UnfilteredGroup(NextObjective nextObj) {
Charles Chan5b9df8d2016-03-28 22:21:40 -0700752 ImmutableList.Builder<GroupInfo> groupInfoBuilder = ImmutableList.builder();
Charles Chan5b9df8d2016-03-28 22:21:40 -0700753 // break up broadcast next objective to multiple groups
Charles Chan367c1c12018-10-19 16:23:28 -0700754 Collection<TrafficTreatment> treatments = nextObj.nextTreatments().stream()
755 .filter(nt -> nt.type() == NextTreatment.Type.TREATMENT)
756 .map(nt -> ((DefaultNextTreatment) nt).treatment())
757 .collect(Collectors.toSet());
758 Collection<Integer> nextIds = nextObj.nextTreatments().stream()
759 .filter(nt -> nt.type() == NextTreatment.Type.ID)
760 .map(nt -> ((IdNextTreatment) nt).nextId())
761 .collect(Collectors.toSet());
762
763 // Each treatment is converted to an L2 unfiltered group
764 treatments.forEach(treatment -> {
765 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
766 // Extract port information
767 PortNumber port = treatment.allInstructions().stream()
768 .map(instr -> (Instructions.OutputInstruction) instr)
769 .map(instr -> instr.port())
770 .findFirst().orElse(null);
771 if (port == null) {
772 log.debug("Skip bucket without output instruction");
773 return;
774 }
775 tBuilder.setOutput(port);
776 if (requireAllowVlanTransition()) {
777 tBuilder.extension(new OfdpaSetAllowVlanTranslation(Ofdpa3AllowVlanTranslationType.ALLOW), deviceId);
778 }
779
780 // Build L2UG
781 int l2ugk = l2UnfilteredGroupKey(deviceId, port.toLong());
782 final GroupKey l2UnfilterGroupKey = new DefaultGroupKey(appKryo.serialize(l2ugk));
783 int l2UnfilteredGroupId = L2_UNFILTERED_TYPE | ((int) port.toLong() & FOUR_NIBBLE_MASK);
784 GroupBucket l2UnfilteredGroupBucket = DefaultGroupBucket.createIndirectGroupBucket(tBuilder.build());
785 GroupDescription l2UnfilteredGroupDesc = new DefaultGroupDescription(deviceId,
786 GroupDescription.Type.INDIRECT,
787 new GroupBuckets(Collections.singletonList(l2UnfilteredGroupBucket)),
788 l2UnfilterGroupKey,
789 l2UnfilteredGroupId,
790 nextObj.appId());
791 log.debug("Trying L2-Unfiltered: device:{} gid:{} gkey:{} nextid:{}",
792 deviceId, Integer.toHexString(l2UnfilteredGroupId), l2UnfilterGroupKey, nextObj.id());
793 groupInfoBuilder.add(new GroupInfo(l2UnfilteredGroupDesc, l2UnfilteredGroupDesc));
794 });
pier0023ca92018-11-29 10:32:40 -0800795 // Save the current count
796 int counts = groupInfoBuilder.build().size();
Charles Chan367c1c12018-10-19 16:23:28 -0700797 // Lookup each nextId in the store and obtain the group information
798 nextIds.forEach(nextId -> {
pier0023ca92018-11-29 10:32:40 -0800799 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextId);
800 if (nextGroup != null) {
801 List<Deque<GroupKey>> allActiveKeys = appKryo.deserialize(nextGroup.data());
802 GroupKey topGroupKey = allActiveKeys.get(0).getFirst();
803 GroupDescription groupDesc = groupService.getGroup(deviceId, topGroupKey);
804 if (groupDesc != null) {
805 log.debug("Trying L2-Hash device:{} gid:{}, gkey:{}, nextid:{}",
806 deviceId, Integer.toHexString(((Group) groupDesc).id().id()), topGroupKey, nextId);
807 groupInfoBuilder.add(new GroupInfo(groupDesc, groupDesc));
808 } else {
809 log.error("Not found L2-Hash device:{}, gkey:{}, nextid:{}", deviceId, topGroupKey, nextId);
810 }
811 } else {
812 log.error("Not found NextGroup device:{}, nextid:{}", deviceId, nextId);
813 }
Charles Chan367c1c12018-10-19 16:23:28 -0700814 });
pier0023ca92018-11-29 10:32:40 -0800815 // Compare the size before and after to detect problems during the creation
816 ImmutableList<GroupInfo> groupInfos = groupInfoBuilder.build();
817 return (counts + nextIds.size()) == groupInfos.size() ? groupInfos : ImmutableList.of();
Charles Chan367c1c12018-10-19 16:23:28 -0700818 }
819
820 private List<GroupInfo> prepareL2InterfaceGroup(NextObjective nextObj, VlanId assignedVlan) {
821 ImmutableList.Builder<GroupInfo> groupInfoBuilder = ImmutableList.builder();
822 // break up broadcast next objective to multiple groups
823 Collection<TrafficTreatment> buckets = nextObj.nextTreatments().stream()
824 .filter(nt -> nt.type() == NextTreatment.Type.TREATMENT)
825 .map(nt -> ((DefaultNextTreatment) nt).treatment())
826 .collect(Collectors.toSet());
827
828 // Each treatment is converted to an L2 interface group
Charles Chan188ebf52015-12-23 00:15:11 -0800829 for (TrafficTreatment treatment : buckets) {
830 TrafficTreatment.Builder newTreatment = DefaultTrafficTreatment.builder();
831 PortNumber portNum = null;
Charles Chan5b9df8d2016-03-28 22:21:40 -0700832 VlanId egressVlan = null;
Charles Chan188ebf52015-12-23 00:15:11 -0800833 // ensure that the only allowed treatments are pop-vlan and output
834 for (Instruction ins : treatment.allInstructions()) {
835 if (ins.type() == Instruction.Type.L2MODIFICATION) {
836 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
837 switch (l2ins.subtype()) {
838 case VLAN_POP:
839 newTreatment.add(l2ins);
840 break;
Charles Chan5b9df8d2016-03-28 22:21:40 -0700841 case VLAN_ID:
842 egressVlan = ((L2ModificationInstruction.ModVlanIdInstruction) l2ins).vlanId();
843 break;
Charles Chan188ebf52015-12-23 00:15:11 -0800844 default:
845 log.debug("action {} not permitted for broadcast nextObj",
846 l2ins.subtype());
847 break;
848 }
849 } else if (ins.type() == Instruction.Type.OUTPUT) {
850 portNum = ((Instructions.OutputInstruction) ins).port();
851 newTreatment.add(ins);
852 } else {
Charles Chane849c192016-01-11 18:28:54 -0800853 log.debug("TrafficTreatment of type {} not permitted in " +
854 " broadcast nextObjective", ins.type());
Charles Chan188ebf52015-12-23 00:15:11 -0800855 }
856 }
Yi Tsengef19de12017-04-24 11:33:05 -0700857 if (portNum == null) {
Ruchi Sahotaef0761c2019-01-28 01:08:18 +0000858 log.debug("Can't find output port for the bucket {}.", treatment);
Yi Tsengef19de12017-04-24 11:33:05 -0700859 continue;
860 }
Charles Chan188ebf52015-12-23 00:15:11 -0800861 // assemble info for l2 interface group
Charles Chan5b9df8d2016-03-28 22:21:40 -0700862 VlanId l2InterfaceGroupVlan =
863 (egressVlan != null && !assignedVlan.equals(egressVlan)) ?
864 egressVlan : assignedVlan;
865 int l2gk = l2InterfaceGroupKey(deviceId, l2InterfaceGroupVlan, portNum.toLong());
866 final GroupKey l2InterfaceGroupKey =
Yi Tsengef19de12017-04-24 11:33:05 -0700867 new DefaultGroupKey(appKryo.serialize(l2gk));
Charles Chan372b63e2017-02-07 12:10:53 -0800868 int l2InterfaceGroupId = L2_INTERFACE_TYPE |
Charles Chan367c1c12018-10-19 16:23:28 -0700869 ((l2InterfaceGroupVlan.toShort() & THREE_NIBBLE_MASK) << PORT_LEN) |
870 ((int) portNum.toLong() & FOUR_NIBBLE_MASK);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700871 GroupBucket l2InterfaceGroupBucket =
Charles Chan188ebf52015-12-23 00:15:11 -0800872 DefaultGroupBucket.createIndirectGroupBucket(newTreatment.build());
Charles Chan5b9df8d2016-03-28 22:21:40 -0700873 GroupDescription l2InterfaceGroupDescription =
Yi Tsengef19de12017-04-24 11:33:05 -0700874 new DefaultGroupDescription(deviceId,
875 GroupDescription.Type.INDIRECT,
876 new GroupBuckets(Collections.singletonList(
877 l2InterfaceGroupBucket)),
878 l2InterfaceGroupKey,
879 l2InterfaceGroupId,
880 nextObj.appId());
Charles Chan188ebf52015-12-23 00:15:11 -0800881 log.debug("Trying L2-Interface: device:{} gid:{} gkey:{} nextid:{}",
Charles Chan5b9df8d2016-03-28 22:21:40 -0700882 deviceId, Integer.toHexString(l2InterfaceGroupId),
883 l2InterfaceGroupKey, nextObj.id());
Ruchi Sahotaef0761c2019-01-28 01:08:18 +0000884
Charles Chan5b9df8d2016-03-28 22:21:40 -0700885 groupInfoBuilder.add(new GroupInfo(l2InterfaceGroupDescription,
886 l2InterfaceGroupDescription));
Charles Chan188ebf52015-12-23 00:15:11 -0800887 }
Charles Chan5b9df8d2016-03-28 22:21:40 -0700888 return groupInfoBuilder.build();
889 }
Charles Chan188ebf52015-12-23 00:15:11 -0800890
Ruchi Sahotaef0761c2019-01-28 01:08:18 +0000891 private GroupInfo prepareL3UnicastGroup(NextObjective nextObj, NextGroup next) {
892
893 ImmutableList.Builder<GroupInfo> groupInfoBuilder = ImmutableList.builder();
894 TrafficTreatment treatment = nextObj.next().iterator().next();
895
896 VlanId assignedVlan = readVlanFromSelector(nextObj.meta());
897 if (assignedVlan == null) {
898 log.warn("VLAN ID required by next obj is missing. Abort.");
899 return null;
900 }
901
902 List<GroupInfo> l2GroupInfos = prepareL2InterfaceGroup(nextObj, assignedVlan);
903 GroupDescription l2InterfaceGroupDesc = l2GroupInfos.get(0).innerMostGroupDesc();
904 GroupKey l2groupkey = l2InterfaceGroupDesc.appCookie();
905
906 TrafficTreatment.Builder outerTtb = DefaultTrafficTreatment.builder();
907 VlanId vlanid = null;
908 MacAddress srcMac;
909 MacAddress dstMac;
910 for (Instruction ins : treatment.allInstructions()) {
911 if (ins.type() == Instruction.Type.L2MODIFICATION) {
912 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
913 switch (l2ins.subtype()) {
914 case ETH_DST:
915 dstMac = ((L2ModificationInstruction.ModEtherInstruction) l2ins).mac();
916 outerTtb.setEthDst(dstMac);
917 break;
918 case ETH_SRC:
919 srcMac = ((L2ModificationInstruction.ModEtherInstruction) l2ins).mac();
920 outerTtb.setEthSrc(srcMac);
921 break;
922 case VLAN_ID:
923 vlanid = ((L2ModificationInstruction.ModVlanIdInstruction) l2ins).vlanId();
924 outerTtb.setVlanId(vlanid);
925 break;
926 default:
927 break;
928 }
929 } else {
930 log.debug("Driver does not handle this type of TrafficTreatment"
931 + " instruction in l2l3chain: {} - {}", ins.type(), ins);
932 }
933 }
934
935 GroupId l2groupId = new GroupId(l2InterfaceGroupDesc.givenGroupId());
936 outerTtb.group(l2groupId);
937
938 // we need the top level group's key to point the flow to it
939 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
940 GroupKey l3groupkey = gkeys.get(0).peekFirst();
941 GroupId grpId = groupService.getGroup(deviceId, l3groupkey).id();
942 int l3groupId = grpId.id();
943
944 // create the l3unicast group description to wait for the
945 // l2 interface group to be processed
946 GroupBucket l3UnicastGroupBucket = DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build());
947
948 GroupDescription l3UnicastGroupDescription = new DefaultGroupDescription(deviceId,
949 GroupDescription.Type.INDIRECT,
950 new GroupBuckets(Collections.singletonList(
951 l3UnicastGroupBucket)), l3groupkey,
952 l3groupId, nextObj.appId());
953
954 // store l2groupkey with the groupChainElem for the outer-group that depends on it
955 GroupChainElem gce = new GroupChainElem(l3UnicastGroupDescription, 1, false, deviceId);
956 updatePendingGroups(l2groupkey, gce);
957
958 log.debug("Trying L3-Interface: device:{} gid:{} gkey:{} nextid:{}",
959 deviceId, Integer.toHexString(l3groupId), l3groupkey, nextObj.id());
960
961 groupInfoBuilder.add(new GroupInfo(l2InterfaceGroupDesc,
962 l3UnicastGroupDescription));
963
964 return groupInfoBuilder.build().iterator().next();
965 }
966
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700967 private void createL2FloodGroup(NextObjective nextObj, VlanId vlanId,
968 List<GroupInfo> groupInfos) {
969 // assemble info for l2 flood group. Since there can be only one flood
970 // group for a vlan, its index is always the same - 0
Yi Tsengef19de12017-04-24 11:33:05 -0700971 Integer l2FloodGroupId = L2_FLOOD_TYPE | (vlanId.toShort() << 16);
972 final GroupKey l2FloodGroupKey = l2FloodGroupKey(vlanId, deviceId);
Charles Chan188ebf52015-12-23 00:15:11 -0800973 // collection of group buckets pointing to all the l2 interface groups
Yi Tsengef19de12017-04-24 11:33:05 -0700974 List<GroupBucket> l2floodBuckets = generateNextGroupBuckets(groupInfos, ALL);
Charles Chan188ebf52015-12-23 00:15:11 -0800975 // create the l2flood group-description to wait for all the
976 // l2interface groups to be processed
977 GroupDescription l2floodGroupDescription =
978 new DefaultGroupDescription(
979 deviceId,
Yi Tseng78f51f42017-02-02 13:54:58 -0800980 ALL,
Charles Chan188ebf52015-12-23 00:15:11 -0800981 new GroupBuckets(l2floodBuckets),
Yi Tsengef19de12017-04-24 11:33:05 -0700982 l2FloodGroupKey,
983 l2FloodGroupId,
Charles Chan188ebf52015-12-23 00:15:11 -0800984 nextObj.appId());
Charles Chan188ebf52015-12-23 00:15:11 -0800985 log.debug("Trying L2-Flood: device:{} gid:{} gkey:{} nextid:{}",
Yi Tsengef19de12017-04-24 11:33:05 -0700986 deviceId, Integer.toHexString(l2FloodGroupId),
987 l2FloodGroupKey, nextObj.id());
Charles Chan5b9df8d2016-03-28 22:21:40 -0700988 // Put all dependency information into allGroupKeys
989 List<Deque<GroupKey>> allGroupKeys = Lists.newArrayList();
990 groupInfos.forEach(groupInfo -> {
Yi Tsengef19de12017-04-24 11:33:05 -0700991 Deque<GroupKey> groupKeyChain = new ArrayDeque<>();
Charles Chan5b9df8d2016-03-28 22:21:40 -0700992 // In this case we should have L2 interface group only
Yi Tsengef19de12017-04-24 11:33:05 -0700993 groupKeyChain.addFirst(groupInfo.nextGroupDesc().appCookie());
994 groupKeyChain.addFirst(l2FloodGroupKey);
995 allGroupKeys.add(groupKeyChain);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700996 });
Charles Chan5b9df8d2016-03-28 22:21:40 -0700997 // Point the next objective to this group
998 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
Yi Tsengef19de12017-04-24 11:33:05 -0700999 updatePendingNextObjective(l2FloodGroupKey, ofdpaGrp);
Charles Chan5b9df8d2016-03-28 22:21:40 -07001000 GroupChainElem gce = new GroupChainElem(l2floodGroupDescription,
Yi Tsengef19de12017-04-24 11:33:05 -07001001 groupInfos.size(), false, deviceId);
Charles Chan5b9df8d2016-03-28 22:21:40 -07001002 groupInfos.forEach(groupInfo -> {
1003 // Point this group to the next group
Yi Tsengef19de12017-04-24 11:33:05 -07001004 updatePendingGroups(groupInfo.nextGroupDesc().appCookie(), gce);
Charles Chan5b9df8d2016-03-28 22:21:40 -07001005 // Start installing the inner-most group
Yi Tsengef19de12017-04-24 11:33:05 -07001006 groupService.addGroup(groupInfo.innerMostGroupDesc());
Charles Chan5b9df8d2016-03-28 22:21:40 -07001007 });
1008 }
1009
Pier Luigi21fffd22018-01-19 10:24:53 +01001010 private void createL3MulticastGroup(NextObjective nextObj, VlanId vlanId,
1011 List<GroupInfo> groupInfos) {
1012 // Let's create a new list mcast buckets
1013 List<GroupBucket> l3McastBuckets = createL3MulticastBucket(groupInfos);
Charles Chan5b9df8d2016-03-28 22:21:40 -07001014
1015 int l3MulticastIndex = getNextAvailableIndex();
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001016 int l3MulticastGroupId = L3_MULTICAST_TYPE |
1017 vlanId.toShort() << 16 | (TYPE_VLAN_MASK & l3MulticastIndex);
1018 final GroupKey l3MulticastGroupKey =
Yi Tsengef19de12017-04-24 11:33:05 -07001019 new DefaultGroupKey(appKryo.serialize(l3MulticastIndex));
Charles Chan5b9df8d2016-03-28 22:21:40 -07001020
1021 GroupDescription l3MulticastGroupDesc = new DefaultGroupDescription(deviceId,
Yi Tseng78f51f42017-02-02 13:54:58 -08001022 ALL,
Charles Chan5b9df8d2016-03-28 22:21:40 -07001023 new GroupBuckets(l3McastBuckets),
1024 l3MulticastGroupKey,
1025 l3MulticastGroupId,
1026 nextObj.appId());
1027
1028 // Put all dependency information into allGroupKeys
1029 List<Deque<GroupKey>> allGroupKeys = Lists.newArrayList();
1030 groupInfos.forEach(groupInfo -> {
1031 Deque<GroupKey> gkeyChain = new ArrayDeque<>();
Yi Tsengef19de12017-04-24 11:33:05 -07001032 gkeyChain.addFirst(groupInfo.innerMostGroupDesc().appCookie());
Charles Chan5b9df8d2016-03-28 22:21:40 -07001033 // Add L3 interface group to the chain if there is one.
Yi Tsengef19de12017-04-24 11:33:05 -07001034 if (!groupInfo.nextGroupDesc().equals(groupInfo.innerMostGroupDesc())) {
1035 gkeyChain.addFirst(groupInfo.nextGroupDesc().appCookie());
Charles Chan5b9df8d2016-03-28 22:21:40 -07001036 }
1037 gkeyChain.addFirst(l3MulticastGroupKey);
1038 allGroupKeys.add(gkeyChain);
1039 });
Charles Chan5b9df8d2016-03-28 22:21:40 -07001040 // Point the next objective to this group
1041 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
1042 updatePendingNextObjective(l3MulticastGroupKey, ofdpaGrp);
Charles Chan5b9df8d2016-03-28 22:21:40 -07001043 GroupChainElem outerGce = new GroupChainElem(l3MulticastGroupDesc,
Yi Tsengef19de12017-04-24 11:33:05 -07001044 groupInfos.size(), false, deviceId);
Charles Chan5b9df8d2016-03-28 22:21:40 -07001045 groupInfos.forEach(groupInfo -> {
1046 // Point this group (L3 multicast) to the next group
Yi Tsengef19de12017-04-24 11:33:05 -07001047 updatePendingGroups(groupInfo.nextGroupDesc().appCookie(), outerGce);
Charles Chan5b9df8d2016-03-28 22:21:40 -07001048 // Point next group to inner-most group, if any
Yi Tsengef19de12017-04-24 11:33:05 -07001049 if (!groupInfo.nextGroupDesc().equals(groupInfo.innerMostGroupDesc())) {
1050 GroupChainElem innerGce = new GroupChainElem(groupInfo.nextGroupDesc(),
1051 1, false, deviceId);
1052 updatePendingGroups(groupInfo.innerMostGroupDesc().appCookie(), innerGce);
Charles Chan5b9df8d2016-03-28 22:21:40 -07001053 }
Charles Chan5b9df8d2016-03-28 22:21:40 -07001054 // Start installing the inner-most group
Yi Tsengef19de12017-04-24 11:33:05 -07001055 groupService.addGroup(groupInfo.innerMostGroupDesc());
Charles Chan5b9df8d2016-03-28 22:21:40 -07001056 });
Charles Chan188ebf52015-12-23 00:15:11 -08001057 }
1058
Charles Chan188ebf52015-12-23 00:15:11 -08001059 /**
1060 * As per the OFDPA 2.0 TTP, packets are sent out of ports by using
1061 * a chain of groups. The hashed Next Objective passed in by the application
1062 * has to be broken up into a group chain comprising of an
1063 * L3 ECMP group as the top level group. Buckets of this group can point
1064 * to a variety of groups in a group chain, depending on the whether
1065 * MPLS labels are being pushed or not.
1066 * <p>
1067 * NOTE: We do not create MPLS ECMP groups as they are unimplemented in
1068 * OF-DPA 2.0 (even though it is in the spec). Therefore we do not
1069 * check the nextObjective meta to see what is matching before being
1070 * sent to this nextObjective.
1071 *
1072 * @param nextObj the nextObjective of type HASHED
1073 */
Charles Chan367c1c12018-10-19 16:23:28 -07001074 protected void processEcmpHashedNextObjective(NextObjective nextObj) {
Charles Chan188ebf52015-12-23 00:15:11 -08001075 // storage for all group keys in the chain of groups created
1076 List<Deque<GroupKey>> allGroupKeys = new ArrayList<>();
1077 List<GroupInfo> unsentGroups = new ArrayList<>();
Charles Chan367c1c12018-10-19 16:23:28 -07001078 createEcmpHashBucketChains(nextObj, allGroupKeys, unsentGroups);
Charles Chan188ebf52015-12-23 00:15:11 -08001079 // now we can create the outermost L3 ECMP group
1080 List<GroupBucket> l3ecmpGroupBuckets = new ArrayList<>();
1081 for (GroupInfo gi : unsentGroups) {
1082 // create ECMP bucket to point to the outer group
1083 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
Yi Tsengef19de12017-04-24 11:33:05 -07001084 ttb.group(new GroupId(gi.nextGroupDesc().givenGroupId()));
Charles Chan188ebf52015-12-23 00:15:11 -08001085 GroupBucket sbucket = DefaultGroupBucket
1086 .createSelectGroupBucket(ttb.build());
1087 l3ecmpGroupBuckets.add(sbucket);
1088 }
Saurav Das8be4e3a2016-03-11 17:19:07 -08001089 int l3ecmpIndex = getNextAvailableIndex();
1090 int l3ecmpGroupId = L3_ECMP_TYPE | (TYPE_MASK & l3ecmpIndex);
1091 GroupKey l3ecmpGroupKey = new DefaultGroupKey(
Yi Tsengef19de12017-04-24 11:33:05 -07001092 appKryo.serialize(l3ecmpIndex));
Charles Chan188ebf52015-12-23 00:15:11 -08001093 GroupDescription l3ecmpGroupDesc =
1094 new DefaultGroupDescription(
1095 deviceId,
Yi Tseng78f51f42017-02-02 13:54:58 -08001096 SELECT,
Charles Chan188ebf52015-12-23 00:15:11 -08001097 new GroupBuckets(l3ecmpGroupBuckets),
1098 l3ecmpGroupKey,
1099 l3ecmpGroupId,
1100 nextObj.appId());
1101 GroupChainElem l3ecmpGce = new GroupChainElem(l3ecmpGroupDesc,
1102 l3ecmpGroupBuckets.size(),
Yi Tsengef19de12017-04-24 11:33:05 -07001103 false, deviceId);
Charles Chan188ebf52015-12-23 00:15:11 -08001104
1105 // create objects for local and distributed storage
Yi Tsengef19de12017-04-24 11:33:05 -07001106 allGroupKeys.forEach(gKeyChain -> gKeyChain.addFirst(l3ecmpGroupKey));
Charles Chan188ebf52015-12-23 00:15:11 -08001107 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
Charles Chan188ebf52015-12-23 00:15:11 -08001108 // store l3ecmpGroupKey with the ofdpaGroupChain for the nextObjective
1109 // that depends on it
1110 updatePendingNextObjective(l3ecmpGroupKey, ofdpaGrp);
Charles Chan188ebf52015-12-23 00:15:11 -08001111 log.debug("Trying L3ECMP: device:{} gid:{} gkey:{} nextId:{}",
1112 deviceId, Integer.toHexString(l3ecmpGroupId),
1113 l3ecmpGroupKey, nextObj.id());
1114 // finally we are ready to send the innermost groups
1115 for (GroupInfo gi : unsentGroups) {
1116 log.debug("Sending innermost group {} in group chain on device {} ",
Yi Tsengef19de12017-04-24 11:33:05 -07001117 Integer.toHexString(gi.innerMostGroupDesc().givenGroupId()), deviceId);
1118 updatePendingGroups(gi.nextGroupDesc().appCookie(), l3ecmpGce);
1119 groupService.addGroup(gi.innerMostGroupDesc());
Charles Chan188ebf52015-12-23 00:15:11 -08001120 }
Charles Chan188ebf52015-12-23 00:15:11 -08001121 }
1122
1123 /**
1124 * Creates group chains for all buckets in a hashed group, and stores the
1125 * GroupInfos and GroupKeys for all the groups in the lists passed in, which
1126 * should be empty.
1127 * <p>
1128 * Does not create the top level ECMP group. Does not actually send the
1129 * groups to the groupService.
1130 *
1131 * @param nextObj the Next Objective with buckets that need to be converted
1132 * to group chains
1133 * @param allGroupKeys a list to store groupKey for each bucket-group-chain
1134 * @param unsentGroups a list to store GroupInfo for each bucket-group-chain
1135 */
Charles Chan367c1c12018-10-19 16:23:28 -07001136 protected void createEcmpHashBucketChains(NextObjective nextObj,
1137 List<Deque<GroupKey>> allGroupKeys,
1138 List<GroupInfo> unsentGroups) {
Charles Chan188ebf52015-12-23 00:15:11 -08001139 // break up hashed next objective to multiple groups
1140 Collection<TrafficTreatment> buckets = nextObj.next();
1141
1142 for (TrafficTreatment bucket : buckets) {
1143 //figure out how many labels are pushed in each bucket
1144 int labelsPushed = 0;
1145 MplsLabel innermostLabel = null;
1146 for (Instruction ins : bucket.allInstructions()) {
1147 if (ins.type() == Instruction.Type.L2MODIFICATION) {
1148 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
1149 if (l2ins.subtype() == L2ModificationInstruction.L2SubType.MPLS_PUSH) {
1150 labelsPushed++;
1151 }
1152 if (l2ins.subtype() == L2ModificationInstruction.L2SubType.MPLS_LABEL) {
1153 if (innermostLabel == null) {
Yi Tsengef19de12017-04-24 11:33:05 -07001154 innermostLabel =
1155 ((L2ModificationInstruction.ModMplsLabelInstruction) l2ins).label();
Charles Chan188ebf52015-12-23 00:15:11 -08001156 }
1157 }
1158 }
1159 }
Yi Tsengef19de12017-04-24 11:33:05 -07001160 Deque<GroupKey> gKeyChain = new ArrayDeque<>();
Saurav Dasa4020382018-02-14 14:14:54 -08001161 // here we only deal with 0 and 1 label push
Charles Chan188ebf52015-12-23 00:15:11 -08001162 if (labelsPushed == 0) {
Yi Tsengef19de12017-04-24 11:33:05 -07001163 GroupInfo noLabelGroupInfo;
Pier Ventre140a8942016-11-02 07:26:38 -07001164 TrafficSelector metaSelector = nextObj.meta();
1165 if (metaSelector != null) {
1166 if (isNotMplsBos(metaSelector)) {
Yi Tsengef19de12017-04-24 11:33:05 -07001167 noLabelGroupInfo = createL2L3Chain(bucket, nextObj.id(),
1168 nextObj.appId(), true,
1169 nextObj.meta());
Pier Ventre140a8942016-11-02 07:26:38 -07001170 } else {
Yi Tsengef19de12017-04-24 11:33:05 -07001171 noLabelGroupInfo = createL2L3Chain(bucket, nextObj.id(),
1172 nextObj.appId(), false,
1173 nextObj.meta());
Pier Ventre140a8942016-11-02 07:26:38 -07001174 }
1175 } else {
Yi Tsengef19de12017-04-24 11:33:05 -07001176 noLabelGroupInfo = createL2L3Chain(bucket, nextObj.id(),
1177 nextObj.appId(), false,
1178 nextObj.meta());
Pier Ventre140a8942016-11-02 07:26:38 -07001179 }
Yi Tsengef19de12017-04-24 11:33:05 -07001180 if (noLabelGroupInfo == null) {
Charles Chan188ebf52015-12-23 00:15:11 -08001181 log.error("Could not process nextObj={} in dev:{}",
1182 nextObj.id(), deviceId);
1183 return;
1184 }
Yi Tsengef19de12017-04-24 11:33:05 -07001185 gKeyChain.addFirst(noLabelGroupInfo.innerMostGroupDesc().appCookie());
1186 gKeyChain.addFirst(noLabelGroupInfo.nextGroupDesc().appCookie());
Charles Chan188ebf52015-12-23 00:15:11 -08001187 // we can't send the inner group description yet, as we have to
1188 // create the dependent ECMP group first. So we store..
Yi Tsengef19de12017-04-24 11:33:05 -07001189 unsentGroups.add(noLabelGroupInfo);
Charles Chan188ebf52015-12-23 00:15:11 -08001190 } else if (labelsPushed == 1) {
1191 GroupInfo onelabelGroupInfo = createL2L3Chain(bucket, nextObj.id(),
1192 nextObj.appId(), true,
1193 nextObj.meta());
1194 if (onelabelGroupInfo == null) {
1195 log.error("Could not process nextObj={} in dev:{}",
1196 nextObj.id(), deviceId);
1197 return;
1198 }
1199 // we need to add another group to this chain - the L3VPN group
1200 TrafficTreatment.Builder l3vpnTtb = DefaultTrafficTreatment.builder();
Charles Chan0f43e472017-02-14 14:00:16 -08001201 if (requireVlanPopBeforeMplsPush()) {
1202 l3vpnTtb.popVlan();
1203 }
Charles Chan188ebf52015-12-23 00:15:11 -08001204 l3vpnTtb.pushMpls()
1205 .setMpls(innermostLabel)
Yi Tsengef19de12017-04-24 11:33:05 -07001206 .group(new GroupId(onelabelGroupInfo.nextGroupDesc().givenGroupId()));
Charles Chan40132b32017-01-22 00:19:37 -08001207 if (supportCopyTtl()) {
1208 l3vpnTtb.copyTtlOut();
1209 }
1210 if (supportSetMplsBos()) {
1211 l3vpnTtb.setMplsBos(true);
1212 }
Charles Chan0f43e472017-02-14 14:00:16 -08001213 if (requireVlanPopBeforeMplsPush()) {
1214 l3vpnTtb.pushVlan().setVlanId(VlanId.vlanId(VlanId.RESERVED));
1215 }
Charles Chan188ebf52015-12-23 00:15:11 -08001216 GroupBucket l3vpnGrpBkt =
1217 DefaultGroupBucket.createIndirectGroupBucket(l3vpnTtb.build());
Saurav Das8be4e3a2016-03-11 17:19:07 -08001218 int l3vpnIndex = getNextAvailableIndex();
Yi Tsengef19de12017-04-24 11:33:05 -07001219 int l3vpnGroupId = MPLS_L3VPN_SUBTYPE | (SUBTYPE_MASK & l3vpnIndex);
1220 GroupKey l3vpnGroupKey = new DefaultGroupKey(
1221 appKryo.serialize(l3vpnIndex));
Charles Chan188ebf52015-12-23 00:15:11 -08001222 GroupDescription l3vpnGroupDesc =
1223 new DefaultGroupDescription(
1224 deviceId,
1225 GroupDescription.Type.INDIRECT,
Yi Tsengef19de12017-04-24 11:33:05 -07001226 new GroupBuckets(Collections.singletonList(l3vpnGrpBkt)),
1227 l3vpnGroupKey,
1228 l3vpnGroupId,
Charles Chan188ebf52015-12-23 00:15:11 -08001229 nextObj.appId());
Yi Tsengef19de12017-04-24 11:33:05 -07001230 GroupChainElem l3vpnGce = new GroupChainElem(l3vpnGroupDesc,
1231 1,
1232 false,
1233 deviceId);
1234 updatePendingGroups(onelabelGroupInfo.nextGroupDesc().appCookie(), l3vpnGce);
Charles Chan188ebf52015-12-23 00:15:11 -08001235
Yi Tsengef19de12017-04-24 11:33:05 -07001236 gKeyChain.addFirst(onelabelGroupInfo.innerMostGroupDesc().appCookie());
1237 gKeyChain.addFirst(onelabelGroupInfo.nextGroupDesc().appCookie());
1238 gKeyChain.addFirst(l3vpnGroupKey);
Charles Chan188ebf52015-12-23 00:15:11 -08001239 //now we can replace the outerGrpDesc with the one we just created
Yi Tsengef19de12017-04-24 11:33:05 -07001240 onelabelGroupInfo.nextGroupDesc(l3vpnGroupDesc);
Charles Chan188ebf52015-12-23 00:15:11 -08001241 // we can't send the innermost group yet, as we have to create
1242 // the dependent ECMP group first. So we store ...
1243 unsentGroups.add(onelabelGroupInfo);
Yi Tsengef19de12017-04-24 11:33:05 -07001244 log.debug("Trying L3VPN: device:{} gid:{} group key:{} nextId:{}",
1245 deviceId, Integer.toHexString(l3vpnGroupId),
1246 l3vpnGroupKey, nextObj.id());
Charles Chan188ebf52015-12-23 00:15:11 -08001247 } else {
1248 log.warn("Driver currently does not handle more than 1 MPLS "
1249 + "labels. Not processing nextObjective {}", nextObj.id());
1250 return;
1251 }
Charles Chan188ebf52015-12-23 00:15:11 -08001252 // all groups in this chain
Yi Tsengef19de12017-04-24 11:33:05 -07001253 allGroupKeys.add(gKeyChain);
Charles Chan188ebf52015-12-23 00:15:11 -08001254 }
1255 }
1256
Pier Ventre42287df2016-11-09 14:17:26 -08001257 /**
Charles Chan367c1c12018-10-19 16:23:28 -07001258 * Create L2 hash group.
1259 *
1260 * @param nextObj next objective
1261 */
1262 private void processL2HashedNextObjective(NextObjective nextObj) {
1263 int l2LbIndex = Optional.ofNullable(nextObj.meta().getCriterion(IN_PORT))
1264 .map(c -> (PortCriterion) c).map(PortCriterion::port).map(PortNumber::toLong).map(Long::intValue)
1265 .orElse(-1);
1266 if (l2LbIndex == -1) {
1267 log.warn("l2LbIndex is not found in the meta of L2 hash objective. Abort");
1268 return;
1269 }
1270
1271 // Storage for all group keys in the chain of groups created
1272 List<Deque<GroupKey>> allGroupKeys = new ArrayList<>();
1273 List<GroupInfo> unsentGroups = new ArrayList<>();
1274 createL2HashBuckets(nextObj, allGroupKeys, unsentGroups);
1275
1276 // Create L2 load balancing group
1277 List<GroupBucket> l2LbGroupBuckets = new ArrayList<>();
1278 for (GroupInfo gi : unsentGroups) {
1279 // create load balancing bucket to point to the outer group
1280 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
1281 ttb.group(new GroupId(gi.nextGroupDesc().givenGroupId()));
1282 GroupBucket sbucket = DefaultGroupBucket.createSelectGroupBucket(ttb.build());
1283 l2LbGroupBuckets.add(sbucket);
1284 }
1285
1286 int l2LbGroupId = L2_LB_TYPE | (TYPE_MASK & l2LbIndex);
1287 int l2lbgk = l2HashGroupKey(deviceId, l2LbIndex);
1288 GroupKey l2LbGroupKey = new DefaultGroupKey(appKryo.serialize(l2lbgk));
1289 GroupDescription l2LbGroupDesc =
1290 new DefaultGroupDescription(
1291 deviceId,
1292 SELECT,
1293 new GroupBuckets(l2LbGroupBuckets),
1294 l2LbGroupKey,
1295 l2LbGroupId,
1296 nextObj.appId());
1297 GroupChainElem l2LbGce = new GroupChainElem(l2LbGroupDesc, l2LbGroupBuckets.size(), false, deviceId);
1298
1299 // Create objects for local and distributed storage
1300 allGroupKeys.forEach(gKeyChain -> gKeyChain.addFirst(l2LbGroupKey));
1301 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
1302 // Store l2LbGroupKey with the ofdpaGroupChain for the nextObjective that depends on it
1303 updatePendingNextObjective(l2LbGroupKey, ofdpaGrp);
1304 log.debug("Trying L2-LB: device:{} gid:{} gkey:{} nextId:{}",
1305 deviceId, Integer.toHexString(l2LbGroupId), l2LbGroupKey, nextObj.id());
1306 // finally we are ready to send the innermost groups
1307 for (GroupInfo gi : unsentGroups) {
1308 log.debug("Sending innermost group {} in group chain on device {} ",
1309 Integer.toHexString(gi.innerMostGroupDesc().givenGroupId()), deviceId);
1310 updatePendingGroups(gi.nextGroupDesc().appCookie(), l2LbGce);
1311 groupService.addGroup(gi.innerMostGroupDesc());
1312 }
1313 }
1314
1315 /**
1316 * Create L2 hash group buckets.
1317 *
1318 * @param nextObj next objective
1319 */
1320 private void createL2HashBuckets(NextObjective nextObj,
1321 List<Deque<GroupKey>> allGroupKeys, List<GroupInfo> unsentGroups) {
1322 List<GroupInfo> groupInfos = prepareL2UnfilteredGroup(nextObj);
1323 groupInfos.forEach(groupInfo -> {
1324 // Update allGroupKeys
1325 Deque<GroupKey> gKeyChain = new ArrayDeque<>();
1326 gKeyChain.addFirst(groupInfo.innerMostGroupDesc().appCookie());
1327 allGroupKeys.add(gKeyChain);
1328
1329 // Update unsent group list
1330 unsentGroups.add(groupInfo);
1331 });
1332 }
1333
1334 /**
Pier Ventre42287df2016-11-09 14:17:26 -08001335 * Processes the pseudo wire related next objective.
1336 * This procedure try to reuse the mpls label groups,
1337 * the mpls interface group and the l2 interface group.
1338 *
1339 * @param nextObjective the objective to process.
1340 */
1341 protected void processPwNextObjective(NextObjective nextObjective) {
Saurav Das1547b3f2017-05-05 17:01:08 -07001342 log.warn("Pseudo wire extensions are not supported in OFDPA 2.0 {}",
1343 nextObjective.id());
Pier Ventre42287df2016-11-09 14:17:26 -08001344 }
1345
Saurav Das8be4e3a2016-03-11 17:19:07 -08001346 //////////////////////////////////////
1347 // Group Editing
1348 //////////////////////////////////////
Charles Chan188ebf52015-12-23 00:15:11 -08001349 /**
1350 * Adds a bucket to the top level group of a group-chain, and creates the chain.
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001351 * Ensures that bucket being added is not a duplicate, by checking existing
Yi Tsengef19de12017-04-24 11:33:05 -07001352 * buckets for the same output port.
Charles Chan188ebf52015-12-23 00:15:11 -08001353 *
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001354 * @param nextObjective the bucket information for a next group
Charles Chan188ebf52015-12-23 00:15:11 -08001355 * @param next the representation of the existing group-chain for this next objective
1356 */
1357 protected void addBucketToGroup(NextObjective nextObjective, NextGroup next) {
Saurav Das1a129a02016-11-18 15:21:57 -08001358 if (nextObjective.type() != NextObjective.Type.HASHED &&
1359 nextObjective.type() != NextObjective.Type.BROADCAST) {
Charles Chan188ebf52015-12-23 00:15:11 -08001360 log.warn("AddBuckets not applied to nextType:{} in dev:{} for next:{}",
Yi Tseng78f51f42017-02-02 13:54:58 -08001361 nextObjective.type(), deviceId, nextObjective.id());
Yi Tsengef19de12017-04-24 11:33:05 -07001362 fail(nextObjective, ObjectiveError.UNSUPPORTED);
Charles Chan188ebf52015-12-23 00:15:11 -08001363 return;
1364 }
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001365 // first check to see if bucket being added is not a duplicate of an
Yi Tsengef19de12017-04-24 11:33:05 -07001366 // existing bucket. If it is for an existing output port, then its a
1367 // duplicate.
Yi Tseng78f51f42017-02-02 13:54:58 -08001368 Set<TrafficTreatment> duplicateBuckets = Sets.newHashSet();
Yi Tsengef19de12017-04-24 11:33:05 -07001369 List<Deque<GroupKey>> allActiveKeys = appKryo.deserialize(next.data());
1370 Set<PortNumber> existingPorts = getExistingOutputPorts(allActiveKeys,
1371 groupService,
1372 deviceId);
Yi Tseng47f82dc2017-03-05 22:48:39 -08001373 Set<TrafficTreatment> nonDuplicateBuckets = Sets.newHashSet();
1374 NextObjective objectiveToAdd;
Yi Tseng78f51f42017-02-02 13:54:58 -08001375 nextObjective.next().forEach(trafficTreatment -> {
1376 PortNumber portNumber = readOutPortFromTreatment(trafficTreatment);
Yi Tseng78f51f42017-02-02 13:54:58 -08001377 if (portNumber == null) {
1378 return;
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001379 }
Yi Tseng78f51f42017-02-02 13:54:58 -08001380 if (existingPorts.contains(portNumber)) {
Saurav Das7bcbe702017-06-13 15:35:54 -07001381 // its possible that portnumbers are same but labels are different
1382 int label = readLabelFromTreatment(trafficTreatment);
1383 if (label == -1) {
1384 duplicateBuckets.add(trafficTreatment);
1385 } else {
Saurav Dasceccf242017-08-03 18:30:35 -07001386 List<Integer> existing = existingPortAndLabel(allActiveKeys,
1387 groupService, deviceId,
1388 portNumber, label);
1389 if (!existing.isEmpty()) {
Saurav Das7bcbe702017-06-13 15:35:54 -07001390 duplicateBuckets.add(trafficTreatment);
1391 } else {
1392 nonDuplicateBuckets.add(trafficTreatment);
1393 }
1394 }
Yi Tseng47f82dc2017-03-05 22:48:39 -08001395 } else {
1396 nonDuplicateBuckets.add(trafficTreatment);
Yi Tseng78f51f42017-02-02 13:54:58 -08001397 }
1398 });
Saurav Dasb28d5dd2017-03-24 19:03:58 -07001399 if (duplicateBuckets.isEmpty()) {
1400 // use the original objective
1401 objectiveToAdd = nextObjective;
1402 } else if (!nonDuplicateBuckets.isEmpty()) {
1403 // only use the non-duplicate buckets if there are any
1404 log.debug("Some buckets {} already exist in next id {}, duplicate "
1405 + "buckets will be ignored.", duplicateBuckets, nextObjective.id());
1406 // new next objective with non duplicate treatments
Yi Tseng47f82dc2017-03-05 22:48:39 -08001407 NextObjective.Builder builder = DefaultNextObjective.builder()
1408 .withType(nextObjective.type())
1409 .withId(nextObjective.id())
1410 .withMeta(nextObjective.meta())
1411 .fromApp(nextObjective.appId());
1412 nonDuplicateBuckets.forEach(builder::addTreatment);
Yi Tseng47f82dc2017-03-05 22:48:39 -08001413 ObjectiveContext context = nextObjective.context().orElse(null);
1414 objectiveToAdd = builder.addToExisting(context);
1415 } else {
Saurav Dasb28d5dd2017-03-24 19:03:58 -07001416 // buckets to add are already there - nothing to do
Saurav Das1547b3f2017-05-05 17:01:08 -07001417 log.debug("buckets already exist {} in next: {} ..ignoring bucket add",
1418 duplicateBuckets, nextObjective.id());
1419 pass(nextObjective);
Saurav Dasb28d5dd2017-03-24 19:03:58 -07001420 return;
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001421 }
Saurav Das1a129a02016-11-18 15:21:57 -08001422 if (nextObjective.type() == NextObjective.Type.HASHED) {
piercd2a3ef2018-11-22 15:44:57 +01001423 if (isL2Hash(nextObjective)) {
1424 addBucketToL2HashGroup(objectiveToAdd, allActiveKeys);
1425 return;
1426 }
1427 addBucketToEcmpHashGroup(objectiveToAdd, allActiveKeys);
Saurav Das1a129a02016-11-18 15:21:57 -08001428 } else if (nextObjective.type() == NextObjective.Type.BROADCAST) {
Yi Tseng47f82dc2017-03-05 22:48:39 -08001429 addBucketToBroadcastGroup(objectiveToAdd, allActiveKeys);
Saurav Das1a129a02016-11-18 15:21:57 -08001430 }
1431 }
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001432
piercd2a3ef2018-11-22 15:44:57 +01001433 private void addBucketToL2HashGroup(NextObjective nextObjective,
1434 List<Deque<GroupKey>> allActiveKeys) {
1435 // storage for all group keys in the chain of groups created
1436 List<Deque<GroupKey>> allGroupKeys = new ArrayList<>();
1437 List<GroupInfo> unsentGroups = new ArrayList<>();
1438 List<GroupBucket> newBuckets;
1439 // Prepare the l2 unfiltered groups
1440 createL2HashBuckets(nextObjective, allGroupKeys, unsentGroups);
1441 // now we can create the buckets to add to the outermost L2 hash group
1442 newBuckets = generateNextGroupBuckets(unsentGroups, SELECT);
1443 // retrieve the original l2 load balance group
1444 Group l2hashGroup = retrieveTopLevelGroup(allActiveKeys, deviceId,
1445 groupService, nextObjective.id());
1446 if (l2hashGroup == null) {
1447 fail(nextObjective, ObjectiveError.GROUPMISSING);
1448 return;
1449 }
1450 GroupKey l2hashGroupKey = l2hashGroup.appCookie();
1451 int l2hashGroupId = l2hashGroup.id().id();
1452 GroupDescription l2hashGroupDesc = new DefaultGroupDescription(deviceId,
1453 SELECT,
1454 new GroupBuckets(newBuckets),
1455 l2hashGroupKey,
1456 l2hashGroupId,
1457 nextObjective.appId());
1458 GroupChainElem l2hashGce = new GroupChainElem(l2hashGroupDesc,
1459 unsentGroups.size(),
1460 true,
1461 deviceId);
1462 // update new bucket-chains
1463 List<Deque<GroupKey>> addedKeys = new ArrayList<>();
1464 for (Deque<GroupKey> newBucketChain : allGroupKeys) {
1465 newBucketChain.addFirst(l2hashGroupKey);
1466 addedKeys.add(newBucketChain);
1467 }
1468 updatePendingNextObjective(l2hashGroupKey,
1469 new OfdpaNextGroup(addedKeys, nextObjective));
1470 log.debug("Adding to L2HASH: device:{} gid:{} group key:{} nextId:{}",
1471 deviceId, Integer.toHexString(l2hashGroupId),
1472 l2hashGroupKey, nextObjective.id());
1473 unsentGroups.forEach(groupInfo -> {
1474 // send the innermost group
1475 log.debug("Sending innermost group {} in group chain on device {} ",
1476 Integer.toHexString(groupInfo.innerMostGroupDesc().givenGroupId()),
1477 deviceId);
1478 updatePendingGroups(groupInfo.nextGroupDesc().appCookie(), l2hashGce);
1479 groupService.addGroup(groupInfo.innerMostGroupDesc());
1480 });
1481 }
1482
1483 private void addBucketToEcmpHashGroup(NextObjective nextObjective,
Yi Tseng78f51f42017-02-02 13:54:58 -08001484 List<Deque<GroupKey>> allActiveKeys) {
Charles Chan188ebf52015-12-23 00:15:11 -08001485 // storage for all group keys in the chain of groups created
1486 List<Deque<GroupKey>> allGroupKeys = new ArrayList<>();
1487 List<GroupInfo> unsentGroups = new ArrayList<>();
Yi Tsengef19de12017-04-24 11:33:05 -07001488 List<GroupBucket> newBuckets;
Charles Chan367c1c12018-10-19 16:23:28 -07001489 createEcmpHashBucketChains(nextObjective, allGroupKeys, unsentGroups);
Yi Tseng78f51f42017-02-02 13:54:58 -08001490 // now we can create the buckets to add to the outermost L3 ECMP group
1491 newBuckets = generateNextGroupBuckets(unsentGroups, SELECT);
Saurav Das1a129a02016-11-18 15:21:57 -08001492 // retrieve the original L3 ECMP group
Jonghwan Hyunf810a7a2018-02-12 16:43:45 +09001493 Group l3ecmpGroup = retrieveTopLevelGroup(allActiveKeys, deviceId,
1494 groupService, nextObjective.id());
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001495 if (l3ecmpGroup == null) {
Yi Tsengef19de12017-04-24 11:33:05 -07001496 fail(nextObjective, ObjectiveError.GROUPMISSING);
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001497 return;
1498 }
Saurav Das1a129a02016-11-18 15:21:57 -08001499 GroupKey l3ecmpGroupKey = l3ecmpGroup.appCookie();
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001500 int l3ecmpGroupId = l3ecmpGroup.id().id();
Charles Chan188ebf52015-12-23 00:15:11 -08001501 // Although GroupDescriptions are not necessary for adding buckets to
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001502 // existing groups, we still use one in the GroupChainElem. When the latter is
Charles Chan188ebf52015-12-23 00:15:11 -08001503 // processed, the info will be extracted for the bucketAdd call to groupService
1504 GroupDescription l3ecmpGroupDesc =
Yi Tsengef19de12017-04-24 11:33:05 -07001505 new DefaultGroupDescription(deviceId,
1506 SELECT,
1507 new GroupBuckets(newBuckets),
1508 l3ecmpGroupKey,
1509 l3ecmpGroupId,
1510 nextObjective.appId());
Yi Tseng78f51f42017-02-02 13:54:58 -08001511 GroupChainElem l3ecmpGce = new GroupChainElem(l3ecmpGroupDesc,
1512 unsentGroups.size(),
Yi Tsengef19de12017-04-24 11:33:05 -07001513 true,
1514 deviceId);
Charles Chan188ebf52015-12-23 00:15:11 -08001515
Saurav Dasc88d4662017-05-15 15:34:25 -07001516 // update new bucket-chains
1517 List<Deque<GroupKey>> addedKeys = new ArrayList<>();
1518 for (Deque<GroupKey> newBucketChain : allGroupKeys) {
1519 newBucketChain.addFirst(l3ecmpGroupKey);
1520 addedKeys.add(newBucketChain);
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001521 }
Saurav Dasc88d4662017-05-15 15:34:25 -07001522 updatePendingNextObjective(l3ecmpGroupKey,
1523 new OfdpaNextGroup(addedKeys, nextObjective));
Yi Tsengef19de12017-04-24 11:33:05 -07001524 log.debug("Adding to L3ECMP: device:{} gid:{} group key:{} nextId:{}",
Charles Chan188ebf52015-12-23 00:15:11 -08001525 deviceId, Integer.toHexString(l3ecmpGroupId),
1526 l3ecmpGroupKey, nextObjective.id());
Yi Tseng78f51f42017-02-02 13:54:58 -08001527 unsentGroups.forEach(groupInfo -> {
1528 // send the innermost group
1529 log.debug("Sending innermost group {} in group chain on device {} ",
Saurav Dasc88d4662017-05-15 15:34:25 -07001530 Integer.toHexString(groupInfo.innerMostGroupDesc().givenGroupId()),
1531 deviceId);
Yi Tsengef19de12017-04-24 11:33:05 -07001532 updatePendingGroups(groupInfo.nextGroupDesc().appCookie(), l3ecmpGce);
1533 groupService.addGroup(groupInfo.innerMostGroupDesc());
Yi Tseng78f51f42017-02-02 13:54:58 -08001534 });
Saurav Das1a129a02016-11-18 15:21:57 -08001535 }
Charles Chan188ebf52015-12-23 00:15:11 -08001536
Saurav Das1a129a02016-11-18 15:21:57 -08001537 private void addBucketToBroadcastGroup(NextObjective nextObj,
Yi Tsengef19de12017-04-24 11:33:05 -07001538 List<Deque<GroupKey>> allActiveKeys) {
1539 VlanId assignedVlan = readVlanFromSelector(nextObj.meta());
Saurav Das1a129a02016-11-18 15:21:57 -08001540 if (assignedVlan == null) {
1541 log.warn("VLAN ID required by broadcast next obj is missing. "
1542 + "Aborting add bucket to broadcast group for next:{} in dev:{}",
1543 nextObj.id(), deviceId);
Yi Tsengef19de12017-04-24 11:33:05 -07001544 fail(nextObj, ObjectiveError.BADPARAMS);
Saurav Das1a129a02016-11-18 15:21:57 -08001545 return;
1546 }
Saurav Das1a129a02016-11-18 15:21:57 -08001547 List<GroupInfo> groupInfos = prepareL2InterfaceGroup(nextObj, assignedVlan);
Yi Tsengef19de12017-04-24 11:33:05 -07001548 IpPrefix ipDst = readIpDstFromSelector(nextObj.meta());
Saurav Das1a129a02016-11-18 15:21:57 -08001549 if (ipDst != null) {
1550 if (ipDst.isMulticast()) {
Yi Tsengef19de12017-04-24 11:33:05 -07001551 addBucketToL3MulticastGroup(nextObj, allActiveKeys, groupInfos, assignedVlan);
Saurav Das1a129a02016-11-18 15:21:57 -08001552 } else {
1553 log.warn("Broadcast NextObj with non-multicast IP address {}", nextObj);
Yi Tsengef19de12017-04-24 11:33:05 -07001554 fail(nextObj, ObjectiveError.BADPARAMS);
Saurav Das1a129a02016-11-18 15:21:57 -08001555 }
1556 } else {
Yi Tsengef19de12017-04-24 11:33:05 -07001557 addBucketToL2FloodGroup(nextObj, allActiveKeys, groupInfos, assignedVlan);
Saurav Das1a129a02016-11-18 15:21:57 -08001558 }
1559 }
1560
1561 private void addBucketToL2FloodGroup(NextObjective nextObj,
1562 List<Deque<GroupKey>> allActiveKeys,
1563 List<GroupInfo> groupInfos,
Yi Tseng78f51f42017-02-02 13:54:58 -08001564 VlanId assignedVlan) {
Jonghwan Hyunf810a7a2018-02-12 16:43:45 +09001565 Group l2FloodGroup = retrieveTopLevelGroup(allActiveKeys, deviceId,
1566 groupService, nextObj.id());
Yi Tseng78f51f42017-02-02 13:54:58 -08001567
1568 if (l2FloodGroup == null) {
1569 log.warn("Can't find L2 flood group while adding bucket to it. NextObj = {}",
1570 nextObj);
Yi Tsengef19de12017-04-24 11:33:05 -07001571 fail(nextObj, ObjectiveError.GROUPMISSING);
Saurav Das1a129a02016-11-18 15:21:57 -08001572 return;
1573 }
Saurav Das1a129a02016-11-18 15:21:57 -08001574
Yi Tseng78f51f42017-02-02 13:54:58 -08001575 GroupKey l2floodGroupKey = l2FloodGroup.appCookie();
1576 int l2floodGroupId = l2FloodGroup.id().id();
1577 List<GroupBucket> newBuckets = generateNextGroupBuckets(groupInfos, ALL);
Yi Tseng78f51f42017-02-02 13:54:58 -08001578 GroupDescription l2FloodGroupDescription =
Yi Tsengef19de12017-04-24 11:33:05 -07001579 new DefaultGroupDescription(deviceId,
1580 ALL,
1581 new GroupBuckets(newBuckets),
1582 l2floodGroupKey,
1583 l2floodGroupId,
1584 nextObj.appId());
Yi Tseng78f51f42017-02-02 13:54:58 -08001585 GroupChainElem l2FloodGroupChainElement =
1586 new GroupChainElem(l2FloodGroupDescription,
1587 groupInfos.size(),
Yi Tsengef19de12017-04-24 11:33:05 -07001588 true,
1589 deviceId);
Yi Tseng78f51f42017-02-02 13:54:58 -08001590
Yi Tseng78f51f42017-02-02 13:54:58 -08001591
1592 //ensure assignedVlan applies to the chosen group
1593 VlanId floodGroupVlan = extractVlanIdFromGroupId(l2floodGroupId);
Yi Tseng78f51f42017-02-02 13:54:58 -08001594 if (!floodGroupVlan.equals(assignedVlan)) {
1595 log.warn("VLAN ID {} does not match Flood group {} to which bucket is "
1596 + "being added, for next:{} in dev:{}. Abort.", assignedVlan,
1597 Integer.toHexString(l2floodGroupId), nextObj.id(), deviceId);
Yi Tsengef19de12017-04-24 11:33:05 -07001598 fail(nextObj, ObjectiveError.BADPARAMS);
Yi Tseng78f51f42017-02-02 13:54:58 -08001599 return;
1600 }
Saurav Dasc88d4662017-05-15 15:34:25 -07001601 List<Deque<GroupKey>> addedKeys = new ArrayList<>();
Yi Tseng78f51f42017-02-02 13:54:58 -08001602 groupInfos.forEach(groupInfo -> {
1603 // update original NextGroup with new bucket-chain
Yi Tseng78f51f42017-02-02 13:54:58 -08001604 Deque<GroupKey> newBucketChain = new ArrayDeque<>();
Yi Tsengef19de12017-04-24 11:33:05 -07001605 newBucketChain.addFirst(groupInfo.nextGroupDesc().appCookie());
Yi Tseng78f51f42017-02-02 13:54:58 -08001606 newBucketChain.addFirst(l2floodGroupKey);
Saurav Dasc88d4662017-05-15 15:34:25 -07001607 addedKeys.add(newBucketChain);
Yi Tsengef19de12017-04-24 11:33:05 -07001608 log.debug("Adding to L2FLOOD: device:{} gid:{} group key:{} nextId:{}",
Yi Tseng78f51f42017-02-02 13:54:58 -08001609 deviceId, Integer.toHexString(l2floodGroupId),
1610 l2floodGroupKey, nextObj.id());
1611 // send the innermost group
1612 log.debug("Sending innermost group {} in group chain on device {} ",
Yi Tsengef19de12017-04-24 11:33:05 -07001613 Integer.toHexString(groupInfo.innerMostGroupDesc().givenGroupId()),
Yi Tseng78f51f42017-02-02 13:54:58 -08001614 deviceId);
Yi Tsengef19de12017-04-24 11:33:05 -07001615 updatePendingGroups(groupInfo.nextGroupDesc().appCookie(), l2FloodGroupChainElement);
Yi Tseng78f51f42017-02-02 13:54:58 -08001616
Yi Tsengef19de12017-04-24 11:33:05 -07001617 DeviceId innerMostGroupDevice = groupInfo.innerMostGroupDesc().deviceId();
1618 GroupKey innerMostGroupKey = groupInfo.innerMostGroupDesc().appCookie();
Yi Tseng78f51f42017-02-02 13:54:58 -08001619 Group existsL2IGroup = groupService.getGroup(innerMostGroupDevice, innerMostGroupKey);
1620
1621 if (existsL2IGroup != null) {
1622 // group already exist
1623 processPendingAddGroupsOrNextObjs(innerMostGroupKey, true);
1624 } else {
Yi Tsengef19de12017-04-24 11:33:05 -07001625 groupService.addGroup(groupInfo.innerMostGroupDesc());
Yi Tseng78f51f42017-02-02 13:54:58 -08001626 }
Yi Tseng78f51f42017-02-02 13:54:58 -08001627 });
Saurav Dasc88d4662017-05-15 15:34:25 -07001628
1629 updatePendingNextObjective(l2floodGroupKey,
1630 new OfdpaNextGroup(addedKeys, nextObj));
Yi Tseng78f51f42017-02-02 13:54:58 -08001631 }
1632
Saurav Das1a129a02016-11-18 15:21:57 -08001633 private void addBucketToL3MulticastGroup(NextObjective nextObj,
1634 List<Deque<GroupKey>> allActiveKeys,
1635 List<GroupInfo> groupInfos,
Yi Tseng78f51f42017-02-02 13:54:58 -08001636 VlanId assignedVlan) {
Pier Luigi21fffd22018-01-19 10:24:53 +01001637 // Create the buckets to add to the outermost L3 Multicast group
1638 List<GroupBucket> newBuckets = createL3MulticastBucket(groupInfos);
Saurav Das1a129a02016-11-18 15:21:57 -08001639
1640 // get the group being edited
Jonghwan Hyunf810a7a2018-02-12 16:43:45 +09001641 Group l3mcastGroup = retrieveTopLevelGroup(allActiveKeys, deviceId,
1642 groupService, nextObj.id());
Saurav Das1a129a02016-11-18 15:21:57 -08001643 if (l3mcastGroup == null) {
Yi Tsengef19de12017-04-24 11:33:05 -07001644 fail(nextObj, ObjectiveError.GROUPMISSING);
Saurav Das1a129a02016-11-18 15:21:57 -08001645 return;
1646 }
1647 GroupKey l3mcastGroupKey = l3mcastGroup.appCookie();
1648 int l3mcastGroupId = l3mcastGroup.id().id();
1649
1650 //ensure assignedVlan applies to the chosen group
Yi Tseng78f51f42017-02-02 13:54:58 -08001651 VlanId expectedVlan = extractVlanIdFromGroupId(l3mcastGroupId);
Saurav Das1a129a02016-11-18 15:21:57 -08001652 if (!expectedVlan.equals(assignedVlan)) {
1653 log.warn("VLAN ID {} does not match L3 Mcast group {} to which bucket is "
1654 + "being added, for next:{} in dev:{}. Abort.", assignedVlan,
1655 Integer.toHexString(l3mcastGroupId), nextObj.id(), deviceId);
Yi Tsengef19de12017-04-24 11:33:05 -07001656 fail(nextObj, ObjectiveError.BADPARAMS);
Saurav Das1a129a02016-11-18 15:21:57 -08001657 }
1658 GroupDescription l3mcastGroupDescription =
Yi Tsengef19de12017-04-24 11:33:05 -07001659 new DefaultGroupDescription(deviceId,
1660 ALL,
1661 new GroupBuckets(newBuckets),
1662 l3mcastGroupKey,
1663 l3mcastGroupId,
1664 nextObj.appId());
Saurav Das1a129a02016-11-18 15:21:57 -08001665 GroupChainElem l3mcastGce = new GroupChainElem(l3mcastGroupDescription,
Yi Tsengef19de12017-04-24 11:33:05 -07001666 groupInfos.size(),
1667 true,
1668 deviceId);
Saurav Dasc88d4662017-05-15 15:34:25 -07001669
1670 List<Deque<GroupKey>> addedKeys = new ArrayList<>();
Yi Tseng78f51f42017-02-02 13:54:58 -08001671 groupInfos.forEach(groupInfo -> {
1672 // update original NextGroup with new bucket-chain
1673 Deque<GroupKey> newBucketChain = new ArrayDeque<>();
Yi Tsengef19de12017-04-24 11:33:05 -07001674 newBucketChain.addFirst(groupInfo.innerMostGroupDesc().appCookie());
Yi Tseng78f51f42017-02-02 13:54:58 -08001675 // Add L3 interface group to the chain if there is one.
Yi Tsengef19de12017-04-24 11:33:05 -07001676 if (!groupInfo.nextGroupDesc().equals(groupInfo.innerMostGroupDesc())) {
1677 newBucketChain.addFirst(groupInfo.nextGroupDesc().appCookie());
Yi Tseng78f51f42017-02-02 13:54:58 -08001678 }
1679 newBucketChain.addFirst(l3mcastGroupKey);
Saurav Dasc88d4662017-05-15 15:34:25 -07001680 addedKeys.add(newBucketChain);
Yi Tseng78f51f42017-02-02 13:54:58 -08001681
Yi Tsengef19de12017-04-24 11:33:05 -07001682 updatePendingGroups(groupInfo.nextGroupDesc().appCookie(), l3mcastGce);
Yi Tseng78f51f42017-02-02 13:54:58 -08001683 // Point next group to inner-most group, if any
Yi Tsengef19de12017-04-24 11:33:05 -07001684 if (!groupInfo.nextGroupDesc().equals(groupInfo.innerMostGroupDesc())) {
1685 GroupChainElem innerGce = new GroupChainElem(groupInfo.nextGroupDesc(),
1686 1,
1687 false,
1688 deviceId);
1689 updatePendingGroups(groupInfo.innerMostGroupDesc().appCookie(), innerGce);
Yi Tseng78f51f42017-02-02 13:54:58 -08001690 }
Yi Tsengef19de12017-04-24 11:33:05 -07001691 log.debug("Adding to L3MCAST: device:{} gid:{} group key:{} nextId:{}",
Yi Tseng78f51f42017-02-02 13:54:58 -08001692 deviceId, Integer.toHexString(l3mcastGroupId),
1693 l3mcastGroupKey, nextObj.id());
1694 // send the innermost group
1695 log.debug("Sending innermost group {} in group chain on device {} ",
Yi Tsengef19de12017-04-24 11:33:05 -07001696 Integer.toHexString(groupInfo.innerMostGroupDesc().givenGroupId()),
Yi Tseng78f51f42017-02-02 13:54:58 -08001697 deviceId);
Yi Tsengef19de12017-04-24 11:33:05 -07001698 groupService.addGroup(groupInfo.innerMostGroupDesc());
Yi Tseng78f51f42017-02-02 13:54:58 -08001699
1700 });
1701
Saurav Das1a129a02016-11-18 15:21:57 -08001702 updatePendingNextObjective(l3mcastGroupKey,
Saurav Dasc88d4662017-05-15 15:34:25 -07001703 new OfdpaNextGroup(addedKeys, nextObj));
Charles Chan188ebf52015-12-23 00:15:11 -08001704 }
1705
1706 /**
Saurav Dasceccf242017-08-03 18:30:35 -07001707 * Removes buckets in the top level group of a possible group-chain. Does
Saurav Das1a129a02016-11-18 15:21:57 -08001708 * not remove the groups in the group-chain pointed to by this bucket, as they
Charles Chan188ebf52015-12-23 00:15:11 -08001709 * may be in use (referenced by other groups) elsewhere.
1710 *
Saurav Dasceccf242017-08-03 18:30:35 -07001711 * @param nextObjective a next objective that contains information for the
1712 * buckets to be removed from the group
1713 * @param next the representation of the existing group-chains for this next
1714 * objective, from which the top-level buckets to remove are determined
Charles Chan188ebf52015-12-23 00:15:11 -08001715 */
1716 protected void removeBucketFromGroup(NextObjective nextObjective, NextGroup next) {
Saurav Das1a129a02016-11-18 15:21:57 -08001717 if (nextObjective.type() != NextObjective.Type.HASHED &&
1718 nextObjective.type() != NextObjective.Type.BROADCAST) {
Charles Chan188ebf52015-12-23 00:15:11 -08001719 log.warn("RemoveBuckets not applied to nextType:{} in dev:{} for next:{}",
1720 nextObjective.type(), deviceId, nextObjective.id());
Yi Tsengef19de12017-04-24 11:33:05 -07001721 fail(nextObjective, ObjectiveError.UNSUPPORTED);
Charles Chan188ebf52015-12-23 00:15:11 -08001722 return;
1723 }
Yi Tsengef19de12017-04-24 11:33:05 -07001724 List<Deque<GroupKey>> allActiveKeys = appKryo.deserialize(next.data());
Saurav Dasceccf242017-08-03 18:30:35 -07001725 List<Integer> indicesToRemove = Lists.newArrayList();
1726 for (TrafficTreatment treatment : nextObjective.next()) {
1727 // find the top-level bucket in the group-chain by matching the
1728 // outport and label from different groups in the chain
1729 PortNumber portToRemove = readOutPortFromTreatment(treatment);
1730 int labelToRemove = readLabelFromTreatment(treatment);
1731 if (portToRemove == null) {
1732 log.warn("treatment {} of next objective {} has no outport.. "
1733 + "cannot remove bucket from group in dev: {}", treatment,
1734 nextObjective.id(), deviceId);
Charles Chan188ebf52015-12-23 00:15:11 -08001735 continue;
1736 }
Saurav Dasceccf242017-08-03 18:30:35 -07001737 List<Integer> existing = existingPortAndLabel(allActiveKeys,
1738 groupService, deviceId,
1739 portToRemove, labelToRemove);
1740 indicesToRemove.addAll(existing);
1741
Charles Chan188ebf52015-12-23 00:15:11 -08001742 }
Saurav Dasceccf242017-08-03 18:30:35 -07001743
1744 List<Deque<GroupKey>> chainsToRemove = Lists.newArrayList();
1745 indicesToRemove.forEach(index -> chainsToRemove
1746 .add(allActiveKeys.get(index)));
Yi Tseng78f51f42017-02-02 13:54:58 -08001747 if (chainsToRemove.isEmpty()) {
Charles Chan188ebf52015-12-23 00:15:11 -08001748 log.warn("Could not find appropriate group-chain for removing bucket"
1749 + " for next id {} in dev:{}", nextObjective.id(), deviceId);
Yi Tsengef19de12017-04-24 11:33:05 -07001750 fail(nextObjective, ObjectiveError.BADPARAMS);
Saurav Das1a129a02016-11-18 15:21:57 -08001751 return;
Charles Chan188ebf52015-12-23 00:15:11 -08001752 }
Saurav Dasceccf242017-08-03 18:30:35 -07001753 removeBucket(chainsToRemove, nextObjective);
1754 }
1755
1756 /**
1757 * Removes top-level buckets from a group that represents the given next objective.
1758 *
1759 * @param chainsToRemove a list of group bucket chains to remove
1760 * @param nextObjective the next objective that contains information for the
1761 * buckets to be removed from the group
1762 */
1763 protected void removeBucket(List<Deque<GroupKey>> chainsToRemove,
1764 NextObjective nextObjective) {
Yi Tseng78f51f42017-02-02 13:54:58 -08001765 List<GroupBucket> bucketsToRemove = Lists.newArrayList();
1766 //first group key is the one we want to modify
1767 GroupKey modGroupKey = chainsToRemove.get(0).peekFirst();
Saurav Das1a129a02016-11-18 15:21:57 -08001768 Group modGroup = groupService.getGroup(deviceId, modGroupKey);
Ray Milkey43869812018-05-04 13:00:39 -07001769 if (modGroup == null) {
1770 log.warn("removeBucket(): Attempt to modify non-existent group {} for device {}", modGroupKey, deviceId);
1771 return;
1772 }
Yi Tseng78f51f42017-02-02 13:54:58 -08001773 for (Deque<GroupKey> foundChain : chainsToRemove) {
1774 //second group key is the one we wish to remove the reference to
1775 if (foundChain.size() < 2) {
Saurav Dasceccf242017-08-03 18:30:35 -07001776 // additional check to make sure second group key exists in
Yi Tseng78f51f42017-02-02 13:54:58 -08001777 // the chain.
1778 log.warn("Can't find second group key from chain {}",
1779 foundChain);
1780 continue;
Saurav Das1a129a02016-11-18 15:21:57 -08001781 }
Saurav Dasceccf242017-08-03 18:30:35 -07001782 GroupKey pointedGroupKey = foundChain.stream()
1783 .collect(Collectors.toList()).get(1);
Yi Tseng78f51f42017-02-02 13:54:58 -08001784 Group pointedGroup = groupService.getGroup(deviceId, pointedGroupKey);
Yi Tseng78f51f42017-02-02 13:54:58 -08001785 if (pointedGroup == null) {
1786 continue;
1787 }
1788
1789 GroupBucket bucket;
1790 if (nextObjective.type() == NextObjective.Type.HASHED) {
1791 bucket = DefaultGroupBucket.createSelectGroupBucket(
1792 DefaultTrafficTreatment.builder()
1793 .group(pointedGroup.id())
1794 .build());
1795 } else {
1796 bucket = DefaultGroupBucket.createAllGroupBucket(
1797 DefaultTrafficTreatment.builder()
1798 .group(pointedGroup.id())
1799 .build());
1800 }
Yi Tseng78f51f42017-02-02 13:54:58 -08001801 bucketsToRemove.add(bucket);
Saurav Das1a129a02016-11-18 15:21:57 -08001802 }
Yi Tseng78f51f42017-02-02 13:54:58 -08001803
1804 GroupBuckets removeBuckets = new GroupBuckets(bucketsToRemove);
1805 List<String> pointedGroupIds; // for debug log
1806 pointedGroupIds = bucketsToRemove.stream()
1807 .map(GroupBucket::treatment)
1808 .map(TrafficTreatment::allInstructions)
1809 .flatMap(List::stream)
1810 .filter(inst -> inst instanceof Instructions.GroupInstruction)
1811 .map(inst -> (Instructions.GroupInstruction) inst)
1812 .map(Instructions.GroupInstruction::groupId)
1813 .map(GroupId::id)
1814 .map(Integer::toHexString)
1815 .map(id -> HEX_PREFIX + id)
1816 .collect(Collectors.toList());
1817
Yi Tseng78f51f42017-02-02 13:54:58 -08001818 log.debug("Removing buckets from group id 0x{} pointing to group id(s) {} "
Ray Milkey43869812018-05-04 13:00:39 -07001819 + "for next id {} in device {}",
1820 Integer.toHexString(modGroup.id().id()),
Yi Tseng78f51f42017-02-02 13:54:58 -08001821 pointedGroupIds, nextObjective.id(), deviceId);
1822 addPendingUpdateNextObjective(modGroupKey, nextObjective);
Saurav Das1a129a02016-11-18 15:21:57 -08001823 groupService.removeBucketsFromGroup(deviceId, modGroupKey,
1824 removeBuckets, modGroupKey,
1825 nextObjective.appId());
Saurav Dasceccf242017-08-03 18:30:35 -07001826 // update store - synchronize access as there may be multiple threads
1827 // trying to remove buckets from the same group, each with its own
1828 // potentially stale copy of allActiveKeys
Saurav Dasc88d4662017-05-15 15:34:25 -07001829 synchronized (flowObjectiveStore) {
Saurav Dasceccf242017-08-03 18:30:35 -07001830 // get a fresh copy of what the store holds
1831 NextGroup next = flowObjectiveStore.getNextGroup(nextObjective.id());
1832 List<Deque<GroupKey>> allActiveKeys = appKryo.deserialize(next.data());
Pier Luigid008dbe2018-01-15 09:48:49 +01001833 allActiveKeys = Lists.newArrayList(allActiveKeys);
Saurav Dasc88d4662017-05-15 15:34:25 -07001834 // Note that since we got a new object, and ArrayDeque does not implement
Saurav Dasceccf242017-08-03 18:30:35 -07001835 // Object.equals(), we have to check the deque elems one by one
1836 allActiveKeys
1837 .removeIf(active ->
1838 chainsToRemove.stream().anyMatch(remove ->
1839 Arrays.equals(remove.toArray(new GroupKey[0]),
1840 active.toArray(new GroupKey[0]))));
Saurav Dasc88d4662017-05-15 15:34:25 -07001841 // If no buckets in the group, then retain an entry for the
1842 // top level group which still exists.
1843 if (allActiveKeys.isEmpty()) {
1844 ArrayDeque<GroupKey> top = new ArrayDeque<>();
1845 top.add(modGroupKey);
1846 allActiveKeys.add(top);
1847 }
1848 flowObjectiveStore.putNextGroup(nextObjective.id(),
Saurav Dasceccf242017-08-03 18:30:35 -07001849 new OfdpaNextGroup(allActiveKeys,
1850 nextObjective));
Saurav Das1a129a02016-11-18 15:21:57 -08001851 }
Charles Chan188ebf52015-12-23 00:15:11 -08001852 }
1853
1854 /**
Saurav Das961beb22017-03-29 19:09:17 -07001855 * Removes all groups in multiple possible group-chains that represent the next-obj.
Charles Chan188ebf52015-12-23 00:15:11 -08001856 *
1857 * @param nextObjective the next objective to remove
1858 * @param next the NextGroup that represents the existing group-chain for
1859 * this next objective
1860 */
1861 protected void removeGroup(NextObjective nextObjective, NextGroup next) {
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001862
Yi Tsengef19de12017-04-24 11:33:05 -07001863 List<Deque<GroupKey>> allActiveKeys = appKryo.deserialize(next.data());
Charles Chanfc5c7802016-05-17 13:13:55 -07001864
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001865 List<GroupKey> groupKeys = allActiveKeys.stream()
Charles Chanfc5c7802016-05-17 13:13:55 -07001866 .map(Deque::getFirst).collect(Collectors.toList());
Yi Tsengef19de12017-04-24 11:33:05 -07001867 addPendingRemoveNextObjective(nextObjective, groupKeys);
Charles Chanfc5c7802016-05-17 13:13:55 -07001868
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -07001869 allActiveKeys
1870 .forEach(groupChain -> groupChain.forEach(groupKey -> groupService
1871 .removeGroup(deviceId, groupKey, nextObjective.appId())));
Charles Chan188ebf52015-12-23 00:15:11 -08001872 flowObjectiveStore.removeNextGroup(nextObjective.id());
1873 }
1874
Saurav Dasceccf242017-08-03 18:30:35 -07001875 /**
Ruchi Sahotaef0761c2019-01-28 01:08:18 +00001876 * modifies group with next objective.
1877 *
1878 * @param nextObjective the NextObjective
1879 * @param nextGroup the NextGroup
1880 */
1881 protected void modifyBucketFromGroup(NextObjective nextObjective, NextGroup nextGroup) {
1882 switch (nextObjective.type()) {
1883 case SIMPLE:
1884 Collection<TrafficTreatment> treatments = nextObjective.next();
1885 if (treatments.size() != 1) {
1886 log.error("Next Objectives of type Simple should only have a "
1887 + "single Traffic Treatment. Next Objective Id:{}",
1888 nextObjective.id());
1889 fail(nextObjective, ObjectiveError.BADPARAMS);
1890 return;
1891 }
1892 modifySimpleNextObjective(nextObjective, nextGroup);
1893 break;
1894 default:
1895 fail(nextObjective, ObjectiveError.UNKNOWN);
1896 log.warn("Unknown next objective type {}", nextObjective.type());
1897 }
1898 }
1899
1900 /**
1901 * As per the OFDPA 2.0 TTP, packets are sent out of ports by using
1902 * a chain of groups. The simple Next Objective passed in by the application
1903 * is broken up into a group chain. The following chains can be modified
1904 * depending on the parameters in the Next Objective.
1905 * 1. L2 Interface group (no chaining)
1906 * 2. L3 Unicast group -> L2 Interface group
1907 *
1908 * @param nextObj the nextObjective of type SIMPLE
1909 */
1910 private void modifySimpleNextObjective(NextObjective nextObj, NextGroup nextGroup) {
1911 TrafficTreatment treatment = nextObj.next().iterator().next();
1912 // determine if plain L2 or L3->L2 chain
1913 boolean plainL2 = true;
1914 for (Instruction ins : treatment.allInstructions()) {
1915 if (ins.type() == Instruction.Type.L2MODIFICATION) {
1916 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
1917 if (l2ins.subtype() == L2ModificationInstruction.L2SubType.ETH_DST ||
1918 l2ins.subtype() == L2ModificationInstruction.L2SubType.ETH_SRC ||
1919 l2ins.subtype() == L2ModificationInstruction.L2SubType.VLAN_ID) {
1920 plainL2 = false;
1921 }
1922 }
1923 }
1924 if (plainL2) {
1925 modifyBucketInL2Group(nextObj, nextGroup);
1926 } else {
1927 modifyBucketInL3Group(nextObj, nextGroup);
1928 }
1929 return;
1930 }
1931
1932
1933 /**
Jonghwan Hyunf810a7a2018-02-12 16:43:45 +09001934 * Modify buckets in the L2 interface group.
1935 *
1936 * @param nextObjective a next objective that contains information for the
1937 * buckets to be modified in the group
1938 * @param next the representation of the existing group-chains for this next
1939 * objective, from which the innermost group buckets to remove are determined
1940 */
Ruchi Sahotaef0761c2019-01-28 01:08:18 +00001941
1942 protected void modifyBucketInL2Group(NextObjective nextObjective, NextGroup next) {
Jonghwan Hyunf810a7a2018-02-12 16:43:45 +09001943
1944 VlanId assignedVlan = readVlanFromSelector(nextObjective.meta());
1945 if (assignedVlan == null) {
1946 log.warn("VLAN ID required by simple next obj is missing. Abort.");
1947 fail(nextObjective, ObjectiveError.BADPARAMS);
1948 return;
1949 }
1950
1951 List<GroupInfo> groupInfos = prepareL2InterfaceGroup(nextObjective, assignedVlan);
1952
1953 // There is only one L2 interface group in this case
1954 GroupDescription l2InterfaceGroupDesc = groupInfos.get(0).innerMostGroupDesc();
1955
1956 // Replace group bucket for L2 interface group
1957 groupService.setBucketsForGroup(deviceId,
1958 l2InterfaceGroupDesc.appCookie(),
1959 l2InterfaceGroupDesc.buckets(),
1960 l2InterfaceGroupDesc.appCookie(),
1961 l2InterfaceGroupDesc.appId());
1962
1963 // update store - synchronize access as there may be multiple threads
1964 // trying to remove buckets from the same group, each with its own
1965 // potentially stale copy of allActiveKeys
1966 synchronized (flowObjectiveStore) {
1967 List<Deque<GroupKey>> modifiedGroupKeys = Lists.newArrayList();
1968 ArrayDeque<GroupKey> top = new ArrayDeque<>();
1969 top.add(l2InterfaceGroupDesc.appCookie());
1970 modifiedGroupKeys.add(top);
1971
1972 flowObjectiveStore.putNextGroup(nextObjective.id(),
1973 new OfdpaNextGroup(modifiedGroupKeys,
1974 nextObjective));
1975 }
Ruchi Sahotaef0761c2019-01-28 01:08:18 +00001976
Jonghwan Hyunf810a7a2018-02-12 16:43:45 +09001977 }
1978
Ruchi Sahotaef0761c2019-01-28 01:08:18 +00001979 protected void modifyBucketInL3Group(NextObjective nextObjective, NextGroup next) {
1980
1981 //get l3 group
1982 GroupInfo groupInfo = prepareL3UnicastGroup(nextObjective, next);
1983 if (groupInfo == null) {
1984 log.warn("Null groupInfo retrieved for next obj. Abort.");
1985 fail(nextObjective, ObjectiveError.BADPARAMS);
1986 return;
1987 }
1988
1989 GroupDescription l3UnicastGroupDesc = groupInfo.nextGroupDesc();
1990
1991 // Replace group bucket for L3 UC interface group
1992 groupService.setBucketsForGroup(deviceId, l3UnicastGroupDesc.appCookie(),
1993 l3UnicastGroupDesc.buckets(), l3UnicastGroupDesc.appCookie(),
1994 l3UnicastGroupDesc.appId());
1995
1996 // create object for local and distributed storage
1997 Deque<GroupKey> gkeyChain = new ArrayDeque<>();
1998 gkeyChain.addFirst(groupInfo.innerMostGroupDesc().appCookie());
1999 gkeyChain.addFirst(groupInfo.nextGroupDesc().appCookie());
2000 List<Deque<GroupKey>> allGroupKeys = Lists.newArrayList();
2001 allGroupKeys.add(gkeyChain);
2002 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObjective);
2003 // store l3groupkey with the ofdpaNextGroup for the nextObjective that depends on it
2004 updatePendingNextObjective(groupInfo.nextGroupDesc().appCookie(), ofdpaGrp);
2005
2006 // update store - synchronize access as there may be multiple threads
2007 // trying to update bucket from the same group, each with its own
2008 // potentially stale copy of allActiveKeys
2009 synchronized (flowObjectiveStore) {
2010
2011 List<Deque<GroupKey>> modifiedGroupKeys = Lists.newArrayList();
2012 ArrayDeque<GroupKey> top = new ArrayDeque<>();
2013 top.add(l3UnicastGroupDesc.appCookie());
2014 top.add(groupInfo.innerMostGroupDesc().appCookie()); //l2 group key
2015 modifiedGroupKeys.add(top);
2016
2017 flowObjectiveStore.putNextGroup(nextObjective.id(),
2018 new OfdpaNextGroup(modifiedGroupKeys,
2019 nextObjective));
2020 }
2021 }
2022
2023
Jonghwan Hyunf810a7a2018-02-12 16:43:45 +09002024 /**
Saurav Dasceccf242017-08-03 18:30:35 -07002025 * Checks existing buckets in {@link NextGroup} to verify if they match
2026 * the buckets in the given {@link NextObjective}. Adds or removes buckets
2027 * to ensure that the buckets match up.
2028 *
2029 * @param nextObjective the next objective to verify
2030 * @param next the representation of the existing group which has to be
2031 * modified to match the given next objective
2032 */
2033 protected void verifyGroup(NextObjective nextObjective, NextGroup next) {
Pier Luigib72201b2018-01-25 16:16:02 +01002034 if (nextObjective.type() == NextObjective.Type.SIMPLE) {
2035 log.warn("verification not supported for indirect group");
Saurav Dasceccf242017-08-03 18:30:35 -07002036 fail(nextObjective, ObjectiveError.UNSUPPORTED);
2037 return;
2038 }
Charles Chan1dd75572018-05-08 11:49:05 -07002039 log.trace("Call to verify device:{} nextId:{}", deviceId, nextObjective.id());
Saurav Dasceccf242017-08-03 18:30:35 -07002040 List<Deque<GroupKey>> allActiveKeys = appKryo.deserialize(next.data());
2041 List<TrafficTreatment> bucketsToCreate = Lists.newArrayList();
2042 List<Integer> indicesToRemove = Lists.newArrayList();
Pier Luigiec6ac422018-01-29 10:30:59 +01002043
2044 // Iterating over the treatments of the next objective allows
2045 // to detect missing buckets and/or duplicate buckets (to be removed)
Saurav Dasceccf242017-08-03 18:30:35 -07002046 for (TrafficTreatment bkt : nextObjective.next()) {
2047 PortNumber portNumber = readOutPortFromTreatment(bkt);
2048 int label = readLabelFromTreatment(bkt);
2049 if (portNumber == null) {
2050 log.warn("treatment {} of next objective {} has no outport.. "
Ravi Dewanganf59ca9e2019-05-28 22:24:18 +00002051 + "cannot remove bucket from group in dev: {}", bkt, nextObjective.id(), deviceId);
Saurav Dasceccf242017-08-03 18:30:35 -07002052 fail(nextObjective, ObjectiveError.BADPARAMS);
2053 return;
2054 }
Ravi Dewanganf59ca9e2019-05-28 22:24:18 +00002055 List<Integer> existing = existingPortAndLabel(allActiveKeys, groupService, deviceId, portNumber, label);
Saurav Dasceccf242017-08-03 18:30:35 -07002056 if (existing.isEmpty()) {
2057 // if it doesn't exist, mark this bucket for creation
2058 bucketsToCreate.add(bkt);
2059 }
2060 if (existing.size() > 1) {
2061 // if it exists but there are duplicates, mark the others for removal
2062 existing.remove(0);
2063 indicesToRemove.addAll(existing);
2064 }
2065 }
2066
Pier Luigiec6ac422018-01-29 10:30:59 +01002067 // Detect situation where the next data has more buckets
2068 // (not duplicates) respect to the next objective
Saurav Dasa4020382018-02-14 14:14:54 -08002069 if (allActiveKeys.size() > nextObjective.next().size() &&
2070 // ignore specific case of empty group
2071 !(nextObjective.next().size() == 0 && allActiveKeys.size() == 1
2072 && allActiveKeys.get(0).size() == 1)) {
Pier Luigiec6ac422018-01-29 10:30:59 +01002073 log.warn("Mismatch detected between next and flowobjstore for device {}: " +
Saurav Dasa4020382018-02-14 14:14:54 -08002074 "nextId:{}, nextObjective-size:{} next-size:{} .. correcting",
2075 deviceId, nextObjective.id(), nextObjective.next().size(),
2076 allActiveKeys.size());
2077 List<Integer> otherIndices =
2078 indicesToRemoveFromNextGroup(allActiveKeys, nextObjective,
2079 groupService, deviceId);
Pier Luigiec6ac422018-01-29 10:30:59 +01002080 // Filter out the indices not present
2081 otherIndices = otherIndices.stream()
2082 .filter(index -> !indicesToRemove.contains(index))
2083 .collect(Collectors.toList());
2084 // Add all to the final list
2085 indicesToRemove.addAll(otherIndices);
2086 }
2087
Charles Chan1dd75572018-05-08 11:49:05 -07002088 log.trace("Buckets to create {}", bucketsToCreate);
2089 log.trace("Indices to remove {}", indicesToRemove);
Pier Luigib72201b2018-01-25 16:16:02 +01002090
Saurav Dasceccf242017-08-03 18:30:35 -07002091 if (!bucketsToCreate.isEmpty()) {
2092 log.info("creating {} buckets as part of nextId: {} verification",
2093 bucketsToCreate.size(), nextObjective.id());
2094 //create a nextObjective only with these buckets
2095 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
2096 .withId(nextObjective.id())
Pier Luigib72201b2018-01-25 16:16:02 +01002097 .withType(nextObjective.type())
Saurav Dasceccf242017-08-03 18:30:35 -07002098 .withMeta(nextObjective.meta())
2099 .fromApp(nextObjective.appId());
Pier Luigib72201b2018-01-25 16:16:02 +01002100 bucketsToCreate.forEach(nextObjBuilder::addTreatment);
2101 // According to the next type we call the proper add function
2102 if (nextObjective.type() == NextObjective.Type.HASHED) {
piercd2a3ef2018-11-22 15:44:57 +01002103 if (isL2Hash(nextObjective)) {
2104 addBucketToL2HashGroup(nextObjBuilder.addToExisting(), allActiveKeys);
2105 } else {
2106 addBucketToEcmpHashGroup(nextObjBuilder.addToExisting(), allActiveKeys);
2107 }
Pier Luigib72201b2018-01-25 16:16:02 +01002108 } else {
2109 addBucketToBroadcastGroup(nextObjBuilder.addToExisting(), allActiveKeys);
2110 }
Saurav Dasceccf242017-08-03 18:30:35 -07002111 }
2112
2113 if (!indicesToRemove.isEmpty()) {
2114 log.info("removing {} buckets as part of nextId: {} verification",
2115 indicesToRemove.size(), nextObjective.id());
2116 List<Deque<GroupKey>> chainsToRemove = Lists.newArrayList();
2117 indicesToRemove.forEach(index -> chainsToRemove
2118 .add(allActiveKeys.get(index)));
2119 removeBucket(chainsToRemove, nextObjective);
2120 }
2121
Saurav Das9df5b7c2017-08-14 16:44:43 -07002122 if (bucketsToCreate.isEmpty() && indicesToRemove.isEmpty()) {
2123 // flowObjective store record is in-sync with nextObjective passed-in
2124 // Nevertheless groupStore may not be in sync due to bug in the store
2125 // - see CORD-1844. XXX When this bug is fixed, the rest of this verify
2126 // method will not be required.
Pier Luigib72201b2018-01-25 16:16:02 +01002127 GroupKey topGroupKey = allActiveKeys.get(0).peekFirst();
2128 Group topGroup = groupService.getGroup(deviceId, topGroupKey);
Ravi Dewanganf59ca9e2019-05-28 22:24:18 +00002129 // topGroup should not be null - adding guard
2130 if (topGroup == null) {
2131 log.warn("topGroup {} not found in GroupStore device:{}, nextId:{}",
2132 topGroupKey, deviceId, nextObjective.id());
pier94a5d1772019-12-10 18:09:00 +01002133 fail(nextObjective, ObjectiveError.GROUPMISSING);
Ravi Dewanganf59ca9e2019-05-28 22:24:18 +00002134 return;
2135 }
Pier Luigib72201b2018-01-25 16:16:02 +01002136 int actualGroupSize = topGroup.buckets().buckets().size();
Saurav Das9df5b7c2017-08-14 16:44:43 -07002137 int objGroupSize = nextObjective.next().size();
2138 if (actualGroupSize != objGroupSize) {
2139 log.warn("Mismatch detected in device:{}, nextId:{}, nextObjective-size"
2140 + ":{} group-size:{} .. correcting", deviceId, nextObjective.id(),
2141 objGroupSize, actualGroupSize);
2142 }
2143 if (actualGroupSize > objGroupSize) {
Pier Luigib72201b2018-01-25 16:16:02 +01002144 // Group in the device has more chains
Saurav Das9df5b7c2017-08-14 16:44:43 -07002145 List<GroupBucket> bucketsToRemove = Lists.newArrayList();
2146 //check every bucket in the actual group
Pier Luigib72201b2018-01-25 16:16:02 +01002147 for (GroupBucket bucket : topGroup.buckets().buckets()) {
Saurav Das9df5b7c2017-08-14 16:44:43 -07002148 GroupInstruction g = (GroupInstruction) bucket.treatment()
2149 .allInstructions().iterator().next();
2150 GroupId gidToCheck = g.groupId(); // the group pointed to
2151 boolean matches = false;
2152 for (Deque<GroupKey> validChain : allActiveKeys) {
2153 if (validChain.size() < 2) {
2154 continue;
2155 }
2156 GroupKey pointedGroupKey = validChain.stream()
2157 .collect(Collectors.toList()).get(1);
2158 Group pointedGroup = groupService.getGroup(deviceId, pointedGroupKey);
2159 if (pointedGroup != null && gidToCheck.equals(pointedGroup.id())) {
2160 matches = true;
2161 break;
2162 }
2163 }
2164 if (!matches) {
2165 log.warn("Removing bucket pointing to groupId:{}", gidToCheck);
2166 bucketsToRemove.add(bucket);
2167 }
2168 }
2169 // remove buckets for which there was no record in the obj store
2170 if (bucketsToRemove.isEmpty()) {
2171 log.warn("Mismatch detected but could not determine which"
2172 + "buckets to remove");
2173 } else {
2174 GroupBuckets removeBuckets = new GroupBuckets(bucketsToRemove);
Pier Luigib72201b2018-01-25 16:16:02 +01002175 groupService.removeBucketsFromGroup(deviceId, topGroupKey,
2176 removeBuckets, topGroupKey,
Saurav Das9df5b7c2017-08-14 16:44:43 -07002177 nextObjective.appId());
2178 }
2179 } else if (actualGroupSize < objGroupSize) {
Pier Luigib72201b2018-01-25 16:16:02 +01002180 // Group in the device has less chains
Saurav Das9df5b7c2017-08-14 16:44:43 -07002181 // should also add buckets not in group-store but in obj-store
2182 List<GroupBucket> bucketsToAdd = Lists.newArrayList();
2183 //check every bucket in the obj
2184 for (Deque<GroupKey> validChain : allActiveKeys) {
2185 if (validChain.size() < 2) {
2186 continue;
2187 }
2188 GroupKey pointedGroupKey = validChain.stream()
2189 .collect(Collectors.toList()).get(1);
2190 Group pointedGroup = groupService.getGroup(deviceId, pointedGroupKey);
2191 if (pointedGroup == null) {
2192 // group should exist, otherwise cannot be added as bucket
2193 continue;
2194 }
2195 boolean matches = false;
Pier Luigib72201b2018-01-25 16:16:02 +01002196 for (GroupBucket bucket : topGroup.buckets().buckets()) {
Saurav Das9df5b7c2017-08-14 16:44:43 -07002197 GroupInstruction g = (GroupInstruction) bucket.treatment()
2198 .allInstructions().iterator().next();
2199 GroupId gidToCheck = g.groupId(); // the group pointed to
2200 if (pointedGroup.id().equals(gidToCheck)) {
2201 matches = true;
2202 break;
2203 }
2204 }
2205 if (!matches) {
2206 log.warn("Adding bucket pointing to groupId:{}", pointedGroup);
2207 TrafficTreatment t = DefaultTrafficTreatment.builder()
2208 .group(pointedGroup.id())
2209 .build();
Pier Luigib72201b2018-01-25 16:16:02 +01002210 // Create the proper bucket according to the next type
2211 if (nextObjective.type() == NextObjective.Type.HASHED) {
2212 bucketsToAdd.add(DefaultGroupBucket.createSelectGroupBucket(t));
2213 } else {
2214 bucketsToAdd.add(DefaultGroupBucket.createAllGroupBucket(t));
2215 }
Saurav Das9df5b7c2017-08-14 16:44:43 -07002216 }
2217 }
2218 if (bucketsToAdd.isEmpty()) {
2219 log.warn("Mismatch detected but could not determine which "
2220 + "buckets to add");
2221 } else {
2222 GroupBuckets addBuckets = new GroupBuckets(bucketsToAdd);
Pier Luigib72201b2018-01-25 16:16:02 +01002223 groupService.addBucketsToGroup(deviceId, topGroupKey,
2224 addBuckets, topGroupKey,
Saurav Das9df5b7c2017-08-14 16:44:43 -07002225 nextObjective.appId());
2226 }
2227 }
2228 }
2229
Saurav Dasceccf242017-08-03 18:30:35 -07002230 pass(nextObjective);
2231 }
2232
2233 //////////////////////////////////////
2234 // Helper methods and classes
2235 //////////////////////////////////////
2236
Yi Tsengef19de12017-04-24 11:33:05 -07002237 protected void updatePendingNextObjective(GroupKey groupKey, OfdpaNextGroup nextGrp) {
2238 pendingAddNextObjectives.asMap().compute(groupKey, (k, val) -> {
Saurav Das961beb22017-03-29 19:09:17 -07002239 if (val == null) {
Yi Tsengef19de12017-04-24 11:33:05 -07002240 val = new CopyOnWriteArrayList<>();
Saurav Das961beb22017-03-29 19:09:17 -07002241 }
Yi Tsengef19de12017-04-24 11:33:05 -07002242 val.add(nextGrp);
Saurav Das961beb22017-03-29 19:09:17 -07002243 return val;
2244 });
Saurav Das8be4e3a2016-03-11 17:19:07 -08002245 }
2246
Yi Tsengef19de12017-04-24 11:33:05 -07002247 protected void updatePendingGroups(GroupKey groupKey, GroupChainElem gce) {
2248 pendingGroups.asMap().compute(groupKey, (k, val) -> {
Saurav Das961beb22017-03-29 19:09:17 -07002249 if (val == null) {
2250 val = Sets.newConcurrentHashSet();
2251 }
2252 val.add(gce);
2253 return val;
2254 });
Saurav Das8be4e3a2016-03-11 17:19:07 -08002255 }
2256
Yi Tsengef19de12017-04-24 11:33:05 -07002257 protected void addPendingUpdateNextObjective(GroupKey groupKey,
2258 NextObjective nextObjective) {
Yi Tseng78f51f42017-02-02 13:54:58 -08002259 pendingUpdateNextObjectives.compute(groupKey, (gKey, nextObjs) -> {
2260 if (nextObjs != null) {
2261 nextObjs.add(nextObjective);
2262 } else {
2263 nextObjs = Sets.newHashSet(nextObjective);
2264 }
2265 return nextObjs;
2266 });
2267 }
2268
Saurav Das2f2c9d02018-04-07 16:51:09 -07002269 protected void addPendingRemoveNextObjective(NextObjective nextObjective,
2270 List<GroupKey> groupKeys) {
2271 pendingRemoveNextObjectives.put(nextObjective, groupKeys);
Yi Tsengef19de12017-04-24 11:33:05 -07002272 }
2273
Saurav Das2f2c9d02018-04-07 16:51:09 -07002274 protected int getNextAvailableIndex() {
2275 return (int) nextIndex.incrementAndGet();
2276 }
2277
2278 protected void processPendingUpdateNextObjs(GroupKey groupKey) {
2279 Set<NextObjective> nextObjs = pendingUpdateNextObjectives.remove(groupKey);
2280 if (nextObjs != null) {
2281 nextObjs.forEach(nextObj -> {
2282 log.debug("Group {} updated, update pending next objective {}.",
2283 groupKey, nextObj);
2284 pass(nextObj);
2285 });
2286 }
2287 }
2288
2289 protected void processPendingRemoveNextObjs(GroupKey key) {
Yi Tsengef19de12017-04-24 11:33:05 -07002290 pendingRemoveNextObjectives.asMap().forEach((nextObjective, groupKeys) -> {
jaegonkim2e4d99e2018-07-09 23:23:10 +09002291 groupKeys.remove(key);
Yi Tsengef19de12017-04-24 11:33:05 -07002292 if (groupKeys.isEmpty()) {
2293 pendingRemoveNextObjectives.invalidate(nextObjective);
2294 pass(nextObjective);
Yi Tsengef19de12017-04-24 11:33:05 -07002295 }
2296 });
2297 }
2298
Yi Tsengef19de12017-04-24 11:33:05 -07002299 protected void processPendingAddGroupsOrNextObjs(GroupKey key, boolean added) {
2300 //first check for group chain
2301 Set<OfdpaGroupHandlerUtility.GroupChainElem> gceSet = pendingGroups.asMap().remove(key);
2302 if (gceSet != null) {
2303 for (GroupChainElem gce : gceSet) {
2304 log.debug("Group service {} group key {} in device {}. "
2305 + "Processing next group in group chain with group id 0x{}",
2306 (added) ? "ADDED" : "processed",
2307 key, deviceId,
2308 Integer.toHexString(gce.groupDescription().givenGroupId()));
2309 processGroupChain(gce);
2310 }
2311 } else {
2312 // otherwise chain complete - check for waiting nextObjectives
2313 List<OfdpaGroupHandlerUtility.OfdpaNextGroup> nextGrpList =
2314 pendingAddNextObjectives.getIfPresent(key);
2315 if (nextGrpList != null) {
2316 pendingAddNextObjectives.invalidate(key);
2317 nextGrpList.forEach(nextGrp -> {
2318 log.debug("Group service {} group key {} in device:{}. "
2319 + "Done implementing next objective: {} <<-->> gid:0x{}",
2320 (added) ? "ADDED" : "processed",
2321 key, deviceId, nextGrp.nextObjective().id(),
2322 Integer.toHexString(groupService.getGroup(deviceId, key)
2323 .givenGroupId()));
2324 pass(nextGrp.nextObjective());
Saurav Dasc88d4662017-05-15 15:34:25 -07002325 updateFlowObjectiveStore(nextGrp.nextObjective().id(), nextGrp);
Yi Tsengef19de12017-04-24 11:33:05 -07002326
2327 // check if addBuckets waiting for this completion
2328 pendingBuckets.compute(nextGrp.nextObjective().id(), (nextId, pendBkts) -> {
2329 if (pendBkts != null) {
2330 pendBkts.forEach(pendBkt -> addBucketToGroup(pendBkt, nextGrp));
2331 }
2332 return null;
2333 });
2334 });
2335 }
2336 }
2337 }
2338
Charles Chan188ebf52015-12-23 00:15:11 -08002339 /**
2340 * Processes next element of a group chain. Assumption is that if this
2341 * group points to another group, the latter has already been created
2342 * and this driver has received notification for it. A second assumption is
2343 * that if there is another group waiting for this group then the appropriate
2344 * stores already have the information to act upon the notification for the
2345 * creation of this group.
2346 * <p>
2347 * The processing of the GroupChainElement depends on the number of groups
2348 * this element is waiting on. For all group types other than SIMPLE, a
2349 * GroupChainElement could be waiting on multiple groups.
2350 *
2351 * @param gce the group chain element to be processed next
2352 */
2353 private void processGroupChain(GroupChainElem gce) {
2354 int waitOnGroups = gce.decrementAndGetGroupsWaitedOn();
2355 if (waitOnGroups != 0) {
2356 log.debug("GCE: {} not ready to be processed", gce);
2357 return;
2358 }
2359 log.debug("GCE: {} ready to be processed", gce);
Yi Tsengef19de12017-04-24 11:33:05 -07002360 if (gce.addBucketToGroup()) {
2361 groupService.addBucketsToGroup(gce.groupDescription().deviceId(),
2362 gce.groupDescription().appCookie(),
2363 gce.groupDescription().buckets(),
2364 gce.groupDescription().appCookie(),
2365 gce.groupDescription().appId());
Charles Chan188ebf52015-12-23 00:15:11 -08002366 } else {
Yi Tsengef19de12017-04-24 11:33:05 -07002367 groupService.addGroup(gce.groupDescription());
Charles Chan188ebf52015-12-23 00:15:11 -08002368 }
2369 }
2370
Saurav Dasc88d4662017-05-15 15:34:25 -07002371 private void updateFlowObjectiveStore(Integer nextId, OfdpaNextGroup nextGrp) {
2372 synchronized (flowObjectiveStore) {
2373 // get fresh copy of what the store holds
2374 NextGroup next = flowObjectiveStore.getNextGroup(nextId);
2375 if (next == null || nextGrp.nextObjective().op() == Operation.ADD) {
2376 flowObjectiveStore.putNextGroup(nextId, nextGrp);
2377 return;
2378 }
2379 if (nextGrp.nextObjective().op() == Operation.ADD_TO_EXISTING) {
2380 List<Deque<GroupKey>> allActiveKeys = appKryo.deserialize(next.data());
Pier Luigid008dbe2018-01-15 09:48:49 +01002381 allActiveKeys = Lists.newArrayList(allActiveKeys);
Saurav Dasc88d4662017-05-15 15:34:25 -07002382 // If active keys shows only the top-level group without a chain of groups,
2383 // then it represents an empty group. Update by replacing empty chain.
2384 if (allActiveKeys.size() == 1 && allActiveKeys.get(0).size() == 1) {
2385 allActiveKeys.clear();
2386 }
2387 allActiveKeys.addAll(nextGrp.allKeys());
2388 flowObjectiveStore.putNextGroup(nextId,
2389 new OfdpaNextGroup(allActiveKeys, nextGrp.nextObjective()));
2390 }
2391 }
2392 }
2393
Saurav Das8be4e3a2016-03-11 17:19:07 -08002394 private class InnerGroupListener implements GroupListener {
2395 @Override
2396 public void event(GroupEvent event) {
Charles Chanfc5c7802016-05-17 13:13:55 -07002397 switch (event.type()) {
2398 case GROUP_ADDED:
2399 processPendingAddGroupsOrNextObjs(event.subject().appCookie(), true);
2400 break;
2401 case GROUP_REMOVED:
2402 processPendingRemoveNextObjs(event.subject().appCookie());
2403 break;
Yi Tseng78f51f42017-02-02 13:54:58 -08002404 case GROUP_UPDATED:
2405 processPendingUpdateNextObjs(event.subject().appCookie());
2406 break;
Charles Chanfc5c7802016-05-17 13:13:55 -07002407 default:
2408 break;
Saurav Das8be4e3a2016-03-11 17:19:07 -08002409 }
2410 }
2411 }
Charles Chan188ebf52015-12-23 00:15:11 -08002412}