blob: e344404d20859fe92d3efcde82c3a980f66cfaf7 [file] [log] [blame]
Yi Tseng0b809722017-11-03 10:23:26 -07001/*
2 * Copyright 2017-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.pipelines.fabric.pipeliner;
18
Yi Tsengfe13f3e2018-08-19 03:09:54 +080019import com.google.common.base.MoreObjects;
20import com.google.common.base.Objects;
Yi Tsengf78e1742018-04-08 19:57:17 +080021import com.google.common.cache.Cache;
22import com.google.common.cache.CacheBuilder;
23import com.google.common.cache.RemovalCause;
24import com.google.common.cache.RemovalListener;
Yi Tseng0b809722017-11-03 10:23:26 -070025import com.google.common.collect.ImmutableList;
26import com.google.common.collect.Lists;
Yi Tsengf78e1742018-04-08 19:57:17 +080027import com.google.common.collect.Sets;
Yi Tseng0b809722017-11-03 10:23:26 -070028import org.onlab.util.KryoNamespace;
Yi Tsengfe13f3e2018-08-19 03:09:54 +080029import org.onlab.util.Tools;
Yi Tsengf78e1742018-04-08 19:57:17 +080030import org.onosproject.core.GroupId;
Yi Tseng0b809722017-11-03 10:23:26 -070031import org.onosproject.net.DeviceId;
32import org.onosproject.net.PortNumber;
33import org.onosproject.net.behaviour.NextGroup;
34import org.onosproject.net.behaviour.Pipeliner;
35import org.onosproject.net.behaviour.PipelinerContext;
36import org.onosproject.net.driver.AbstractHandlerBehaviour;
Yi Tseng4fd28432018-02-01 14:48:03 -080037import org.onosproject.net.driver.Driver;
Yi Tsengf78e1742018-04-08 19:57:17 +080038import org.onosproject.net.flow.FlowId;
Yi Tseng0b809722017-11-03 10:23:26 -070039import org.onosproject.net.flow.FlowRule;
40import org.onosproject.net.flow.FlowRuleOperations;
41import org.onosproject.net.flow.FlowRuleOperationsContext;
42import org.onosproject.net.flow.FlowRuleService;
43import org.onosproject.net.flow.instructions.Instruction;
44import org.onosproject.net.flow.instructions.Instructions;
45import org.onosproject.net.flowobjective.FilteringObjective;
46import org.onosproject.net.flowobjective.FlowObjectiveStore;
47import org.onosproject.net.flowobjective.ForwardingObjective;
48import org.onosproject.net.flowobjective.NextObjective;
49import org.onosproject.net.flowobjective.Objective;
50import org.onosproject.net.flowobjective.ObjectiveError;
51import org.onosproject.net.group.GroupDescription;
52import org.onosproject.net.group.GroupEvent;
53import org.onosproject.net.group.GroupListener;
54import org.onosproject.net.group.GroupService;
55import org.onosproject.store.serializers.KryoNamespaces;
56import org.slf4j.Logger;
57
58import java.util.Collection;
59import java.util.List;
Yi Tseng1b154bd2017-11-20 17:48:19 -080060import java.util.Map;
Yi Tsengf78e1742018-04-08 19:57:17 +080061import java.util.Set;
Yi Tsengfe13f3e2018-08-19 03:09:54 +080062import java.util.concurrent.CompletableFuture;
Yi Tsengf78e1742018-04-08 19:57:17 +080063import java.util.concurrent.ConcurrentHashMap;
Yi Tsengfe13f3e2018-08-19 03:09:54 +080064import java.util.concurrent.ExecutorService;
65import java.util.concurrent.Executors;
Yi Tseng0b809722017-11-03 10:23:26 -070066import java.util.concurrent.TimeUnit;
Yi Tseng0b809722017-11-03 10:23:26 -070067import java.util.function.Consumer;
Yi Tseng1b154bd2017-11-20 17:48:19 -080068import java.util.stream.Collectors;
Yi Tseng0b809722017-11-03 10:23:26 -070069
70import static org.slf4j.LoggerFactory.getLogger;
71
72/**
73 * Pipeliner for fabric pipeline.
74 */
75public class FabricPipeliner extends AbstractHandlerBehaviour implements Pipeliner {
76 private static final Logger log = getLogger(FabricPipeliner.class);
77
78 protected static final KryoNamespace KRYO = new KryoNamespace.Builder()
79 .register(KryoNamespaces.API)
80 .register(FabricNextGroup.class)
81 .build("FabricPipeliner");
82
Yi Tsengf78e1742018-04-08 19:57:17 +080083 private static final Set<GroupEvent.Type> GROUP_FAILED_TYPES =
84 Sets.newHashSet(GroupEvent.Type.GROUP_ADD_FAILED,
85 GroupEvent.Type.GROUP_REMOVE_FAILED,
86 GroupEvent.Type.GROUP_UPDATE_FAILED);
87
Yi Tseng0b809722017-11-03 10:23:26 -070088 // TODO: make this configurable
Yi Tseng1b154bd2017-11-20 17:48:19 -080089 private static final long DEFAULT_INSTALLATION_TIME_OUT = 40;
Yi Tsengfe13f3e2018-08-19 03:09:54 +080090 private static final int NUM_CALLBACK_THREAD = 2;
Yi Tseng0b809722017-11-03 10:23:26 -070091
92 protected DeviceId deviceId;
93 protected FlowRuleService flowRuleService;
94 protected GroupService groupService;
Yi Tsengf78e1742018-04-08 19:57:17 +080095 protected GroupListener groupListener = new InternalGroupListener();
Yi Tseng0b809722017-11-03 10:23:26 -070096 protected FlowObjectiveStore flowObjectiveStore;
97 protected FabricFilteringPipeliner pipelinerFilter;
98 protected FabricForwardingPipeliner pipelinerForward;
99 protected FabricNextPipeliner pipelinerNext;
100
Yi Tsengf78e1742018-04-08 19:57:17 +0800101 private Map<FlowId, PendingInstallObjective> pendingInstallObjectiveFlows = new ConcurrentHashMap<>();
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800102 private Map<PendingGroupKey, PendingInstallObjective> pendingInstallObjectiveGroups = new ConcurrentHashMap<>();
Yi Tsengf78e1742018-04-08 19:57:17 +0800103 private Cache<Objective, PendingInstallObjective> pendingInstallObjectives = CacheBuilder.newBuilder()
104 .expireAfterWrite(DEFAULT_INSTALLATION_TIME_OUT, TimeUnit.SECONDS)
105 .removalListener((RemovalListener<Objective, PendingInstallObjective>) removalNotification -> {
106 RemovalCause cause = removalNotification.getCause();
107 PendingInstallObjective pio = removalNotification.getValue();
108 if (cause == RemovalCause.EXPIRED && pio != null) {
109 pio.failed(ObjectiveError.INSTALLATIONTIMEOUT);
110 }
111 })
112 .build();
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800113 private static ExecutorService flowObjCallbackExecutor =
114 Executors.newFixedThreadPool(NUM_CALLBACK_THREAD, Tools.groupedThreads("fabric-pipeliner", "cb-", log));
Yi Tsengf78e1742018-04-08 19:57:17 +0800115
Yi Tseng0b809722017-11-03 10:23:26 -0700116
117 @Override
118 public void init(DeviceId deviceId, PipelinerContext context) {
Yi Tseng4fd28432018-02-01 14:48:03 -0800119 Driver driver = handler().driver();
Yi Tseng0b809722017-11-03 10:23:26 -0700120 this.deviceId = deviceId;
121 this.flowRuleService = context.directory().get(FlowRuleService.class);
122 this.groupService = context.directory().get(GroupService.class);
Yi Tsengf78e1742018-04-08 19:57:17 +0800123 this.groupService.addListener(groupListener);
Yi Tseng0b809722017-11-03 10:23:26 -0700124 this.flowObjectiveStore = context.directory().get(FlowObjectiveStore.class);
125 this.pipelinerFilter = new FabricFilteringPipeliner(deviceId);
126 this.pipelinerForward = new FabricForwardingPipeliner(deviceId);
Yi Tseng4fd28432018-02-01 14:48:03 -0800127 this.pipelinerNext = new FabricNextPipeliner(deviceId, driver);
Yi Tseng0b809722017-11-03 10:23:26 -0700128 }
129
130 @Override
131 public void filter(FilteringObjective filterObjective) {
132 PipelinerTranslationResult result = pipelinerFilter.filter(filterObjective);
133 if (result.error().isPresent()) {
134 fail(filterObjective, result.error().get());
135 return;
136 }
137
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800138 applyTranslationResult(filterObjective, result, error -> {
139 if (error == null) {
Yi Tseng0b809722017-11-03 10:23:26 -0700140 success(filterObjective);
141 } else {
142 fail(filterObjective, ObjectiveError.FLOWINSTALLATIONFAILED);
143 }
144 });
145 }
146
147 @Override
148 public void forward(ForwardingObjective forwardObjective) {
149 PipelinerTranslationResult result = pipelinerForward.forward(forwardObjective);
150 if (result.error().isPresent()) {
151 fail(forwardObjective, result.error().get());
152 return;
153 }
154
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800155 applyTranslationResult(forwardObjective, result, error -> {
156 if (error == null) {
Yi Tseng0b809722017-11-03 10:23:26 -0700157 success(forwardObjective);
158 } else {
159 fail(forwardObjective, ObjectiveError.FLOWINSTALLATIONFAILED);
160 }
161 });
162 }
163
164 @Override
165 public void next(NextObjective nextObjective) {
166 PipelinerTranslationResult result = pipelinerNext.next(nextObjective);
167
168 if (result.error().isPresent()) {
169 fail(nextObjective, result.error().get());
170 return;
171 }
172
Yi Tseng1b154bd2017-11-20 17:48:19 -0800173 if (nextObjective.op() == Objective.Operation.VERIFY) {
174 // TODO: support VERIFY operation
175 log.debug("Currently we don't support VERIFY operation, return success directly to the context");
176 success(nextObjective);
177 return;
178 }
179
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800180 applyTranslationResult(nextObjective, result, error -> {
181 if (error != null) {
182 fail(nextObjective, error);
Yi Tseng0b809722017-11-03 10:23:26 -0700183 return;
184 }
185
186 // Success, put next group to objective store
187 List<PortNumber> portNumbers = Lists.newArrayList();
188 nextObjective.next().forEach(treatment -> {
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800189 treatment.allInstructions()
Yi Tseng0b809722017-11-03 10:23:26 -0700190 .stream()
191 .filter(inst -> inst.type() == Instruction.Type.OUTPUT)
192 .map(inst -> (Instructions.OutputInstruction) inst)
193 .findFirst()
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800194 .map(Instructions.OutputInstruction::port)
195 .ifPresent(portNumbers::add);
Yi Tseng0b809722017-11-03 10:23:26 -0700196 });
197 FabricNextGroup nextGroup = new FabricNextGroup(nextObjective.type(),
198 portNumbers);
199 flowObjectiveStore.putNextGroup(nextObjective.id(), nextGroup);
200 success(nextObjective);
201 });
202 }
203
204 @Override
205 public List<String> getNextMappings(NextGroup nextGroup) {
Yi Tseng1b154bd2017-11-20 17:48:19 -0800206 FabricNextGroup fabricNextGroup = KRYO.deserialize(nextGroup.data());
207 NextObjective.Type type = fabricNextGroup.type();
208 Collection<PortNumber> outputPorts = fabricNextGroup.outputPorts();
209
210 return outputPorts.stream()
211 .map(port -> String.format("%s -> %s", type, port))
212 .collect(Collectors.toList());
Yi Tseng0b809722017-11-03 10:23:26 -0700213 }
214
215 private void applyTranslationResult(Objective objective,
216 PipelinerTranslationResult result,
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800217 Consumer<ObjectiveError> callback) {
Yi Tseng0b809722017-11-03 10:23:26 -0700218 Collection<GroupDescription> groups = result.groups();
219 Collection<FlowRule> flowRules = result.flowRules();
Yi Tsengf78e1742018-04-08 19:57:17 +0800220
221 Set<FlowId> flowIds = flowRules.stream().map(FlowRule::id).collect(Collectors.toSet());
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800222 Set<PendingGroupKey> pendingGroupKeys = groups.stream().map(GroupDescription::givenGroupId)
223 .map(GroupId::new)
224 .map(gid -> new PendingGroupKey(gid, objective.op()))
225 .collect(Collectors.toSet());
Yi Tsengf78e1742018-04-08 19:57:17 +0800226
227 PendingInstallObjective pio =
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800228 new PendingInstallObjective(objective, flowIds, pendingGroupKeys, callback);
Yi Tsengf78e1742018-04-08 19:57:17 +0800229
230 flowIds.forEach(flowId -> {
231 pendingInstallObjectiveFlows.put(flowId, pio);
232 });
233
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800234 pendingGroupKeys.forEach(pendingGroupKey -> {
235 pendingInstallObjectiveGroups.put(pendingGroupKey, pio);
Yi Tsengf78e1742018-04-08 19:57:17 +0800236 });
237
238 pendingInstallObjectives.put(objective, pio);
239 installGroups(objective, groups);
240 installFlows(objective, flowRules);
Yi Tseng0b809722017-11-03 10:23:26 -0700241 }
242
Yi Tsengf78e1742018-04-08 19:57:17 +0800243 private void installFlows(Objective objective, Collection<FlowRule> flowRules) {
Yi Tseng0b809722017-11-03 10:23:26 -0700244 if (flowRules.isEmpty()) {
Yi Tsengf78e1742018-04-08 19:57:17 +0800245 return;
Yi Tseng0b809722017-11-03 10:23:26 -0700246 }
Yi Tsengf78e1742018-04-08 19:57:17 +0800247
Yi Tseng0b809722017-11-03 10:23:26 -0700248 FlowRuleOperationsContext ctx = new FlowRuleOperationsContext() {
249 @Override
250 public void onSuccess(FlowRuleOperations ops) {
Yi Tsengf78e1742018-04-08 19:57:17 +0800251 ops.stages().forEach(stage -> {
252 stage.forEach(op -> {
253 FlowId flowId = op.rule().id();
254 PendingInstallObjective pio = pendingInstallObjectiveFlows.remove(flowId);
255
256 if (pio != null) {
257 pio.flowInstalled(flowId);
258 }
259 });
260 });
Yi Tseng0b809722017-11-03 10:23:26 -0700261 }
262
263 @Override
264 public void onError(FlowRuleOperations ops) {
265 log.warn("Failed to install flow rules: {}", flowRules);
Yi Tsengf78e1742018-04-08 19:57:17 +0800266 PendingInstallObjective pio = pendingInstallObjectives.getIfPresent(objective);
267 if (pio != null) {
268 pio.failed(ObjectiveError.FLOWINSTALLATIONFAILED);
269 }
Yi Tseng0b809722017-11-03 10:23:26 -0700270 }
271 };
272
273 FlowRuleOperations ops = buildFlowRuleOps(objective, flowRules, ctx);
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800274 if (ops != null) {
275 flowRuleService.apply(ops);
276 } else {
277 // remove pendings
278 flowRules.forEach(flowRule -> pendingInstallObjectiveFlows.remove(flowRule.id()));
279 }
Yi Tseng0b809722017-11-03 10:23:26 -0700280 }
281
Yi Tsengf78e1742018-04-08 19:57:17 +0800282 private void installGroups(Objective objective, Collection<GroupDescription> groups) {
Yi Tseng0b809722017-11-03 10:23:26 -0700283 if (groups.isEmpty()) {
Yi Tsengf78e1742018-04-08 19:57:17 +0800284 return;
Yi Tseng0b809722017-11-03 10:23:26 -0700285 }
Yi Tseng0b809722017-11-03 10:23:26 -0700286
287 switch (objective.op()) {
288 case ADD:
289 groups.forEach(groupService::addGroup);
290 break;
291 case REMOVE:
292 groups.forEach(group -> groupService.removeGroup(deviceId, group.appCookie(), objective.appId()));
293 break;
Yi Tseng1b154bd2017-11-20 17:48:19 -0800294 case ADD_TO_EXISTING:
295 groups.forEach(group -> {
296 groupService.addBucketsToGroup(deviceId, group.appCookie(),
297 group.buckets(),
298 group.appCookie(),
299 group.appId());
300 });
301 break;
302 case REMOVE_FROM_EXISTING:
303 groups.forEach(group -> {
304 groupService.removeBucketsFromGroup(deviceId, group.appCookie(),
305 group.buckets(),
306 group.appCookie(),
307 group.appId());
308 });
309 break;
Yi Tseng0b809722017-11-03 10:23:26 -0700310 default:
311 log.warn("Unsupported objective operation {}", objective.op());
Yi Tseng0b809722017-11-03 10:23:26 -0700312 }
313 }
314
315 static void fail(Objective objective, ObjectiveError error) {
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800316 CompletableFuture.runAsync(() -> {
317 objective.context().ifPresent(ctx -> ctx.onError(objective, error));
318 }, flowObjCallbackExecutor);
319
Yi Tseng0b809722017-11-03 10:23:26 -0700320 }
321
322 static void success(Objective objective) {
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800323 CompletableFuture.runAsync(() -> {
324 objective.context().ifPresent(ctx -> ctx.onSuccess(objective));
325 }, flowObjCallbackExecutor);
Yi Tseng0b809722017-11-03 10:23:26 -0700326 }
327
328 static FlowRuleOperations buildFlowRuleOps(Objective objective, Collection<FlowRule> flowRules,
329 FlowRuleOperationsContext ctx) {
330 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
331 switch (objective.op()) {
332 case ADD:
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800333 case ADD_TO_EXISTING: // For egress VLAN
Yi Tseng0b809722017-11-03 10:23:26 -0700334 flowRules.forEach(ops::add);
335 break;
336 case REMOVE:
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800337 case REMOVE_FROM_EXISTING: // For egress VLAN
Yi Tseng0b809722017-11-03 10:23:26 -0700338 flowRules.forEach(ops::remove);
339 break;
340 default:
Yi Tseng20f9e7b2018-05-24 23:27:39 +0800341 log.warn("Unsupported op {} for {}", objective.op(), objective);
Yi Tseng0b809722017-11-03 10:23:26 -0700342 fail(objective, ObjectiveError.BADPARAMS);
343 return null;
344 }
345 return ops.build(ctx);
346 }
347
348 class FabricNextGroup implements NextGroup {
349 private NextObjective.Type type;
350 private Collection<PortNumber> outputPorts;
351
352 public FabricNextGroup(NextObjective.Type type, Collection<PortNumber> outputPorts) {
353 this.type = type;
354 this.outputPorts = ImmutableList.copyOf(outputPorts);
355 }
356
357 public NextObjective.Type type() {
358 return type;
359 }
360
361 public Collection<PortNumber> outputPorts() {
362 return outputPorts;
363 }
364
365 @Override
366 public byte[] data() {
367 return KRYO.serialize(this);
368 }
369 }
Yi Tseng1b154bd2017-11-20 17:48:19 -0800370
Yi Tsengf78e1742018-04-08 19:57:17 +0800371 class InternalGroupListener implements GroupListener {
372 @Override
373 public void event(GroupEvent event) {
374 GroupId groupId = event.subject().id();
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800375 PendingGroupKey pendingGroupKey = new PendingGroupKey(groupId, event.type());
376 PendingInstallObjective pio = pendingInstallObjectiveGroups.remove(pendingGroupKey);
Yi Tsengf78e1742018-04-08 19:57:17 +0800377 if (GROUP_FAILED_TYPES.contains(event.type())) {
378 pio.failed(ObjectiveError.GROUPINSTALLATIONFAILED);
379 }
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800380 pio.groupInstalled(pendingGroupKey);
Yi Tsengf78e1742018-04-08 19:57:17 +0800381 }
382
383 @Override
384 public boolean isRelevant(GroupEvent event) {
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800385 PendingGroupKey pendingGroupKey = new PendingGroupKey(event.subject().id(), event.type());
386 return pendingInstallObjectiveGroups.containsKey(pendingGroupKey);
Yi Tsengf78e1742018-04-08 19:57:17 +0800387 }
388 }
389
390 class PendingInstallObjective {
391 Objective objective;
392 Collection<FlowId> flowIds;
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800393 Collection<PendingGroupKey> pendingGroupKeys;
394 Consumer<ObjectiveError> callback;
Yi Tsengf78e1742018-04-08 19:57:17 +0800395
396 public PendingInstallObjective(Objective objective, Collection<FlowId> flowIds,
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800397 Collection<PendingGroupKey> pendingGroupKeys,
398 Consumer<ObjectiveError> callback) {
Yi Tsengf78e1742018-04-08 19:57:17 +0800399 this.objective = objective;
400 this.flowIds = flowIds;
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800401 this.pendingGroupKeys = pendingGroupKeys;
Yi Tsengf78e1742018-04-08 19:57:17 +0800402 this.callback = callback;
403 }
404
405 void flowInstalled(FlowId flowId) {
406 flowIds.remove(flowId);
407 checkIfFinished();
408 }
409
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800410 void groupInstalled(PendingGroupKey pendingGroupKey) {
411 pendingGroupKeys.remove(pendingGroupKey);
Yi Tsengf78e1742018-04-08 19:57:17 +0800412 checkIfFinished();
413 }
414
415 private void checkIfFinished() {
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800416 if (flowIds.isEmpty() && pendingGroupKeys.isEmpty()) {
Yi Tsengf78e1742018-04-08 19:57:17 +0800417 pendingInstallObjectives.invalidate(objective);
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800418 callback.accept(null);
Yi Tsengf78e1742018-04-08 19:57:17 +0800419 }
420 }
421
422 void failed(ObjectiveError error) {
423 flowIds.forEach(pendingInstallObjectiveFlows::remove);
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800424 pendingGroupKeys.forEach(pendingInstallObjectiveGroups::remove);
Yi Tsengf78e1742018-04-08 19:57:17 +0800425 pendingInstallObjectives.invalidate(objective);
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800426 callback.accept(error);
427 }
428 }
429
430 class PendingGroupKey {
431 private GroupId groupId;
432 private GroupEvent.Type expectedEventType;
433
434 PendingGroupKey(GroupId groupId, GroupEvent.Type expectedEventType) {
435 this.groupId = groupId;
436 this.expectedEventType = expectedEventType;
437 }
438
439 PendingGroupKey(GroupId groupId, NextObjective.Operation objOp) {
440 this.groupId = groupId;
441
442 switch (objOp) {
443 case ADD:
444 expectedEventType = GroupEvent.Type.GROUP_ADDED;
445 break;
446 case REMOVE:
447 expectedEventType = GroupEvent.Type.GROUP_REMOVED;
448 break;
449 case MODIFY:
450 case ADD_TO_EXISTING:
451 case REMOVE_FROM_EXISTING:
452 expectedEventType = GroupEvent.Type.GROUP_UPDATED;
453 break;
454 default:
455 expectedEventType = null;
456 }
457 }
458
459 @Override
460 public boolean equals(Object o) {
461 if (this == o) {
462 return true;
463 }
464 if (o == null || getClass() != o.getClass()) {
465 return false;
466 }
467 PendingGroupKey pendingGroupKey = (PendingGroupKey) o;
468 return Objects.equal(groupId, pendingGroupKey.groupId) &&
469 expectedEventType == pendingGroupKey.expectedEventType;
470 }
471
472 @Override
473 public int hashCode() {
474 return Objects.hashCode(groupId, expectedEventType);
475 }
476
477 @Override
478 public String toString() {
479 return MoreObjects.toStringHelper(this)
480 .add("groupId", groupId)
481 .add("expectedEventType", expectedEventType)
482 .toString();
Yi Tsengf78e1742018-04-08 19:57:17 +0800483 }
484 }
Yi Tseng0b809722017-11-03 10:23:26 -0700485}