/*
* Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.openflowplugin.impl.util;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import org.opendaylight.openflowplugin.impl.services.batch.BatchPlanStep;
import org.opendaylight.openflowplugin.impl.services.batch.BatchStepType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.ProcessFlatBatchOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.ProcessFlatBatchOutputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.Batch;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.BatchChoice;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchAddFlowCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchAddGroupCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchAddMeterCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchRemoveFlowCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchRemoveGroupCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchRemoveMeterCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchUpdateFlowCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchUpdateGroupCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchUpdateMeterCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.output.BatchFailure;
import org.opendaylight.yang.gen.v1.urn.opendaylight.service.batch.common.rev160322.BatchOrderGrouping;
import org.opendaylight.yangtools.yang.binding.DataContainer;
import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
/**
* Provides flat batch util methods.
*/
public final class FlatBatchUtil {
private FlatBatchUtil() {
throw new IllegalStateException("This class should not be instantiated.");
}
public static void markBarriersWhereNeeded(final List<BatchPlanStep> batchPlan) {
final EnumSet<BatchStepType> previousTypes = EnumSet.noneOf(BatchStepType.class);
BatchPlanStep previousPlanStep = null;
for (BatchPlanStep planStep : batchPlan) {
final BatchStepType type = planStep.getStepType();
if (!previousTypes.isEmpty() && decideBarrier(previousTypes, type)) {
previousPlanStep.setBarrierAfter(true);
previousTypes.clear();
}
previousTypes.add(type);
previousPlanStep = planStep;
}
}
@VisibleForTesting
static boolean decideBarrier(final EnumSet<BatchStepType> previousTypes, final BatchStepType type) {
return isFlowBarrierNeeded(previousTypes, type)
|| isGroupBarrierNeeded(previousTypes, type)
|| isMeterBarrierNeeded(previousTypes, type);
}
private static boolean isFlowBarrierNeeded(final EnumSet<BatchStepType> previousTypes, final BatchStepType type) {
return (type == BatchStepType.FLOW_ADD
|| type == BatchStepType.FLOW_UPDATE)
&& (previousTypes.contains(BatchStepType.GROUP_ADD)
|| previousTypes.contains(BatchStepType.METER_ADD));
}
private static boolean isGroupBarrierNeeded(final EnumSet<BatchStepType> previousTypes, final BatchStepType type) {
return (type == BatchStepType.GROUP_ADD
&& (previousTypes.contains(BatchStepType.GROUP_ADD)
|| previousTypes.contains(BatchStepType.GROUP_UPDATE)))
|| (type == BatchStepType.GROUP_REMOVE
&& (previousTypes.contains(BatchStepType.FLOW_REMOVE)
|| previousTypes.contains(BatchStepType.FLOW_UPDATE)
|| previousTypes.contains(BatchStepType.GROUP_REMOVE)
|| previousTypes.contains(BatchStepType.GROUP_UPDATE)));
}
private static boolean isMeterBarrierNeeded(final EnumSet<BatchStepType> previousTypes, final BatchStepType type) {
return type == BatchStepType.METER_REMOVE
&& (previousTypes.contains(BatchStepType.FLOW_REMOVE)
|| previousTypes.contains(BatchStepType.FLOW_UPDATE));
}
public static List<BatchPlanStep> assembleBatchPlan(List<Batch> batches) {
final List<BatchPlanStep> plan = new ArrayList<>();
BatchPlanStep planStep;
for (Batch batch : batches) {
final BatchStepType nextStepType = detectBatchStepType(batch.getBatchChoice());
planStep = new BatchPlanStep(nextStepType);
planStep.getTaskBag().addAll(extractBatchData(planStep.getStepType(), batch.getBatchChoice()));
if (!planStep.isEmpty()) {
plan.add(planStep);
}
}
return plan;
}
private static List<? extends BatchOrderGrouping> extractBatchData(final BatchStepType batchStepType,
final BatchChoice batchChoice) {
final List<? extends BatchOrderGrouping> batchData;
switch (batchStepType) {
case FLOW_ADD:
batchData = ((FlatBatchAddFlowCase) batchChoice).getFlatBatchAddFlow();
break;
case FLOW_REMOVE:
batchData = ((FlatBatchRemoveFlowCase) batchChoice).getFlatBatchRemoveFlow();
break;
case FLOW_UPDATE:
batchData = ((FlatBatchUpdateFlowCase) batchChoice).getFlatBatchUpdateFlow();
break;
case GROUP_ADD:
batchData = ((FlatBatchAddGroupCase) batchChoice).getFlatBatchAddGroup();
break;
case GROUP_REMOVE:
batchData = ((FlatBatchRemoveGroupCase) batchChoice).getFlatBatchRemoveGroup();
break;
case GROUP_UPDATE:
batchData = ((FlatBatchUpdateGroupCase) batchChoice).getFlatBatchUpdateGroup();
break;
case METER_ADD:
batchData = ((FlatBatchAddMeterCase) batchChoice).getFlatBatchAddMeter();
break;
case METER_REMOVE:
batchData = ((FlatBatchRemoveMeterCase) batchChoice).getFlatBatchRemoveMeter();
break;
case METER_UPDATE:
batchData = ((FlatBatchUpdateMeterCase) batchChoice).getFlatBatchUpdateMeter();
break;
default:
throw new IllegalArgumentException("Unsupported batch step type obtained: " + batchStepType);
}
return batchData;
}
@VisibleForTesting
static <T extends BatchChoice> BatchStepType detectBatchStepType(final T batchCase) {
final BatchStepType type;
final Class<? extends DataContainer> implementedInterface = batchCase.getImplementedInterface();
if (FlatBatchAddFlowCase.class.equals(implementedInterface)) {
type = BatchStepType.FLOW_ADD;
} else if (FlatBatchRemoveFlowCase.class.equals(implementedInterface)) {
type = BatchStepType.FLOW_REMOVE;
} else if (FlatBatchUpdateFlowCase.class.equals(implementedInterface)) {
type = BatchStepType.FLOW_UPDATE;
} else if (FlatBatchAddGroupCase.class.equals(implementedInterface)) {
type = BatchStepType.GROUP_ADD;
} else if (FlatBatchRemoveGroupCase.class.equals(implementedInterface)) {
type = BatchStepType.GROUP_REMOVE;
} else if (FlatBatchUpdateGroupCase.class.equals(implementedInterface)) {
type = BatchStepType.GROUP_UPDATE;
} else if (FlatBatchAddMeterCase.class.equals(implementedInterface)) {
type = BatchStepType.METER_ADD;
} else if (FlatBatchRemoveMeterCase.class.equals(implementedInterface)) {
type = BatchStepType.METER_REMOVE;
} else if (FlatBatchUpdateMeterCase.class.equals(implementedInterface)) {
type = BatchStepType.METER_UPDATE;
} else {
throw new IllegalArgumentException("Unsupported batch obtained: " + implementedInterface);
}
return type;
}
@VisibleForTesting
static Function<List<RpcResult<ProcessFlatBatchOutput>>, RpcResult<ProcessFlatBatchOutput>> mergeRpcResults() {
return jobsResults -> {
boolean isSuccessful = true;
List<RpcError> rpcErrors = new ArrayList<>();
List<BatchFailure> batchFailures = new ArrayList<>();
for (RpcResult<ProcessFlatBatchOutput> jobResult : jobsResults) {
if (jobResult != null) {
isSuccessful = (isSuccessful && jobResult.isSuccessful());
rpcErrors.addAll(jobResult.getErrors());
batchFailures.addAll(jobResult.getResult().getBatchFailure());
}
}
return RpcResultBuilder.<ProcessFlatBatchOutput>status(isSuccessful)
.withRpcErrors(rpcErrors)
.withResult(new ProcessFlatBatchOutputBuilder().setBatchFailure(batchFailures).build())
.build();
};
}
/**
* Merge list of Futures with partial results into one ListenableFuture with single result.
* @param firedJobs list of ListenableFutures with RPC results {@link ProcessFlatBatchOutput}
* @return ListenableFuture of RPC result with combined status and all errors + batch failures
*/
public static ListenableFuture<RpcResult<ProcessFlatBatchOutput>> mergeJobsResultsFutures(
final List<ListenableFuture<RpcResult<ProcessFlatBatchOutput>>> firedJobs) {
return Futures.transform(Futures.successfulAsList(firedJobs), mergeRpcResults());
}
/**
* Creates empty result future for flat batch service.
* @param status RPC result status
* @return ListenableFuture of RPC result with empty list of errors and batch failures
*/
public static ListenableFuture<RpcResult<ProcessFlatBatchOutput>> createEmptyRpcBatchResultFuture(final boolean status) {
return RpcResultBuilder.<ProcessFlatBatchOutput>status(status)
.withRpcErrors(new ArrayList<>())
.withResult(new ProcessFlatBatchOutputBuilder().setBatchFailure(new ArrayList<>()).build())
.buildFuture();
}
}