Update MRI projects for Aluminium
[openflowplugin.git] / openflowplugin-impl / src / main / java / org / opendaylight / openflowplugin / impl / util / FlatBatchUtil.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.openflowplugin.impl.util;
10
11 import com.google.common.annotations.VisibleForTesting;
12 import com.google.common.base.Function;
13 import com.google.common.util.concurrent.Futures;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import com.google.common.util.concurrent.MoreExecutors;
16 import java.util.ArrayList;
17 import java.util.Collection;
18 import java.util.EnumSet;
19 import java.util.List;
20 import org.opendaylight.openflowplugin.impl.services.batch.BatchPlanStep;
21 import org.opendaylight.openflowplugin.impl.services.batch.BatchStepType;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.ProcessFlatBatchOutput;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.ProcessFlatBatchOutputBuilder;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.Batch;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.BatchChoice;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchAddFlowCase;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchAddGroupCase;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchAddMeterCase;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchRemoveFlowCase;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchRemoveGroupCase;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchRemoveMeterCase;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchUpdateFlowCase;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchUpdateGroupCase;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchUpdateMeterCase;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.output.BatchFailure;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.service.batch.common.rev160322.BatchOrderGrouping;
37 import org.opendaylight.yangtools.yang.binding.DataContainer;
38 import org.opendaylight.yangtools.yang.common.RpcError;
39 import org.opendaylight.yangtools.yang.common.RpcResult;
40 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
41
42 /**
43  * Provides flat batch util methods.
44  */
45 public final class FlatBatchUtil {
46
47     private FlatBatchUtil() {
48         throw new IllegalStateException("This class should not be instantiated.");
49     }
50
51     public static void markBarriersWhereNeeded(final List<BatchPlanStep> batchPlan) {
52         final EnumSet<BatchStepType> previousTypes = EnumSet.noneOf(BatchStepType.class);
53
54         BatchPlanStep previousPlanStep = null;
55         for (BatchPlanStep planStep : batchPlan) {
56             final BatchStepType type = planStep.getStepType();
57             if (!previousTypes.isEmpty() && decideBarrier(previousTypes, type)) {
58                 previousPlanStep.setBarrierAfter(true);
59                 previousTypes.clear();
60             }
61             previousTypes.add(type);
62             previousPlanStep = planStep;
63         }
64     }
65
66     @VisibleForTesting
67     static boolean decideBarrier(final EnumSet<BatchStepType> previousTypes, final BatchStepType type) {
68         return isFlowBarrierNeeded(previousTypes, type)
69                 || isGroupBarrierNeeded(previousTypes, type)
70                 || isMeterBarrierNeeded(previousTypes, type);
71     }
72
73     private static boolean isFlowBarrierNeeded(final EnumSet<BatchStepType> previousTypes, final BatchStepType type) {
74         return (type == BatchStepType.FLOW_ADD
75                 || type == BatchStepType.FLOW_UPDATE)
76                 && (previousTypes.contains(BatchStepType.GROUP_ADD)
77                 || previousTypes.contains(BatchStepType.METER_ADD));
78     }
79
80     private static boolean isGroupBarrierNeeded(final EnumSet<BatchStepType> previousTypes, final BatchStepType type) {
81         return type == BatchStepType.GROUP_ADD
82                 && (previousTypes.contains(BatchStepType.GROUP_ADD)
83                 || previousTypes.contains(BatchStepType.GROUP_UPDATE))
84                 || type == BatchStepType.GROUP_REMOVE
85                 && (previousTypes.contains(BatchStepType.FLOW_REMOVE)
86                 || previousTypes.contains(BatchStepType.FLOW_UPDATE)
87                 || previousTypes.contains(BatchStepType.GROUP_REMOVE)
88                 || previousTypes.contains(BatchStepType.GROUP_UPDATE));
89     }
90
91     private static boolean isMeterBarrierNeeded(final EnumSet<BatchStepType> previousTypes, final BatchStepType type) {
92         return type == BatchStepType.METER_REMOVE
93                 && (previousTypes.contains(BatchStepType.FLOW_REMOVE)
94                 || previousTypes.contains(BatchStepType.FLOW_UPDATE));
95     }
96
97     public static List<BatchPlanStep> assembleBatchPlan(final Collection<Batch> batches) {
98         final List<BatchPlanStep> plan = new ArrayList<>();
99
100         BatchPlanStep planStep;
101         for (Batch batch : batches) {
102             final BatchStepType nextStepType = detectBatchStepType(batch.getBatchChoice());
103
104             planStep = new BatchPlanStep(nextStepType);
105             planStep.getTaskBag().addAll(extractBatchData(planStep.getStepType(), batch.getBatchChoice()));
106             if (!planStep.isEmpty()) {
107                 plan.add(planStep);
108             }
109         }
110
111         return plan;
112     }
113
114     private static Collection<? extends BatchOrderGrouping> extractBatchData(final BatchStepType batchStepType,
115                                                                        final BatchChoice batchChoice) {
116         final Collection<? extends BatchOrderGrouping> batchData;
117         switch (batchStepType) {
118             case FLOW_ADD:
119                 batchData = ((FlatBatchAddFlowCase) batchChoice).nonnullFlatBatchAddFlow().values();
120                 break;
121             case FLOW_REMOVE:
122                 batchData = ((FlatBatchRemoveFlowCase) batchChoice).nonnullFlatBatchRemoveFlow().values();
123                 break;
124             case FLOW_UPDATE:
125                 batchData = ((FlatBatchUpdateFlowCase) batchChoice).nonnullFlatBatchUpdateFlow().values();
126                 break;
127             case GROUP_ADD:
128                 batchData = ((FlatBatchAddGroupCase) batchChoice).nonnullFlatBatchAddGroup().values();
129                 break;
130             case GROUP_REMOVE:
131                 batchData = ((FlatBatchRemoveGroupCase) batchChoice).nonnullFlatBatchRemoveGroup().values();
132                 break;
133             case GROUP_UPDATE:
134                 batchData = ((FlatBatchUpdateGroupCase) batchChoice).nonnullFlatBatchUpdateGroup().values();
135                 break;
136             case METER_ADD:
137                 batchData = ((FlatBatchAddMeterCase) batchChoice).nonnullFlatBatchAddMeter().values();
138                 break;
139             case METER_REMOVE:
140                 batchData = ((FlatBatchRemoveMeterCase) batchChoice).nonnullFlatBatchRemoveMeter().values();
141                 break;
142             case METER_UPDATE:
143                 batchData = ((FlatBatchUpdateMeterCase) batchChoice).nonnullFlatBatchUpdateMeter().values();
144                 break;
145             default:
146                 throw new IllegalArgumentException("Unsupported batch step type obtained: " + batchStepType);
147         }
148         return batchData;
149     }
150
151     @VisibleForTesting
152     static <T extends BatchChoice> BatchStepType detectBatchStepType(final T batchCase) {
153         final BatchStepType type;
154         final Class<? extends DataContainer> implementedInterface = batchCase.implementedInterface();
155
156         // FIXME: use a lookup table instead of this cascade
157         if (FlatBatchAddFlowCase.class.equals(implementedInterface)) {
158             type = BatchStepType.FLOW_ADD;
159         } else if (FlatBatchRemoveFlowCase.class.equals(implementedInterface)) {
160             type = BatchStepType.FLOW_REMOVE;
161         } else if (FlatBatchUpdateFlowCase.class.equals(implementedInterface)) {
162             type = BatchStepType.FLOW_UPDATE;
163         } else if (FlatBatchAddGroupCase.class.equals(implementedInterface)) {
164             type = BatchStepType.GROUP_ADD;
165         } else if (FlatBatchRemoveGroupCase.class.equals(implementedInterface)) {
166             type = BatchStepType.GROUP_REMOVE;
167         } else if (FlatBatchUpdateGroupCase.class.equals(implementedInterface)) {
168             type = BatchStepType.GROUP_UPDATE;
169         } else if (FlatBatchAddMeterCase.class.equals(implementedInterface)) {
170             type = BatchStepType.METER_ADD;
171         } else if (FlatBatchRemoveMeterCase.class.equals(implementedInterface)) {
172             type = BatchStepType.METER_REMOVE;
173         } else if (FlatBatchUpdateMeterCase.class.equals(implementedInterface)) {
174             type = BatchStepType.METER_UPDATE;
175         } else {
176             throw new IllegalArgumentException("Unsupported batch obtained: " + implementedInterface);
177         }
178         return type;
179     }
180
181     @VisibleForTesting
182     static Function<List<RpcResult<ProcessFlatBatchOutput>>, RpcResult<ProcessFlatBatchOutput>> mergeRpcResults() {
183         return jobsResults -> {
184             boolean isSuccessful = true;
185             List<RpcError> rpcErrors = new ArrayList<>();
186             List<BatchFailure> batchFailures = new ArrayList<>();
187
188             for (RpcResult<ProcessFlatBatchOutput> jobResult : jobsResults) {
189                 if (jobResult != null) {
190                     isSuccessful = isSuccessful && jobResult.isSuccessful();
191                     rpcErrors.addAll(jobResult.getErrors());
192                     batchFailures.addAll(jobResult.getResult().nonnullBatchFailure().values());
193                 }
194             }
195
196             return RpcResultBuilder.<ProcessFlatBatchOutput>status(isSuccessful)
197                     .withRpcErrors(rpcErrors)
198                     .withResult(new ProcessFlatBatchOutputBuilder().setBatchFailure(batchFailures).build())
199                     .build();
200         };
201     }
202
203     /**
204      * Merge list of Futures with partial results into one ListenableFuture with single result.
205      * @param firedJobs list of ListenableFutures with RPC results {@link ProcessFlatBatchOutput}
206      * @return ListenableFuture of RPC result with combined status and all errors + batch failures
207      */
208     public static ListenableFuture<RpcResult<ProcessFlatBatchOutput>> mergeJobsResultsFutures(
209             final List<ListenableFuture<RpcResult<ProcessFlatBatchOutput>>> firedJobs) {
210         return Futures.transform(Futures.successfulAsList(firedJobs),
211                 mergeRpcResults(),
212                 MoreExecutors.directExecutor());
213     }
214
215     /**
216      * Creates empty result future for flat batch service.
217      * @param status RPC result status
218      * @return ListenableFuture of RPC result with empty list of errors and batch failures
219      */
220     public static ListenableFuture<RpcResult<ProcessFlatBatchOutput>> createEmptyRpcBatchResultFuture(
221             final boolean status) {
222         return RpcResultBuilder.<ProcessFlatBatchOutput>status(status)
223                                .withRpcErrors(new ArrayList<>())
224                                .withResult(new ProcessFlatBatchOutputBuilder()
225                                        .setBatchFailure(new ArrayList<>())
226                                        .build())
227                                .buildFuture();
228     }
229 }