Merge "Bug 6366 - of-switch-config-pusher - DTCL instead of DTL"
[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 java.util.ArrayList;
16 import java.util.EnumSet;
17 import java.util.List;
18 import org.opendaylight.openflowplugin.impl.services.batch.BatchPlanStep;
19 import org.opendaylight.openflowplugin.impl.services.batch.BatchStepType;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.ProcessFlatBatchOutput;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.ProcessFlatBatchOutputBuilder;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.Batch;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.BatchChoice;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchAddFlowCase;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchAddGroupCase;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchAddMeterCase;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchRemoveFlowCase;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchRemoveGroupCase;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchRemoveMeterCase;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchUpdateFlowCase;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchUpdateGroupCase;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchUpdateMeterCase;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.output.BatchFailure;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.service.batch.common.rev160322.BatchOrderGrouping;
35 import org.opendaylight.yangtools.yang.binding.DataContainer;
36 import org.opendaylight.yangtools.yang.common.RpcError;
37 import org.opendaylight.yangtools.yang.common.RpcResult;
38 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
39
40 /**
41  * provides flat batch util methods
42  */
43 public final class FlatBatchUtil {
44
45     private FlatBatchUtil() {
46         throw new IllegalStateException("This class should not be instantiated.");
47     }
48
49     public static void markBarriersWhereNeeded(final List<BatchPlanStep> batchPlan) {
50         final EnumSet<BatchStepType> previousTypes = EnumSet.noneOf(BatchStepType.class);
51
52         BatchPlanStep previousPlanStep = null;
53         for (BatchPlanStep planStep : batchPlan) {
54             final BatchStepType type = planStep.getStepType();
55             if (!previousTypes.isEmpty() && decideBarrier(previousTypes, type)) {
56                 previousPlanStep.setBarrierAfter(true);
57                 previousTypes.clear();
58             }
59             previousTypes.add(type);
60             previousPlanStep = planStep;
61         }
62     }
63
64     @VisibleForTesting
65     static boolean decideBarrier(final EnumSet<BatchStepType> previousTypes, final BatchStepType type) {
66         return isFlowBarrierNeeded(previousTypes, type)
67                 || isGroupBarrierNeeded(previousTypes, type)
68                 || isMeterBarrierNeeded(previousTypes, type);
69     }
70
71     private static boolean isFlowBarrierNeeded(final EnumSet<BatchStepType> previousTypes, final BatchStepType type) {
72         return (type == BatchStepType.FLOW_ADD
73                 || type == BatchStepType.FLOW_UPDATE)
74                 && (previousTypes.contains(BatchStepType.GROUP_ADD)
75                 || previousTypes.contains(BatchStepType.METER_ADD));
76     }
77
78     private static boolean isGroupBarrierNeeded(final EnumSet<BatchStepType> previousTypes, final BatchStepType type) {
79         return (type == BatchStepType.GROUP_ADD
80                 && (previousTypes.contains(BatchStepType.GROUP_ADD)
81                 || previousTypes.contains(BatchStepType.GROUP_UPDATE)))
82                 || (type == BatchStepType.GROUP_REMOVE
83                 && (previousTypes.contains(BatchStepType.FLOW_REMOVE)
84                 || previousTypes.contains(BatchStepType.FLOW_UPDATE)
85                 || previousTypes.contains(BatchStepType.GROUP_REMOVE)
86                 || previousTypes.contains(BatchStepType.GROUP_UPDATE)));
87     }
88
89     private static boolean isMeterBarrierNeeded(final EnumSet<BatchStepType> previousTypes, final BatchStepType type) {
90         return type == BatchStepType.METER_REMOVE
91                 && (previousTypes.contains(BatchStepType.FLOW_REMOVE)
92                 || previousTypes.contains(BatchStepType.FLOW_UPDATE));
93     }
94
95     public static List<BatchPlanStep> assembleBatchPlan(List<Batch> batches) {
96         final List<BatchPlanStep> plan = new ArrayList<>();
97
98         BatchPlanStep planStep;
99         for (Batch batch : batches) {
100             final BatchStepType nextStepType = detectBatchStepType(batch.getBatchChoice());
101
102             planStep = new BatchPlanStep(nextStepType);
103             planStep.getTaskBag().addAll(extractBatchData(planStep.getStepType(), batch.getBatchChoice()));
104             if (!planStep.isEmpty()) {
105                 plan.add(planStep);
106             }
107         }
108
109         return plan;
110     }
111
112     private static List<? extends BatchOrderGrouping> extractBatchData(final BatchStepType batchStepType,
113                                                                        final BatchChoice batchChoice) {
114         final List<? extends BatchOrderGrouping> batchData;
115         switch (batchStepType) {
116             case FLOW_ADD:
117                 batchData = ((FlatBatchAddFlowCase) batchChoice).getFlatBatchAddFlow();
118                 break;
119             case FLOW_REMOVE:
120                 batchData = ((FlatBatchRemoveFlowCase) batchChoice).getFlatBatchRemoveFlow();
121                 break;
122             case FLOW_UPDATE:
123                 batchData = ((FlatBatchUpdateFlowCase) batchChoice).getFlatBatchUpdateFlow();
124                 break;
125             case GROUP_ADD:
126                 batchData = ((FlatBatchAddGroupCase) batchChoice).getFlatBatchAddGroup();
127                 break;
128             case GROUP_REMOVE:
129                 batchData = ((FlatBatchRemoveGroupCase) batchChoice).getFlatBatchRemoveGroup();
130                 break;
131             case GROUP_UPDATE:
132                 batchData = ((FlatBatchUpdateGroupCase) batchChoice).getFlatBatchUpdateGroup();
133                 break;
134             case METER_ADD:
135                 batchData = ((FlatBatchAddMeterCase) batchChoice).getFlatBatchAddMeter();
136                 break;
137             case METER_REMOVE:
138                 batchData = ((FlatBatchRemoveMeterCase) batchChoice).getFlatBatchRemoveMeter();
139                 break;
140             case METER_UPDATE:
141                 batchData = ((FlatBatchUpdateMeterCase) batchChoice).getFlatBatchUpdateMeter();
142                 break;
143             default:
144                 throw new IllegalArgumentException("Unsupported batch step type obtained: " + batchStepType);
145         }
146         return batchData;
147     }
148
149     @VisibleForTesting
150     static <T extends BatchChoice> BatchStepType detectBatchStepType(final T batchCase) {
151         final BatchStepType type;
152         final Class<? extends DataContainer> implementedInterface = batchCase.getImplementedInterface();
153
154         if (FlatBatchAddFlowCase.class.equals(implementedInterface)) {
155             type = BatchStepType.FLOW_ADD;
156         } else if (FlatBatchRemoveFlowCase.class.equals(implementedInterface)) {
157             type = BatchStepType.FLOW_REMOVE;
158         } else if (FlatBatchUpdateFlowCase.class.equals(implementedInterface)) {
159             type = BatchStepType.FLOW_UPDATE;
160         } else if (FlatBatchAddGroupCase.class.equals(implementedInterface)) {
161             type = BatchStepType.GROUP_ADD;
162         } else if (FlatBatchRemoveGroupCase.class.equals(implementedInterface)) {
163             type = BatchStepType.GROUP_REMOVE;
164         } else if (FlatBatchUpdateGroupCase.class.equals(implementedInterface)) {
165             type = BatchStepType.GROUP_UPDATE;
166         } else if (FlatBatchAddMeterCase.class.equals(implementedInterface)) {
167             type = BatchStepType.METER_ADD;
168         } else if (FlatBatchRemoveMeterCase.class.equals(implementedInterface)) {
169             type = BatchStepType.METER_REMOVE;
170         } else if (FlatBatchUpdateMeterCase.class.equals(implementedInterface)) {
171             type = BatchStepType.METER_UPDATE;
172         } else {
173             throw new IllegalArgumentException("Unsupported batch obtained: " + implementedInterface);
174         }
175         return type;
176     }
177
178     /**
179      * @return RPC result incorporating partial results (state, errors, batch failures)
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().getBatchFailure());
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      * shortcut for {@link #mergeRpcResults()}
206      * @param firedJobs list of ListenableFutures with RPC results {@link ProcessFlatBatchOutput}
207      * @return ListenableFuture of RPC result with combined status and all errors + batch failures
208      */
209     public static ListenableFuture<RpcResult<ProcessFlatBatchOutput>> mergeJobsResultsFutures(
210             final List<ListenableFuture<RpcResult<ProcessFlatBatchOutput>>> firedJobs) {
211         return Futures.transform(Futures.successfulAsList(firedJobs), mergeRpcResults());
212     }
213
214     /**
215      *
216      * @param status RPC result status
217      * @return ListenableFuture of RPC result with empty list of errors and batch failures
218      */
219     public static ListenableFuture<RpcResult<ProcessFlatBatchOutput>> createEmptyRpcBatchResultFuture(final boolean status) {
220         return RpcResultBuilder.<ProcessFlatBatchOutput>status(status)
221                                .withRpcErrors(new ArrayList<>())
222                                .withResult(new ProcessFlatBatchOutputBuilder().setBatchFailure(new ArrayList<>()).build())
223                                .buildFuture();
224     }
225
226 }