--- /dev/null
+/**
+ * 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.applications.frsync;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import org.opendaylight.openflowplugin.applications.frsync.impl.strategy.SynchronizationDiffInput;
+import org.opendaylight.openflowplugin.applications.frsync.util.SyncCrudCounters;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+/**
+ * Prescribes common synchronization plan execution strategy.
+ * Implementations should be stateless.
+ */
+public interface SyncPlanPushStrategy {
+
+ /**
+ * @param resultVehicle bootstrap future - execution will chain it's async calls to this one
+ * @param diffInput wraps all diff data required for any strategy ({add,remove,update} x {flow,group,meter})
+ * @param counters reference to internal one-shot statistics - summary off successfully pushed items shall be recorded here
+ * @return last future of the chain
+ */
+ ListenableFuture<RpcResult<Void>> executeSyncStrategy(ListenableFuture<RpcResult<Void>> resultVehicle,
+ SynchronizationDiffInput diffInput,
+ SyncCrudCounters counters);
+}
package org.opendaylight.openflowplugin.applications.frsync.impl;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
-
import javax.annotation.Nonnull;
-
import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Optional;
-import com.google.common.util.concurrent.ListenableFuture;
-
/**
* Abstract Listener for node changes.
*/
DataTreeModification<T> modification) throws ReadFailedException, InterruptedException;
public abstract LogicalDatastoreType dsType();
-
+
static String threadName() {
final Thread currentThread = Thread.currentThread();
return currentThread.getName();
import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
import org.opendaylight.openflowplugin.applications.frsync.NodeListener;
+import org.opendaylight.openflowplugin.applications.frsync.SyncPlanPushStrategy;
import org.opendaylight.openflowplugin.applications.frsync.SyncReactor;
import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeCachedDao;
import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeDao;
import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeOdlDao;
import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeSnapshotDao;
+import org.opendaylight.openflowplugin.applications.frsync.impl.strategy.SyncPlanPushStrategyIncrementalImpl;
import org.opendaylight.openflowplugin.applications.frsync.util.SemaphoreKeeperGuavaImpl;
import org.opendaylight.openflowplugin.common.wait.SimpleTaskRetryLooper;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
final TableForwarder tableForwarder = new TableForwarder(salTableService);
{
- final SyncReactorImpl syncReactorImpl = new SyncReactorImpl();
- final SyncReactor syncReactorGuard = new SyncReactorGuardDecorator(syncReactorImpl
+ //TODO: make is switchable
+ final SyncPlanPushStrategy syncPlanPushStrategy = new SyncPlanPushStrategyIncrementalImpl()
.setFlowForwarder(flowForwarder)
.setGroupForwarder(groupForwarder)
.setMeterForwarder(meterForwarder)
- .setTableForwarder(tableForwarder)
- .setTransactionService(transactionService),
+ .setTableForwarder(tableForwarder)
+ .setTransactionService(transactionService);
+
+ final SyncReactorImpl syncReactorImpl = new SyncReactorImpl(syncPlanPushStrategy);
+ final SyncReactor syncReactorGuard = new SyncReactorGuardDecorator(syncReactorImpl,
new SemaphoreKeeperGuavaImpl<InstanceIdentifier<FlowCapableNode>>(1, true));
final SyncReactor cfgReactor = new SyncReactorFutureWithCompressionDecorator(syncReactorGuard, syncThreadPool);
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
-import com.google.common.base.MoreObjects;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import com.google.common.util.concurrent.AsyncFunction;
-import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.JdkFutureAdapters;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
-import javax.annotation.Nullable;
+import org.opendaylight.openflowplugin.applications.frsync.SyncPlanPushStrategy;
import org.opendaylight.openflowplugin.applications.frsync.SyncReactor;
-import org.opendaylight.openflowplugin.applications.frsync.markandsweep.SwitchFlowId;
+import org.opendaylight.openflowplugin.applications.frsync.impl.strategy.SynchronizationDiffInput;
import org.opendaylight.openflowplugin.applications.frsync.util.CrudCounts;
import org.opendaylight.openflowplugin.applications.frsync.util.FlowCapableNodeLookups;
+import org.opendaylight.openflowplugin.applications.frsync.util.FxChainUtil;
import org.opendaylight.openflowplugin.applications.frsync.util.ItemSyncBox;
import org.opendaylight.openflowplugin.applications.frsync.util.PathUtil;
import org.opendaylight.openflowplugin.applications.frsync.util.ReconcileUtil;
import org.opendaylight.openflowplugin.applications.frsync.util.SyncCrudCounters;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.MeterKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowOutput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowOutput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.FlowCapableTransactionService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupOutput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.RemoveGroupOutput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.UpdateGroupOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.AddMeterOutput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.RemoveMeterOutput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.UpdateMeterOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.table.service.rev131026.UpdateTableOutput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeatures;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeaturesKey;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
-import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.slf4j.Logger;
public class SyncReactorImpl implements SyncReactor {
private static final Logger LOG = LoggerFactory.getLogger(SyncReactorImpl.class);
+ private final SyncPlanPushStrategy syncPlanPushStrategy;
- private FlowForwarder flowForwarder;
- private TableForwarder tableForwarder;
- private MeterForwarder meterForwarder;
- private GroupForwarder groupForwarder;
- private FlowCapableTransactionService transactionService;
+ public SyncReactorImpl(SyncPlanPushStrategy syncPlanPushStrategy) {
+ this.syncPlanPushStrategy = Preconditions.checkNotNull(syncPlanPushStrategy, "execution strategy is mandatory");
+ }
@Override
public ListenableFuture<Boolean> syncup(final InstanceIdentifier<FlowCapableNode> nodeIdent,
final SyncCrudCounters counters = new SyncCrudCounters();
/**
* instructions:
- * - extract diff changes and preapare change steps in safe order
+ * - extract diff changes and prepare change steps in safe order
* - optimization: decide if updates needed
* - execute chosen implementation (e.g. conventional API, bulk API, flat bulk API)
- * -
- * reconciliation strategy - phase 1: - add/update missing objects in following order -
- * table features - groups (reordered) - meters - flows
+ * - recommended order follows:
+ * reconciliation strategy - phase 1: - add/update missing objects in following order:
+ * - table features - groups (reordered) - meters - flows
+ *
+ * reconciliation strategy - phase 2: - remove redundant objects in following order:
+ * - flows - meters - groups (reordered)
**/
- ListenableFuture<RpcResult<Void>> resultVehicle = RpcResultBuilder.<Void>success().buildFuture();
+
final NodeId nodeId = PathUtil.digNodeId(nodeIdent);
- /* Tables - have to be pushed before groups */
- // TODO enable table-update when ready
- //resultVehicle = updateTableFeatures(nodeIdent, configTree);
+ final List<ItemSyncBox<Group>> groupsToAddOrUpdate = extractGroupsToAddOrUpdate(nodeId, configTree, operationalTree);
+ final ItemSyncBox<Meter> metersToAddOrUpdate = extractMetersToAddOrUpdate(nodeId, configTree, operationalTree);
+ final Map<TableKey, ItemSyncBox<Flow>> flowsToAddOrUpdate = extractFlowsToAddOrUpdate(nodeId, configTree, operationalTree);
- resultVehicle = Futures.transform(resultVehicle, new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
- @Override
- public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input) throws Exception {
- if (!input.isSuccessful()) {
- //TODO chain errors but not skip processing on first error return Futures.immediateFuture(input);
- //final ListenableFuture<RpcResult<Void>> singleVoidUpdateResult = Futures.transform(
- // Futures.asList Arrays.asList(input, output),
- // ReconcileUtil.<UpdateFlowOutput>createRpcResultCondenser("TODO"));
- }
- return addMissingGroups(nodeIdent, configTree, operationalTree, counters);
- }
- });
- Futures.addCallback(resultVehicle, logResultCallback(nodeId, "addMissingGroups"));
- resultVehicle = Futures.transform(resultVehicle, new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
- @Override
- public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input) throws Exception {
- if (!input.isSuccessful()) {
- //TODO chain errors but not skip processing on first error return Futures.immediateFuture(input);
- }
- return addMissingMeters(nodeIdent, configTree, operationalTree, counters);
- }
- });
- Futures.addCallback(resultVehicle, logResultCallback(nodeId, "addMissingMeters"));
- resultVehicle = Futures.transform(resultVehicle, new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
- @Override
- public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input) throws Exception {
- if (!input.isSuccessful()) {
- //TODO chain errors but not skip processing on first error return Futures.immediateFuture(input);
- }
- return addMissingFlows(nodeIdent, configTree, operationalTree, counters);
- }
- });
- Futures.addCallback(resultVehicle, logResultCallback(nodeId, "addMissingFlows"));
+ final Map<TableKey, ItemSyncBox<Flow>> flowsToRemove = extractFlowsToRemove(nodeId, configTree, operationalTree);
+ final ItemSyncBox<Meter> metersToRemove = extractMetersToRemove(nodeId, configTree, operationalTree);
+ final List<ItemSyncBox<Group>> groupsToRemove = extractGroupsToRemove(nodeId, configTree, operationalTree);
- /**
- * reconciliation strategy - phase 2: - remove redundand objects in following order - flows
- * - meters - groups (reordered)
- **/
- resultVehicle = Futures.transform(resultVehicle, new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
- @Override
- public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input) throws Exception {
- if (!input.isSuccessful()) {
- //TODO chain errors but not skip processing on first error return Futures.immediateFuture(input);
- }
- return removeRedundantFlows(nodeIdent, configTree, operationalTree, counters);
- }
- });
- Futures.addCallback(resultVehicle, logResultCallback(nodeId, "removeRedundantFlows"));
- resultVehicle = Futures.transform(resultVehicle, new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
- @Override
- public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input) throws Exception {
- if (!input.isSuccessful()) {
- //TODO chain errors but not skip processing on first error return Futures.immediateFuture(input);
- }
- return removeRedundantMeters(nodeIdent, configTree, operationalTree, counters);
- }
- });
- Futures.addCallback(resultVehicle, logResultCallback(nodeId, "removeRedundantMeters"));
- resultVehicle = Futures.transform(resultVehicle, new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
- @Override
- public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input) throws Exception {
- if (!input.isSuccessful()) {
- //TODO chain errors but not skip processing on first error return Futures.immediateFuture(input);
- }
- return removeRedundantGroups(nodeIdent, configTree, operationalTree, counters);
- }
- });
- Futures.addCallback(resultVehicle, logResultCallback(nodeId, "removeRedundantGroups"));
+ final SynchronizationDiffInput input = new SynchronizationDiffInput(nodeIdent,
+ groupsToAddOrUpdate, metersToAddOrUpdate, flowsToAddOrUpdate,
+ flowsToRemove, metersToRemove, groupsToRemove);
+
+ final ListenableFuture<RpcResult<Void>> bootstrapResultFuture = RpcResultBuilder.<Void>success().buildFuture();
+ final ListenableFuture<RpcResult<Void>> resultVehicle = syncPlanPushStrategy.executeSyncStrategy(
+ bootstrapResultFuture, input, counters);
// log final result
- Futures.addCallback(resultVehicle, logResultCallback(nodeId, "final result"));
+ Futures.addCallback(resultVehicle, FxChainUtil.logResultCallback(nodeId, "final result"));
return Futures.transform(resultVehicle, new Function<RpcResult<Void>, Boolean>() {
@Override
});
}
- private FutureCallback<RpcResult<Void>> logResultCallback(final NodeId nodeId, final String prefix) {
- return new FutureCallback<RpcResult<Void>>() {
- @Override
- public void onSuccess(@Nullable final RpcResult<Void> result) {
- if (result != null) {
- if (result.isSuccessful()) {
- LOG.debug(prefix + " finished successfully: {}", nodeId.getValue());
- } else {
- final Collection<RpcError> errors = MoreObjects.firstNonNull(result.getErrors(), ImmutableList.<RpcError>of());
- LOG.debug(prefix + " failed: {} -> {}", nodeId.getValue(), Arrays.toString(errors.toArray()));
- }
- } else {
- LOG.debug(prefix + "reconciliation failed: {} -> null result", nodeId.getValue());
- }
- }
-
- @Override
- public void onFailure(final Throwable t) {
- LOG.debug(prefix + "reconciliation failed seriously: {}", nodeId.getValue(), t);
- }
- };
- }
-
- public SyncReactorImpl setFlowForwarder(final FlowForwarder flowForwarder) {
- this.flowForwarder = flowForwarder;
- return this;
- }
-
- public SyncReactorImpl setTableForwarder(final TableForwarder tableForwarder) {
- this.tableForwarder = tableForwarder;
- return this;
- }
-
- public SyncReactorImpl setMeterForwarder(final MeterForwarder meterForwarder) {
- this.meterForwarder = meterForwarder;
- return this;
- }
-
- public SyncReactorImpl setGroupForwarder(final GroupForwarder groupForwarder) {
- this.groupForwarder = groupForwarder;
- return this;
- }
-
- public SyncReactorImpl setTransactionService(final FlowCapableTransactionService transactionService) {
- this.transactionService = transactionService;
- return this;
- }
-
- ListenableFuture<RpcResult<Void>> updateTableFeatures(final InstanceIdentifier<FlowCapableNode> nodeIdent,
- final FlowCapableNode flowCapableNodeConfigured) {
- // CHECK if while pushing the update, updateTableInput can be null to emulate a table add
- final List<Table> tableList = safeTables(flowCapableNodeConfigured);
-
- final List<ListenableFuture<RpcResult<UpdateTableOutput>>> allResults = new ArrayList<>();
- for (Table table : tableList) {
- TableKey tableKey = table.getKey();
- KeyedInstanceIdentifier<TableFeatures, TableFeaturesKey> tableFeaturesII = nodeIdent
- .child(TableFeatures.class, new TableFeaturesKey(tableKey.getId()));
- List<TableFeatures> tableFeatures = flowCapableNodeConfigured.getTableFeatures();
- if (tableFeatures != null) {
- for (TableFeatures tableFeaturesItem : tableFeatures) {
- // TODO uncomment java.lang.NullPointerException
- // at
- // org.opendaylight.openflowjava.protocol.impl.serialization.match.AbstractOxmMatchEntrySerializer.serializeHeader(AbstractOxmMatchEntrySerializer.java:31
- // allResults.add(JdkFutureAdapters.listenInPoolThread(
- // tableForwarder.update(tableFeaturesII, null, tableFeaturesItem, nodeIdent)));
- }
- }
- }
-
- final ListenableFuture<RpcResult<Void>> singleVoidResult = Futures.transform(
- Futures.allAsList(allResults),
- ReconcileUtil.<UpdateTableOutput>createRpcResultCondenser("table update"));
-
- return Futures.transform(singleVoidResult,
- ReconcileUtil.chainBarrierFlush(PathUtil.digNodePath(nodeIdent), transactionService));
- }
-
-
@VisibleForTesting
- ListenableFuture<RpcResult<Void>> addMissingGroups(final InstanceIdentifier<FlowCapableNode> nodeIdent,
- final FlowCapableNode flowCapableNodeConfigured,
- final FlowCapableNode flowCapableNodeOperational,
- final SyncCrudCounters counters) {
- final NodeId nodeId = PathUtil.digNodeId(nodeIdent);
- final List<Group> groupsConfigured = safeGroups(flowCapableNodeConfigured);
- if (groupsConfigured.isEmpty()) {
- LOG.trace("no groups configured for node: {} -> SKIPPING", nodeId.getValue());
- return RpcResultBuilder.<Void>success().buildFuture();
- }
-
- final List<Group> groupsOperational = safeGroups(flowCapableNodeOperational);
-
- return addMissingGroups(nodeId, nodeIdent, groupsConfigured, groupsOperational, counters);
- }
-
- protected ListenableFuture<RpcResult<Void>> addMissingGroups(NodeId nodeId,
- final InstanceIdentifier<FlowCapableNode> nodeIdent,
- final List<Group> groupsConfigured,
- final List<Group> groupsOperational,
- final SyncCrudCounters counters) {
-
+ static List<ItemSyncBox<Group>> extractGroupsToAddOrUpdate(final NodeId nodeId,
+ final FlowCapableNode flowCapableNodeConfigured,
+ final FlowCapableNode flowCapableNodeOperational) {
+ final List<Group> groupsConfigured = ReconcileUtil.safeGroups(flowCapableNodeConfigured);
+ final List<Group> groupsOperational = ReconcileUtil.safeGroups(flowCapableNodeOperational);
final Map<Long, Group> groupOperationalMap = FlowCapableNodeLookups.wrapGroupsToMap(groupsOperational);
final List<Group> pendingGroups = new ArrayList<>();
pendingGroups.addAll(groupsConfigured);
- ListenableFuture<RpcResult<Void>> chainedResult;
- try {
- final List<ItemSyncBox<Group>> groupsAddPlan =
- ReconcileUtil.resolveAndDivideGroups(nodeId, groupOperationalMap, pendingGroups);
- if (!groupsAddPlan.isEmpty()) {
- final CrudCounts groupCrudCounts = counters.getGroupCrudCounts();
- groupCrudCounts.setAdded(ReconcileUtil.countTotalAdds(groupsAddPlan));
- groupCrudCounts.setUpdated(ReconcileUtil.countTotalUpdated(groupsAddPlan));
-
- if (LOG.isDebugEnabled()) {
- LOG.debug("adding groups: inputGroups={}, planSteps={}, toAddTotal={}, toUpdateTotal={}",
- pendingGroups.size(), groupsAddPlan.size(),
- groupCrudCounts.getAdded(),
- groupCrudCounts.getUpdated());
- }
-
- chainedResult = flushAddGroupPortionAndBarrier(nodeIdent, groupsAddPlan.get(0));
- for (final ItemSyncBox<Group> groupsPortion : Iterables.skip(groupsAddPlan, 1)) {
- chainedResult =
- Futures.transform(chainedResult, new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
- @Override
- public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input)
- throws Exception {
- final ListenableFuture<RpcResult<Void>> result;
- if (input.isSuccessful()) {
- result = flushAddGroupPortionAndBarrier(nodeIdent, groupsPortion);
- } else {
- // pass through original unsuccessful rpcResult
- result = Futures.immediateFuture(input);
- }
-
- return result;
- }
- });
- }
- } else {
- chainedResult = RpcResultBuilder.<Void>success().buildFuture();
- }
- } catch (IllegalStateException e) {
- chainedResult = RpcResultBuilder.<Void>failed()
- .withError(RpcError.ErrorType.APPLICATION, "failed to add missing groups", e)
- .buildFuture();
- }
-
- return chainedResult;
- }
-
- private ListenableFuture<RpcResult<Void>> flushAddGroupPortionAndBarrier(
- final InstanceIdentifier<FlowCapableNode> nodeIdent,
- final ItemSyncBox<Group> groupsPortion) {
- final List<ListenableFuture<RpcResult<AddGroupOutput>>> allResults = new ArrayList<>();
- final List<ListenableFuture<RpcResult<UpdateGroupOutput>>> allUpdateResults = new ArrayList<>();
-
- for (Group group : groupsPortion.getItemsToAdd()) {
- final KeyedInstanceIdentifier<Group, GroupKey> groupIdent = nodeIdent.child(Group.class, group.getKey());
- allResults.add(JdkFutureAdapters.listenInPoolThread(groupForwarder.add(groupIdent, group, nodeIdent)));
-
- }
-
- for (ItemSyncBox.ItemUpdateTuple<Group> groupTuple : groupsPortion.getItemsToUpdate()) {
- final Group existingGroup = groupTuple.getOriginal();
- final Group group = groupTuple.getUpdated();
-
- final KeyedInstanceIdentifier<Group, GroupKey> groupIdent = nodeIdent.child(Group.class, group.getKey());
- allUpdateResults.add(JdkFutureAdapters.listenInPoolThread(
- groupForwarder.update(groupIdent, existingGroup, group, nodeIdent)));
- }
-
- final ListenableFuture<RpcResult<Void>> singleVoidAddResult = Futures.transform(
- Futures.allAsList(allResults), ReconcileUtil.<AddGroupOutput>createRpcResultCondenser("group add"));
-
- final ListenableFuture<RpcResult<Void>> singleVoidUpdateResult = Futures.transform(
- Futures.allAsList(allUpdateResults),
- ReconcileUtil.<UpdateGroupOutput>createRpcResultCondenser("group update"));
-
- final ListenableFuture<RpcResult<Void>> summaryResult = Futures.transform(
- Futures.allAsList(singleVoidAddResult, singleVoidUpdateResult),
- ReconcileUtil.<Void>createRpcResultCondenser("group add/update"));
-
-
- return Futures.transform(summaryResult,
- ReconcileUtil.chainBarrierFlush(
- PathUtil.digNodePath(nodeIdent), transactionService));
+ return ReconcileUtil.resolveAndDivideGroupDiffs(nodeId, groupOperationalMap, pendingGroups, true);
}
- ListenableFuture<RpcResult<Void>> addMissingMeters(final InstanceIdentifier<FlowCapableNode> nodeIdent,
- final FlowCapableNode flowCapableNodeConfigured,
- final FlowCapableNode flowCapableNodeOperational,
- final SyncCrudCounters counters) {
- final NodeId nodeId = PathUtil.digNodeId(nodeIdent);
- final List<Meter> metersConfigured = safeMeters(flowCapableNodeConfigured);
- if (metersConfigured.isEmpty()) {
- LOG.trace("no meters configured for node: {} -> SKIPPING", nodeId.getValue());
- return RpcResultBuilder.<Void>success().buildFuture();
- }
-
- final List<Meter> metersOperational = safeMeters(flowCapableNodeOperational);
-
- return addMissingMeters(nodeId, nodeIdent, metersConfigured, metersOperational, counters);
- }
-
-
- protected ListenableFuture<RpcResult<Void>> addMissingMeters(NodeId nodeId,
- final InstanceIdentifier<FlowCapableNode> nodeIdent,
- List<Meter> metersConfigured,
- List<Meter> metersOperational,
- final SyncCrudCounters counters) {
-
+ @VisibleForTesting
+ static ItemSyncBox<Meter> extractMetersToAddOrUpdate(final NodeId nodeId,
+ final FlowCapableNode flowCapableNodeConfigured,
+ final FlowCapableNode flowCapableNodeOperational) {
+ final List<Meter> metersConfigured = ReconcileUtil.safeMeters(flowCapableNodeConfigured);
+ final List<Meter> metersOperational = ReconcileUtil.safeMeters(flowCapableNodeOperational);
final Map<MeterId, Meter> meterOperationalMap = FlowCapableNodeLookups.wrapMetersToMap(metersOperational);
- final CrudCounts meterCrudCounts = counters.getMeterCrudCounts();
-
- final List<ListenableFuture<RpcResult<AddMeterOutput>>> allResults = new ArrayList<>();
- final List<ListenableFuture<RpcResult<UpdateMeterOutput>>> allUpdateResults = new ArrayList<>();
- for (Meter meter : metersConfigured) {
- final Meter existingMeter = meterOperationalMap.get(meter.getMeterId());
- final KeyedInstanceIdentifier<Meter, MeterKey> meterIdent = nodeIdent.child(Meter.class, meter.getKey());
-
- if (existingMeter == null) {
- LOG.debug("adding meter {} - absent on device {}",
- meter.getMeterId(), nodeId);
- allResults.add(JdkFutureAdapters.listenInPoolThread(
- meterForwarder.add(meterIdent, meter, nodeIdent)));
- meterCrudCounts.incAdded();
- } else {
- // compare content and eventually update
- LOG.trace("meter {} - already present on device {} .. comparing", meter.getMeterId(), nodeId);
- if (!meter.equals(existingMeter)) {
- LOG.trace("meter {} - needs update on device {}", meter.getMeterId(), nodeId);
- allUpdateResults.add(JdkFutureAdapters.listenInPoolThread(
- meterForwarder.update(meterIdent, existingMeter, meter, nodeIdent)));
- meterCrudCounts.incUpdated();
- } else {
- LOG.trace("meter {} - on device {} is equal to the configured one", meter.getMeterId(), nodeId);
- }
- }
- }
-
- final ListenableFuture<RpcResult<Void>> singleVoidAddResult = Futures.transform(
- Futures.allAsList(allResults), ReconcileUtil.<AddMeterOutput>createRpcResultCondenser("meter add"));
-
- final ListenableFuture<RpcResult<Void>> singleVoidUpdateResult = Futures.transform(
- Futures.allAsList(allUpdateResults),
- ReconcileUtil.<UpdateMeterOutput>createRpcResultCondenser("meter update"));
- final ListenableFuture<RpcResult<Void>> summaryResults = Futures.transform(
- Futures.allAsList(singleVoidUpdateResult, singleVoidAddResult),
- ReconcileUtil.<Void>createRpcResultCondenser("meter add/update"));
-
- return summaryResults;
-
- /*
- return Futures.transform(summaryResults,
- ReconcileUtil.chainBarrierFlush(PathUtil.digNodePath(nodeIdent), transactionService));
- */
+ return ReconcileUtil.resolveMeterDiffs(nodeId, meterOperationalMap, metersConfigured, true);
}
@VisibleForTesting
- ListenableFuture<RpcResult<Void>> addMissingFlows(final InstanceIdentifier<FlowCapableNode> nodeIdent,
- final FlowCapableNode flowCapableNodeConfigured,
- final FlowCapableNode flowCapableNodeOperational,
- final SyncCrudCounters counters) {
- final NodeId nodeId = PathUtil.digNodeId(nodeIdent);
- final List<Table> tablesConfigured = safeTables(flowCapableNodeConfigured);
+ static Map<TableKey, ItemSyncBox<Flow>> extractFlowsToAddOrUpdate(final NodeId nodeId,
+ final FlowCapableNode flowCapableNodeConfigured,
+ final FlowCapableNode flowCapableNodeOperational) {
+ final List<Table> tablesConfigured = ReconcileUtil.safeTables(flowCapableNodeConfigured);
if (tablesConfigured.isEmpty()) {
- LOG.trace("no tables in config for node: {} -> SKIPPING", nodeId.getValue());
- return RpcResultBuilder.<Void>success().buildFuture();
+ return Collections.emptyMap();
}
- final List<Table> tablesOperational = safeTables(flowCapableNodeOperational);
-
- return addMissingFlows(nodeId, nodeIdent, tablesConfigured, tablesOperational, counters);
- }
-
- protected ListenableFuture<RpcResult<Void>> addMissingFlows(NodeId nodeId,
- final InstanceIdentifier<FlowCapableNode> nodeIdent,
- List<Table> tablesConfigured, List<Table> tablesOperational,
- final SyncCrudCounters counters) {
-
+ final List<Table> tablesOperational = ReconcileUtil.safeTables(flowCapableNodeOperational);
final Map<Short, Table> tableOperationalMap = FlowCapableNodeLookups.wrapTablesToMap(tablesOperational);
- final List<ListenableFuture<RpcResult<AddFlowOutput>>> allResults = new ArrayList<>();
- final List<ListenableFuture<RpcResult<UpdateFlowOutput>>> allUpdateResults = new ArrayList<>();
- final CrudCounts flowCrudCounts = counters.getFlowCrudCounts();
-
- for (final Table tableConfigured : tablesConfigured) {
- final List<Flow> flowsConfigured = tableConfigured.getFlow();
- if (flowsConfigured == null || flowsConfigured.isEmpty()) {
- continue;
- }
-
- final KeyedInstanceIdentifier<Table, TableKey> tableIdent =
- nodeIdent.child(Table.class, tableConfigured.getKey());
-
- // lookup table (on device)
- final Table tableOperational = tableOperationalMap.get(tableConfigured.getId());
- // wrap existing (on device) flows in current table into map
- final Map<SwitchFlowId, Flow> flowOperationalMap = FlowCapableNodeLookups.wrapFlowsToMap(
- tableOperational != null
- ? tableOperational.getFlow()
- : null);
-
-
- // loop configured flows and check if already present on device
- for (final Flow flow : flowsConfigured) {
- final Flow existingFlow = FlowCapableNodeLookups.flowMapLookupExisting(flow, flowOperationalMap);
- final KeyedInstanceIdentifier<Flow, FlowKey> flowIdent = tableIdent.child(Flow.class, flow.getKey());
-
- if (existingFlow == null) {
- LOG.debug("adding flow {} in table {} - absent on device {} match{}",
- flow.getId(), tableConfigured.getKey(), nodeId, flow.getMatch());
-
- allResults.add(JdkFutureAdapters.listenInPoolThread(
- flowForwarder.add(flowIdent, flow, nodeIdent)));
- flowCrudCounts.incAdded();
- } else {
- LOG.trace("flow {} in table {} - already present on device {} .. comparing match{}",
- flow.getId(), tableConfigured.getKey(), nodeId, flow.getMatch());
- // check instructions and eventually update
- if (!Objects.equals(flow.getInstructions(), existingFlow.getInstructions())) {
- LOG.trace("flow {} in table {} - needs update on device {} match{}",
- flow.getId(), tableConfigured.getKey(), nodeId, flow.getMatch());
- allUpdateResults.add(JdkFutureAdapters.listenInPoolThread(
- flowForwarder.update(flowIdent, existingFlow, flow, nodeIdent)));
- flowCrudCounts.incUpdated();
- } else {
- LOG.trace("flow {} in table {} - is equal to configured one on device {} match{}",
- flow.getId(), tableConfigured.getKey(), nodeId, flow.getMatch());
- }
- }
- }
- }
-
- final ListenableFuture<RpcResult<Void>> singleVoidAddResult = Futures.transform(
- Futures.allAsList(allResults),
- ReconcileUtil.<AddFlowOutput>createRpcResultCondenser("flow adding"));
-
- final ListenableFuture<RpcResult<Void>> singleVoidUpdateResult = Futures.transform(
- Futures.allAsList(allUpdateResults),
- ReconcileUtil.<UpdateFlowOutput>createRpcResultCondenser("flow updating"));
-
- final ListenableFuture<RpcResult<Void>> summaryResult = Futures.transform(
- Futures.allAsList(singleVoidAddResult, singleVoidUpdateResult),
- ReconcileUtil.<Void>createRpcResultCondenser("flow add/update"));
- return summaryResult;
-
- /*
- return Futures.transform(summaryResult,
- ReconcileUtil.chainBarrierFlush(PathUtil.digNodePath(nodeIdent), transactionService));
- */
+ return ReconcileUtil.resolveFlowDiffsInAllTables(nodeId, tableOperationalMap, tablesConfigured, true);
}
@VisibleForTesting
- ListenableFuture<RpcResult<Void>> removeRedundantFlows(
- final InstanceIdentifier<FlowCapableNode> nodeIdent,
- final FlowCapableNode flowCapableNodeConfigured,
- final FlowCapableNode flowCapableNodeOperational,
- final SyncCrudCounters counters) {
- final NodeId nodeId = PathUtil.digNodeId(nodeIdent);
- final List<Table> tablesOperational = safeTables(flowCapableNodeOperational);
-
+ static Map<TableKey, ItemSyncBox<Flow>> extractFlowsToRemove(final NodeId nodeId,
+ final FlowCapableNode flowCapableNodeConfigured,
+ final FlowCapableNode flowCapableNodeOperational) {
+ final List<Table> tablesOperational = ReconcileUtil.safeTables(flowCapableNodeOperational);
if (tablesOperational.isEmpty()) {
- LOG.trace("no tables in operational for node: {} -> SKIPPING", nodeId.getValue());
- return RpcResultBuilder.<Void>success().buildFuture();
- }
-
- final List<Table> tablesConfigured = safeTables(flowCapableNodeConfigured);
-
- return removeRedundantFlows(nodeId, nodeIdent, tablesConfigured, tablesOperational, counters);
- }
-
- protected ListenableFuture<RpcResult<Void>> removeRedundantFlows(
- NodeId nodeId, final InstanceIdentifier<FlowCapableNode> nodeIdent,
- final List<Table> tablesConfigured, final List<Table> tablesOperational, final SyncCrudCounters counters) {
- final Map<Short, Table> tableConfigMap = FlowCapableNodeLookups.wrapTablesToMap(tablesConfigured);
- final List<ListenableFuture<RpcResult<RemoveFlowOutput>>> allResults = new ArrayList<>();
- final CrudCounts flowCrudCounts = counters.getFlowCrudCounts();
-
- for (final Table tableOperational : tablesOperational) {
- final List<Flow> flowsOperational = tableOperational.getFlow();
- if (flowsOperational == null || flowsOperational.isEmpty()) {
- continue;
- }
-
- final KeyedInstanceIdentifier<Table, TableKey> tableIdent =
- nodeIdent.child(Table.class, tableOperational.getKey());
-
- // lookup configured table
- final Table tableConfig = tableConfigMap.get(tableOperational.getId());
- // wrap configured flows in current table into map
- final Map<SwitchFlowId, Flow> flowConfigMap = FlowCapableNodeLookups.wrapFlowsToMap(
- tableConfig != null
- ? tableConfig.getFlow()
- : null);
-
- // loop flows on device and check if the are configured
- for (final Flow flow : flowsOperational) {
- final Flow existingFlow = FlowCapableNodeLookups.flowMapLookupExisting(flow, flowConfigMap);
- if (existingFlow == null) {
- LOG.trace("removing flow {} in table {} - absent in config {}, match {}",
- flow.getId(), tableOperational.getKey(), nodeId, flow.getMatch());
-
- final KeyedInstanceIdentifier<Flow, FlowKey> flowIdent =
- tableIdent.child(Flow.class, flow.getKey());
- allResults.add(JdkFutureAdapters.listenInPoolThread(
- flowForwarder.remove(flowIdent, flow, nodeIdent)));
- flowCrudCounts.incRemoved();
- } else {
- LOG.trace("skipping flow {} in table {} - present in config {}, match {}",
- flow.getId(), tableOperational.getKey(), nodeId, flow.getMatch());
- }
- }
+ return Collections.emptyMap();
}
- final ListenableFuture<RpcResult<Void>> singleVoidResult = Futures.transform(
- Futures.allAsList(allResults), ReconcileUtil.<RemoveFlowOutput>createRpcResultCondenser("flow remove"));
- return Futures.transform(singleVoidResult,
- ReconcileUtil.chainBarrierFlush(PathUtil.digNodePath(nodeIdent), transactionService));
+ final List<Table> tablesConfigured = ReconcileUtil.safeTables(flowCapableNodeConfigured);
+ final Map<Short, Table> tableConfiguredMap = FlowCapableNodeLookups.wrapTablesToMap(tablesConfigured);
+ return ReconcileUtil.resolveFlowDiffsInAllTables(nodeId, tableConfiguredMap, tablesOperational, false);
}
@VisibleForTesting
- ListenableFuture<RpcResult<Void>> removeRedundantMeters(final InstanceIdentifier<FlowCapableNode> nodeIdent,
- final FlowCapableNode flowCapableNodeConfigured,
- final FlowCapableNode flowCapableNodeOperational,
- final SyncCrudCounters counters) {
-
- final NodeId nodeId = PathUtil.digNodeId(nodeIdent);
- final List<Meter> metersOperational = safeMeters(flowCapableNodeOperational);
- if (metersOperational.isEmpty()) {
- LOG.trace("no meters on device for node: {} -> SKIPPING", nodeId.getValue());
- return RpcResultBuilder.<Void>success().buildFuture();
- }
-
- final List<Meter> metersConfigured = safeMeters(flowCapableNodeConfigured);
+ static ItemSyncBox<Meter> extractMetersToRemove(final NodeId nodeId,
+ final FlowCapableNode flowCapableNodeConfigured,
+ final FlowCapableNode flowCapableNodeOperational) {
+ final List<Meter> metersConfigured = ReconcileUtil.safeMeters(flowCapableNodeConfigured);
+ final List<Meter> metersOperational = ReconcileUtil.safeMeters(flowCapableNodeOperational);
+ final Map<MeterId, Meter> meterConfiguredMap = FlowCapableNodeLookups.wrapMetersToMap(metersConfigured);
- return removeRedundantMeters(nodeId, nodeIdent, metersConfigured, metersOperational, counters);
- }
-
-
- protected ListenableFuture<RpcResult<Void>> removeRedundantMeters(NodeId nodeId,
- final InstanceIdentifier<FlowCapableNode> nodeIdent,
- List<Meter> metersConfigured,
- List<Meter> metersOperational,
- final SyncCrudCounters counters) {
-
- final Map<MeterId, Meter> meterConfigMap = FlowCapableNodeLookups.wrapMetersToMap(metersConfigured);
- final CrudCounts meterCrudCounts = counters.getMeterCrudCounts();
-
- final List<ListenableFuture<RpcResult<RemoveMeterOutput>>> allResults = new ArrayList<>();
- for (Meter meter : metersOperational) {
- if (!meterConfigMap.containsKey(meter.getMeterId())) {
- LOG.trace("removing meter {} - absent in config {}",
- meter.getMeterId(), nodeId);
- final KeyedInstanceIdentifier<Meter, MeterKey> meterIdent =
- nodeIdent.child(Meter.class, meter.getKey());
- allResults.add(JdkFutureAdapters.listenInPoolThread(
- meterForwarder.remove(meterIdent, meter, nodeIdent)));
- meterCrudCounts.incRemoved();
- } else {
- LOG.trace("skipping meter {} - present in config {}",
- meter.getMeterId(), nodeId);
- }
- }
-
- final ListenableFuture<RpcResult<Void>> singleVoidResult = Futures.transform(
- Futures.allAsList(allResults),
- ReconcileUtil.<RemoveMeterOutput>createRpcResultCondenser("meter remove"));
- return singleVoidResult;
- /*
- return Futures.transform(singleVoidResult,
- ReconcileUtil.chainBarrierFlush(PathUtil.digNodePath(nodeIdent), transactionService));
- */
+ return ReconcileUtil.resolveMeterDiffs(nodeId, meterConfiguredMap, metersOperational, false);
}
@VisibleForTesting
- ListenableFuture<RpcResult<Void>> removeRedundantGroups(final InstanceIdentifier<FlowCapableNode> nodeIdent,
- final FlowCapableNode flowCapableNodeConfigured,
- final FlowCapableNode flowCapableNodeOperational,
- final SyncCrudCounters counters) {
- final NodeId nodeId = PathUtil.digNodeId(nodeIdent);
- final List<Group> groupsOperational = safeGroups(flowCapableNodeOperational);
- if (groupsOperational == null || groupsOperational.isEmpty()) {
- LOG.trace("no groups on device for node: {} -> SKIPPING", nodeId.getValue());
- return RpcResultBuilder.<Void>success().buildFuture();
- }
-
- final List<Group> groupsConfigured = safeGroups(flowCapableNodeConfigured);
-
- return removeRedundantGroups(nodeId, nodeIdent, groupsConfigured, groupsOperational, counters);
- }
-
- ListenableFuture<RpcResult<Void>> removeRedundantGroups(NodeId nodeId,
- final InstanceIdentifier<FlowCapableNode> nodeIdent,
- List<Group> groupsConfigured, List<Group> groupsOperational,
- final SyncCrudCounters counters) {
-
- final Map<Long, Group> groupConfigMap = FlowCapableNodeLookups.wrapGroupsToMap(groupsConfigured);
- final CrudCounts groupCrudCounts = counters.getGroupCrudCounts();
+ static List<ItemSyncBox<Group>> extractGroupsToRemove(final NodeId nodeId,
+ final FlowCapableNode flowCapableNodeConfigured,
+ final FlowCapableNode flowCapableNodeOperational) {
+ final List<Group> groupsConfigured = ReconcileUtil.safeGroups(flowCapableNodeConfigured);
+ final List<Group> groupsOperational = ReconcileUtil.safeGroups(flowCapableNodeOperational);
+ final Map<Long, Group> groupConfiguredMap = FlowCapableNodeLookups.wrapGroupsToMap(groupsConfigured);
final List<Group> pendingGroups = new ArrayList<>();
pendingGroups.addAll(groupsOperational);
- ListenableFuture<RpcResult<Void>> chainedResult;
- try {
- final List<ItemSyncBox<Group>> groupsRemovePlan =
- ReconcileUtil.resolveAndDivideGroups(nodeId, groupConfigMap, pendingGroups, false);
- if (!groupsRemovePlan.isEmpty()) {
- groupCrudCounts.setRemoved(ReconcileUtil.countTotalAdds(groupsRemovePlan));
- if (LOG.isDebugEnabled()) {
- LOG.debug("removing groups: inputGroups={}, planSteps={}, toRemoveTotal={}",
- pendingGroups.size(), groupsRemovePlan.size(),
- groupCrudCounts.getRemoved());
- }
- Collections.reverse(groupsRemovePlan);
- chainedResult = flushRemoveGroupPortionAndBarrier(nodeIdent, groupsRemovePlan.get(0));
- for (final ItemSyncBox<Group> groupsPortion : Iterables.skip(groupsRemovePlan, 1)) {
- chainedResult =
- Futures.transform(chainedResult, new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
- @Override
- public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input)
- throws Exception {
- final ListenableFuture<RpcResult<Void>> result;
- if (input.isSuccessful()) {
- result = flushRemoveGroupPortionAndBarrier(nodeIdent, groupsPortion);
- } else {
- // pass through original unsuccessful rpcResult
- result = Futures.immediateFuture(input);
- }
-
- return result;
- }
- });
- }
- } else {
- chainedResult = RpcResultBuilder.<Void>success().buildFuture();
- }
- } catch (IllegalStateException e) {
- chainedResult = RpcResultBuilder.<Void>failed()
- .withError(RpcError.ErrorType.APPLICATION, "failed to add missing groups", e)
- .buildFuture();
- }
-
- return chainedResult;
- }
-
- static List<Group> safeGroups(FlowCapableNode node) {
- if (node == null) {
- return Collections.emptyList();
- }
-
- return MoreObjects.firstNonNull(node.getGroup(), ImmutableList.<Group>of());
- }
-
- static List<Table> safeTables(FlowCapableNode node) {
- if (node == null) {
- return Collections.emptyList();
- }
-
- return MoreObjects.firstNonNull(node.getTable(), ImmutableList.<Table>of());
- }
-
- static List<Meter> safeMeters(FlowCapableNode node) {
- if (node == null) {
- return Collections.emptyList();
- }
-
- return MoreObjects.firstNonNull(node.getMeter(), ImmutableList.<Meter>of());
+ return ReconcileUtil.resolveAndDivideGroupDiffs(nodeId, groupConfiguredMap, pendingGroups, false);
}
- private ListenableFuture<RpcResult<Void>> flushRemoveGroupPortionAndBarrier(
- final InstanceIdentifier<FlowCapableNode> nodeIdent,
- final ItemSyncBox<Group> groupsPortion) {
- List<ListenableFuture<RpcResult<RemoveGroupOutput>>> allResults = new ArrayList<>();
- for (Group group : groupsPortion.getItemsToAdd()) {
- final KeyedInstanceIdentifier<Group, GroupKey> groupIdent = nodeIdent.child(Group.class, group.getKey());
- allResults.add(JdkFutureAdapters.listenInPoolThread(groupForwarder.remove(groupIdent, group, nodeIdent)));
- }
-
- final ListenableFuture<RpcResult<Void>> singleVoidResult = Futures.transform(
- Futures.allAsList(allResults),
- ReconcileUtil.<RemoveGroupOutput>createRpcResultCondenser("group remove"));
-
- return Futures.transform(singleVoidResult,
- ReconcileUtil.chainBarrierFlush(PathUtil.digNodePath(nodeIdent), transactionService));
- }
}
--- /dev/null
+/**
+ * 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.applications.frsync.impl.strategy;
+
+import com.google.common.collect.Iterables;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.JdkFutureAdapters;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.opendaylight.openflowplugin.applications.frsync.SyncPlanPushStrategy;
+import org.opendaylight.openflowplugin.applications.frsync.impl.FlowForwarder;
+import org.opendaylight.openflowplugin.applications.frsync.impl.GroupForwarder;
+import org.opendaylight.openflowplugin.applications.frsync.impl.MeterForwarder;
+import org.opendaylight.openflowplugin.applications.frsync.impl.TableForwarder;
+import org.opendaylight.openflowplugin.applications.frsync.util.CrudCounts;
+import org.opendaylight.openflowplugin.applications.frsync.util.FxChainUtil;
+import org.opendaylight.openflowplugin.applications.frsync.util.ItemSyncBox;
+import org.opendaylight.openflowplugin.applications.frsync.util.PathUtil;
+import org.opendaylight.openflowplugin.applications.frsync.util.ReconcileUtil;
+import org.opendaylight.openflowplugin.applications.frsync.util.SyncCrudCounters;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.MeterKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.FlowCapableTransactionService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.RemoveGroupOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.UpdateGroupOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.AddMeterOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.RemoveMeterOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.UpdateMeterOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.service.rev131026.UpdateTableOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeatures;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeaturesKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Execute CRUD API for flow + group + meter involving one-by-one (incremental) strategy.
+ */
+public class SyncPlanPushStrategyIncrementalImpl implements SyncPlanPushStrategy {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SyncPlanPushStrategyIncrementalImpl.class);
+
+ private FlowForwarder flowForwarder;
+ private TableForwarder tableForwarder;
+ private MeterForwarder meterForwarder;
+ private GroupForwarder groupForwarder;
+ private FlowCapableTransactionService transactionService;
+
+ @Override
+ public ListenableFuture<RpcResult<Void>> executeSyncStrategy(ListenableFuture<RpcResult<Void>> resultVehicle,
+ final SynchronizationDiffInput diffInput,
+ final SyncCrudCounters counters) {
+ final InstanceIdentifier<FlowCapableNode> nodeIdent = diffInput.getNodeIdent();
+ final NodeId nodeId = PathUtil.digNodeId(nodeIdent);
+
+ /* Tables - have to be pushed before groups */
+ // TODO enable table-update when ready
+ //resultVehicle = updateTableFeatures(nodeIdent, configTree);
+
+ resultVehicle = Futures.transform(resultVehicle, new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
+ @Override
+ public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input) throws Exception {
+ if (!input.isSuccessful()) {
+ //TODO chain errors but not skip processing on first error return Futures.immediateFuture(input);
+ //final ListenableFuture<RpcResult<Void>> singleVoidUpdateResult = Futures.transform(
+ // Futures.asList Arrays.asList(input, output),
+ // ReconcileUtil.<UpdateFlowOutput>createRpcResultCondenser("TODO"));
+ }
+ return addMissingGroups(nodeId, nodeIdent, diffInput.getGroupsToAddOrUpdate(), counters);
+ }
+ });
+ Futures.addCallback(resultVehicle, FxChainUtil.logResultCallback(nodeId, "addMissingGroups"));
+ resultVehicle = Futures.transform(resultVehicle, new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
+ @Override
+ public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input) throws Exception {
+ if (!input.isSuccessful()) {
+ //TODO chain errors but not skip processing on first error return Futures.immediateFuture(input);
+ }
+ return addMissingMeters(nodeId, nodeIdent, diffInput.getMetersToAddOrUpdate(), counters);
+ }
+ });
+ Futures.addCallback(resultVehicle, FxChainUtil.logResultCallback(nodeId, "addMissingMeters"));
+ resultVehicle = Futures.transform(resultVehicle, new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
+ @Override
+ public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input) throws Exception {
+ if (!input.isSuccessful()) {
+ //TODO chain errors but not skip processing on first error return Futures.immediateFuture(input);
+ }
+ return addMissingFlows(nodeId, nodeIdent, diffInput.getFlowsToAddOrUpdate(), counters);
+ }
+ });
+ Futures.addCallback(resultVehicle, FxChainUtil.logResultCallback(nodeId, "addMissingFlows"));
+
+
+ resultVehicle = Futures.transform(resultVehicle, new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
+ @Override
+ public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input) throws Exception {
+ if (!input.isSuccessful()) {
+ //TODO chain errors but not skip processing on first error return Futures.immediateFuture(input);
+ }
+ return removeRedundantFlows(nodeId, nodeIdent, diffInput.getFlowsToRemove(), counters);
+ }
+ });
+ Futures.addCallback(resultVehicle, FxChainUtil.logResultCallback(nodeId, "removeRedundantFlows"));
+ resultVehicle = Futures.transform(resultVehicle, new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
+ @Override
+ public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input) throws Exception {
+ if (!input.isSuccessful()) {
+ //TODO chain errors but not skip processing on first error return Futures.immediateFuture(input);
+ }
+ return removeRedundantMeters(nodeId, nodeIdent, diffInput.getMetersToRemove(), counters);
+ }
+ });
+ Futures.addCallback(resultVehicle, FxChainUtil.logResultCallback(nodeId, "removeRedundantMeters"));
+ resultVehicle = Futures.transform(resultVehicle, new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
+ @Override
+ public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input) throws Exception {
+ if (!input.isSuccessful()) {
+ //TODO chain errors but not skip processing on first error return Futures.immediateFuture(input);
+ }
+ return removeRedundantGroups(nodeId, nodeIdent, diffInput.getGroupsToRemove(), counters);
+ }
+ });
+ Futures.addCallback(resultVehicle, FxChainUtil.logResultCallback(nodeId, "removeRedundantGroups"));
+ return resultVehicle;
+ }
+
+
+ protected ListenableFuture<RpcResult<Void>> addMissingFlows(final NodeId nodeId,
+ final InstanceIdentifier<FlowCapableNode> nodeIdent,
+ final Map<TableKey, ItemSyncBox<Flow>> flowsInTablesSyncBox,
+ final SyncCrudCounters counters) {
+ if (flowsInTablesSyncBox.isEmpty()) {
+ LOG.trace("no tables in config for node: {} -> SKIPPING", nodeId.getValue());
+ return RpcResultBuilder.<Void>success().buildFuture();
+ }
+
+ final List<ListenableFuture<RpcResult<AddFlowOutput>>> allResults = new ArrayList<>();
+ final List<ListenableFuture<RpcResult<UpdateFlowOutput>>> allUpdateResults = new ArrayList<>();
+ final CrudCounts flowCrudCounts = counters.getFlowCrudCounts();
+
+ for (Map.Entry<TableKey, ItemSyncBox<Flow>> flowsInTableBoxEntry : flowsInTablesSyncBox.entrySet()) {
+ final TableKey tableKey = flowsInTableBoxEntry.getKey();
+ final ItemSyncBox<Flow> flowSyncBox = flowsInTableBoxEntry.getValue();
+
+ final KeyedInstanceIdentifier<Table, TableKey> tableIdent = nodeIdent.child(Table.class, tableKey);
+
+ for (final Flow flow : flowSyncBox.getItemsToPush()) {
+ final KeyedInstanceIdentifier<Flow, FlowKey> flowIdent = tableIdent.child(Flow.class, flow.getKey());
+
+ LOG.trace("adding flow {} in table {} - absent on device {} match{}",
+ flow.getId(), tableKey, nodeId, flow.getMatch());
+
+ allResults.add(JdkFutureAdapters.listenInPoolThread(
+ flowForwarder.add(flowIdent, flow, nodeIdent)));
+ flowCrudCounts.incAdded();
+ }
+
+ for (final ItemSyncBox.ItemUpdateTuple<Flow> flowUpdate : flowSyncBox.getItemsToUpdate()) {
+ final Flow existingFlow = flowUpdate.getOriginal();
+ final Flow updatedFlow = flowUpdate.getUpdated();
+
+ final KeyedInstanceIdentifier<Flow, FlowKey> flowIdent = tableIdent.child(Flow.class, updatedFlow.getKey());
+ LOG.trace("flow {} in table {} - needs update on device {} match{}",
+ updatedFlow.getId(), tableKey, nodeId, updatedFlow.getMatch());
+
+ allUpdateResults.add(JdkFutureAdapters.listenInPoolThread(
+ flowForwarder.update(flowIdent, existingFlow, updatedFlow, nodeIdent)));
+ flowCrudCounts.incUpdated();
+ }
+ }
+
+ final ListenableFuture<RpcResult<Void>> singleVoidAddResult = Futures.transform(
+ Futures.allAsList(allResults),
+ ReconcileUtil.<AddFlowOutput>createRpcResultCondenser("flow adding"));
+
+ final ListenableFuture<RpcResult<Void>> singleVoidUpdateResult = Futures.transform(
+ Futures.allAsList(allUpdateResults),
+ ReconcileUtil.<UpdateFlowOutput>createRpcResultCondenser("flow updating"));
+
+ final ListenableFuture<RpcResult<Void>> summaryResult = Futures.transform(
+ Futures.allAsList(singleVoidAddResult, singleVoidUpdateResult),
+ ReconcileUtil.<Void>createRpcResultCondenser("flow add/update"));
+
+ return summaryResult;
+
+ /*
+ return Futures.transform(summaryResult,
+ ReconcileUtil.chainBarrierFlush(PathUtil.digNodePath(nodeIdent), transactionService));
+ */
+ }
+
+ protected ListenableFuture<RpcResult<Void>> removeRedundantFlows(final NodeId nodeId,
+ final InstanceIdentifier<FlowCapableNode> nodeIdent,
+ final Map<TableKey, ItemSyncBox<Flow>> removalPlan,
+ final SyncCrudCounters counters) {
+ if (removalPlan.isEmpty()) {
+ LOG.trace("no tables in operational for node: {} -> SKIPPING", nodeId.getValue());
+ return RpcResultBuilder.<Void>success().buildFuture();
+ }
+
+ final List<ListenableFuture<RpcResult<RemoveFlowOutput>>> allResults = new ArrayList<>();
+ final CrudCounts flowCrudCounts = counters.getFlowCrudCounts();
+
+ for (final Map.Entry<TableKey, ItemSyncBox<Flow>> flowsPerTable : removalPlan.entrySet()) {
+ final KeyedInstanceIdentifier<Table, TableKey> tableIdent =
+ nodeIdent.child(Table.class, flowsPerTable.getKey());
+
+ // loop flows on device and check if the are configured
+ for (final Flow flow : flowsPerTable.getValue().getItemsToPush()) {
+ final KeyedInstanceIdentifier<Flow, FlowKey> flowIdent =
+ tableIdent.child(Flow.class, flow.getKey());
+ allResults.add(JdkFutureAdapters.listenInPoolThread(
+ flowForwarder.remove(flowIdent, flow, nodeIdent)));
+ flowCrudCounts.incRemoved();
+ }
+ }
+
+ final ListenableFuture<RpcResult<Void>> singleVoidResult = Futures.transform(
+ Futures.allAsList(allResults), ReconcileUtil.<RemoveFlowOutput>createRpcResultCondenser("flow remove"));
+ return Futures.transform(singleVoidResult,
+ ReconcileUtil.chainBarrierFlush(PathUtil.digNodePath(nodeIdent), transactionService));
+
+ }
+
+ protected ListenableFuture<RpcResult<Void>> removeRedundantMeters(final NodeId nodeId,
+ final InstanceIdentifier<FlowCapableNode> nodeIdent,
+ final ItemSyncBox<Meter> meterRemovalPlan,
+ final SyncCrudCounters counters) {
+ if (meterRemovalPlan.isEmpty()) {
+ LOG.trace("no meters on device for node: {} -> SKIPPING", nodeId.getValue());
+ return RpcResultBuilder.<Void>success().buildFuture();
+ }
+
+ final CrudCounts meterCrudCounts = counters.getMeterCrudCounts();
+
+ final List<ListenableFuture<RpcResult<RemoveMeterOutput>>> allResults = new ArrayList<>();
+ for (Meter meter : meterRemovalPlan.getItemsToPush()) {
+ LOG.trace("removing meter {} - absent in config {}",
+ meter.getMeterId(), nodeId);
+ final KeyedInstanceIdentifier<Meter, MeterKey> meterIdent =
+ nodeIdent.child(Meter.class, meter.getKey());
+ allResults.add(JdkFutureAdapters.listenInPoolThread(
+ meterForwarder.remove(meterIdent, meter, nodeIdent)));
+ meterCrudCounts.incRemoved();
+ }
+
+ final ListenableFuture<RpcResult<Void>> singleVoidResult = Futures.transform(
+ Futures.allAsList(allResults),
+ ReconcileUtil.<RemoveMeterOutput>createRpcResultCondenser("meter remove"));
+ return singleVoidResult;
+ /*
+ return Futures.transform(singleVoidResult,
+ ReconcileUtil.chainBarrierFlush(PathUtil.digNodePath(nodeIdent), transactionService));
+ */
+ }
+
+ ListenableFuture<RpcResult<Void>> removeRedundantGroups(final NodeId nodeId,
+ final InstanceIdentifier<FlowCapableNode> nodeIdent,
+ final List<ItemSyncBox<Group>> groupsRemovalPlan,
+ final SyncCrudCounters counters) {
+ if (groupsRemovalPlan.isEmpty()) {
+ LOG.trace("no groups on device for node: {} -> SKIPPING", nodeId.getValue());
+ return RpcResultBuilder.<Void>success().buildFuture();
+ }
+
+ final CrudCounts groupCrudCounts = counters.getGroupCrudCounts();
+
+ ListenableFuture<RpcResult<Void>> chainedResult = RpcResultBuilder.<Void>success().buildFuture();
+ try {
+ groupCrudCounts.setRemoved(ReconcileUtil.countTotalAdds(groupsRemovalPlan));
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("removing groups: planSteps={}, toRemoveTotal={}",
+ groupsRemovalPlan.size(), groupCrudCounts.getRemoved());
+ }
+ Collections.reverse(groupsRemovalPlan);
+ for (final ItemSyncBox<Group> groupsPortion : groupsRemovalPlan) {
+ chainedResult =
+ Futures.transform(chainedResult, new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
+ @Override
+ public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input)
+ throws Exception {
+ final ListenableFuture<RpcResult<Void>> result;
+ if (input.isSuccessful()) {
+ result = flushRemoveGroupPortionAndBarrier(nodeIdent, groupsPortion);
+ } else {
+ // pass through original unsuccessful rpcResult
+ result = Futures.immediateFuture(input);
+ }
+
+ return result;
+ }
+ });
+ }
+ } catch (IllegalStateException e) {
+ chainedResult = RpcResultBuilder.<Void>failed()
+ .withError(RpcError.ErrorType.APPLICATION, "failed to add missing groups", e)
+ .buildFuture();
+ }
+
+ return chainedResult;
+ }
+
+ private ListenableFuture<RpcResult<Void>> flushRemoveGroupPortionAndBarrier(
+ final InstanceIdentifier<FlowCapableNode> nodeIdent,
+ final ItemSyncBox<Group> groupsPortion) {
+ List<ListenableFuture<RpcResult<RemoveGroupOutput>>> allResults = new ArrayList<>();
+ for (Group group : groupsPortion.getItemsToPush()) {
+ final KeyedInstanceIdentifier<Group, GroupKey> groupIdent = nodeIdent.child(Group.class, group.getKey());
+ allResults.add(JdkFutureAdapters.listenInPoolThread(groupForwarder.remove(groupIdent, group, nodeIdent)));
+ }
+
+ final ListenableFuture<RpcResult<Void>> singleVoidResult = Futures.transform(
+ Futures.allAsList(allResults),
+ ReconcileUtil.<RemoveGroupOutput>createRpcResultCondenser("group remove"));
+
+ return Futures.transform(singleVoidResult,
+ ReconcileUtil.chainBarrierFlush(PathUtil.digNodePath(nodeIdent), transactionService));
+ }
+
+ ListenableFuture<RpcResult<Void>> updateTableFeatures(final InstanceIdentifier<FlowCapableNode> nodeIdent,
+ final FlowCapableNode flowCapableNodeConfigured) {
+ // CHECK if while pushing the update, updateTableInput can be null to emulate a table add
+ final List<Table> tableList = ReconcileUtil.safeTables(flowCapableNodeConfigured);
+
+ final List<ListenableFuture<RpcResult<UpdateTableOutput>>> allResults = new ArrayList<>();
+ for (Table table : tableList) {
+ TableKey tableKey = table.getKey();
+ KeyedInstanceIdentifier<TableFeatures, TableFeaturesKey> tableFeaturesII = nodeIdent
+ .child(TableFeatures.class, new TableFeaturesKey(tableKey.getId()));
+ List<TableFeatures> tableFeatures = flowCapableNodeConfigured.getTableFeatures();
+ if (tableFeatures != null) {
+ for (TableFeatures tableFeaturesItem : tableFeatures) {
+ // TODO uncomment java.lang.NullPointerException
+ // at
+ // org.opendaylight.openflowjava.protocol.impl.serialization.match.AbstractOxmMatchEntrySerializer.serializeHeader(AbstractOxmMatchEntrySerializer.java:31
+ // allResults.add(JdkFutureAdapters.listenInPoolThread(
+ // tableForwarder.update(tableFeaturesII, null, tableFeaturesItem, nodeIdent)));
+ }
+ }
+ }
+
+ final ListenableFuture<RpcResult<Void>> singleVoidResult = Futures.transform(
+ Futures.allAsList(allResults),
+ ReconcileUtil.<UpdateTableOutput>createRpcResultCondenser("table update"));
+
+ return Futures.transform(singleVoidResult,
+ ReconcileUtil.chainBarrierFlush(PathUtil.digNodePath(nodeIdent), transactionService));
+ }
+
+ private ListenableFuture<RpcResult<Void>> flushAddGroupPortionAndBarrier(
+ final InstanceIdentifier<FlowCapableNode> nodeIdent,
+ final ItemSyncBox<Group> groupsPortion) {
+ final List<ListenableFuture<RpcResult<AddGroupOutput>>> allResults = new ArrayList<>();
+ final List<ListenableFuture<RpcResult<UpdateGroupOutput>>> allUpdateResults = new ArrayList<>();
+
+ for (Group group : groupsPortion.getItemsToPush()) {
+ final KeyedInstanceIdentifier<Group, GroupKey> groupIdent = nodeIdent.child(Group.class, group.getKey());
+ allResults.add(JdkFutureAdapters.listenInPoolThread(groupForwarder.add(groupIdent, group, nodeIdent)));
+
+ }
+
+ for (ItemSyncBox.ItemUpdateTuple<Group> groupTuple : groupsPortion.getItemsToUpdate()) {
+ final Group existingGroup = groupTuple.getOriginal();
+ final Group group = groupTuple.getUpdated();
+
+ final KeyedInstanceIdentifier<Group, GroupKey> groupIdent = nodeIdent.child(Group.class, group.getKey());
+ allUpdateResults.add(JdkFutureAdapters.listenInPoolThread(
+ groupForwarder.update(groupIdent, existingGroup, group, nodeIdent)));
+ }
+
+ final ListenableFuture<RpcResult<Void>> singleVoidAddResult = Futures.transform(
+ Futures.allAsList(allResults), ReconcileUtil.<AddGroupOutput>createRpcResultCondenser("group add"));
+
+ final ListenableFuture<RpcResult<Void>> singleVoidUpdateResult = Futures.transform(
+ Futures.allAsList(allUpdateResults),
+ ReconcileUtil.<UpdateGroupOutput>createRpcResultCondenser("group update"));
+
+ final ListenableFuture<RpcResult<Void>> summaryResult = Futures.transform(
+ Futures.allAsList(singleVoidAddResult, singleVoidUpdateResult),
+ ReconcileUtil.<Void>createRpcResultCondenser("group add/update"));
+
+
+ return Futures.transform(summaryResult,
+ ReconcileUtil.chainBarrierFlush(
+ PathUtil.digNodePath(nodeIdent), transactionService));
+ }
+
+ protected ListenableFuture<RpcResult<Void>> addMissingMeters(final NodeId nodeId,
+ final InstanceIdentifier<FlowCapableNode> nodeIdent,
+ final ItemSyncBox<Meter> syncBox,
+ final SyncCrudCounters counters) {
+ if (syncBox.isEmpty()) {
+ LOG.trace("no meters configured for node: {} -> SKIPPING", nodeId.getValue());
+ return RpcResultBuilder.<Void>success().buildFuture();
+ }
+
+ final CrudCounts meterCrudCounts = counters.getMeterCrudCounts();
+
+ final List<ListenableFuture<RpcResult<AddMeterOutput>>> allResults = new ArrayList<>();
+ final List<ListenableFuture<RpcResult<UpdateMeterOutput>>> allUpdateResults = new ArrayList<>();
+ for (Meter meter : syncBox.getItemsToPush()) {
+ final KeyedInstanceIdentifier<Meter, MeterKey> meterIdent = nodeIdent.child(Meter.class, meter.getKey());
+ LOG.debug("adding meter {} - absent on device {}",
+ meter.getMeterId(), nodeId);
+ allResults.add(JdkFutureAdapters.listenInPoolThread(
+ meterForwarder.add(meterIdent, meter, nodeIdent)));
+ meterCrudCounts.incAdded();
+ }
+
+ for (ItemSyncBox.ItemUpdateTuple<Meter> meterTuple : syncBox.getItemsToUpdate()) {
+ final Meter existingMeter = meterTuple.getOriginal();
+ final Meter updated = meterTuple.getUpdated();
+ final KeyedInstanceIdentifier<Meter, MeterKey> meterIdent = nodeIdent.child(Meter.class, updated.getKey());
+ LOG.trace("meter {} - needs update on device {}", updated.getMeterId(), nodeId);
+ allUpdateResults.add(JdkFutureAdapters.listenInPoolThread(
+ meterForwarder.update(meterIdent, existingMeter, updated, nodeIdent)));
+ meterCrudCounts.incUpdated();
+ }
+
+ final ListenableFuture<RpcResult<Void>> singleVoidAddResult = Futures.transform(
+ Futures.allAsList(allResults), ReconcileUtil.<AddMeterOutput>createRpcResultCondenser("meter add"));
+
+ final ListenableFuture<RpcResult<Void>> singleVoidUpdateResult = Futures.transform(
+ Futures.allAsList(allUpdateResults),
+ ReconcileUtil.<UpdateMeterOutput>createRpcResultCondenser("meter update"));
+
+ final ListenableFuture<RpcResult<Void>> summaryResults = Futures.transform(
+ Futures.allAsList(singleVoidUpdateResult, singleVoidAddResult),
+ ReconcileUtil.<Void>createRpcResultCondenser("meter add/update"));
+
+ return summaryResults;
+
+ /*
+ return Futures.transform(summaryResults,
+ ReconcileUtil.chainBarrierFlush(PathUtil.digNodePath(nodeIdent), transactionService));
+ */
+ }
+
+ protected ListenableFuture<RpcResult<Void>> addMissingGroups(final NodeId nodeId,
+ final InstanceIdentifier<FlowCapableNode> nodeIdent,
+ final List<ItemSyncBox<Group>> groupsAddPlan,
+ final SyncCrudCounters counters) {
+ if (groupsAddPlan.isEmpty()) {
+ LOG.trace("no groups configured for node: {} -> SKIPPING", nodeId.getValue());
+ return RpcResultBuilder.<Void>success().buildFuture();
+ }
+
+ ListenableFuture<RpcResult<Void>> chainedResult;
+ try {
+ if (!groupsAddPlan.isEmpty()) {
+ final CrudCounts groupCrudCounts = counters.getGroupCrudCounts();
+ groupCrudCounts.setAdded(ReconcileUtil.countTotalAdds(groupsAddPlan));
+ groupCrudCounts.setUpdated(ReconcileUtil.countTotalUpdated(groupsAddPlan));
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("adding groups: planSteps={}, toAddTotal={}, toUpdateTotal={}",
+ groupsAddPlan.size(),
+ groupCrudCounts.getAdded(),
+ groupCrudCounts.getUpdated());
+ }
+
+ chainedResult = flushAddGroupPortionAndBarrier(nodeIdent, groupsAddPlan.get(0));
+ for (final ItemSyncBox<Group> groupsPortion : Iterables.skip(groupsAddPlan, 1)) {
+ chainedResult =
+ Futures.transform(chainedResult, new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
+ @Override
+ public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input)
+ throws Exception {
+ final ListenableFuture<RpcResult<Void>> result;
+ if (input.isSuccessful()) {
+ result = flushAddGroupPortionAndBarrier(nodeIdent, groupsPortion);
+ } else {
+ // pass through original unsuccessful rpcResult
+ result = Futures.immediateFuture(input);
+ }
+
+ return result;
+ }
+ });
+ }
+ } else {
+ chainedResult = RpcResultBuilder.<Void>success().buildFuture();
+ }
+ } catch (IllegalStateException e) {
+ chainedResult = RpcResultBuilder.<Void>failed()
+ .withError(RpcError.ErrorType.APPLICATION, "failed to add missing groups", e)
+ .buildFuture();
+ }
+
+ return chainedResult;
+ }
+
+
+ public SyncPlanPushStrategyIncrementalImpl setFlowForwarder(final FlowForwarder flowForwarder) {
+ this.flowForwarder = flowForwarder;
+ return this;
+ }
+
+ public SyncPlanPushStrategyIncrementalImpl setTableForwarder(final TableForwarder tableForwarder) {
+ this.tableForwarder = tableForwarder;
+ return this;
+ }
+
+ public SyncPlanPushStrategyIncrementalImpl setMeterForwarder(final MeterForwarder meterForwarder) {
+ this.meterForwarder = meterForwarder;
+ return this;
+ }
+
+ public SyncPlanPushStrategyIncrementalImpl setGroupForwarder(final GroupForwarder groupForwarder) {
+ this.groupForwarder = groupForwarder;
+ return this;
+ }
+
+ public SyncPlanPushStrategyIncrementalImpl setTransactionService(final FlowCapableTransactionService transactionService) {
+ this.transactionService = transactionService;
+ return this;
+ }
+
+}
--- /dev/null
+/**
+ * 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.applications.frsync.impl.strategy;
+
+import java.util.List;
+import java.util.Map;
+import org.opendaylight.openflowplugin.applications.frsync.util.ItemSyncBox;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Wraps all the required inputs (diffs) for synchronization strategy execution.
+ */
+public class SynchronizationDiffInput {
+
+ private final InstanceIdentifier<FlowCapableNode> nodeIdent;
+ final List<ItemSyncBox<Group>> groupsToAddOrUpdate;
+ final ItemSyncBox<Meter> metersToAddOrUpdate;
+ final Map<TableKey, ItemSyncBox<Flow>> flowsToAddOrUpdate;
+ final Map<TableKey, ItemSyncBox<Flow>> flowsToRemove;
+ final ItemSyncBox<Meter> metersToRemove;
+ final List<ItemSyncBox<Group>> groupsToRemove;
+
+ public SynchronizationDiffInput(final InstanceIdentifier<FlowCapableNode> nodeIdent,
+ final List<ItemSyncBox<Group>> groupsToAddOrUpdate,
+ final ItemSyncBox<Meter> metersToAddOrUpdate,
+ final Map<TableKey, ItemSyncBox<Flow>> flowsToAddOrUpdate,
+ final Map<TableKey, ItemSyncBox<Flow>> flowsToRemove,
+ final ItemSyncBox<Meter> metersToRemove,
+ final List<ItemSyncBox<Group>> groupsToRemove) {
+ this.nodeIdent = nodeIdent;
+ this.groupsToAddOrUpdate = groupsToAddOrUpdate;
+ this.metersToAddOrUpdate = metersToAddOrUpdate;
+ this.flowsToAddOrUpdate = flowsToAddOrUpdate;
+ this.flowsToRemove = flowsToRemove;
+ this.metersToRemove = metersToRemove;
+ this.groupsToRemove = groupsToRemove;
+ }
+
+ public InstanceIdentifier<FlowCapableNode> getNodeIdent() {
+ return nodeIdent;
+ }
+
+ public List<ItemSyncBox<Group>> getGroupsToAddOrUpdate() {
+ return groupsToAddOrUpdate;
+ }
+
+ public ItemSyncBox<Meter> getMetersToAddOrUpdate() {
+ return metersToAddOrUpdate;
+ }
+
+ public Map<TableKey, ItemSyncBox<Flow>> getFlowsToAddOrUpdate() {
+ return flowsToAddOrUpdate;
+ }
+
+ public Map<TableKey, ItemSyncBox<Flow>> getFlowsToRemove() {
+ return flowsToRemove;
+ }
+
+ public ItemSyncBox<Meter> getMetersToRemove() {
+ return metersToRemove;
+ }
+
+ public List<ItemSyncBox<Group>> getGroupsToRemove() {
+ return groupsToRemove;
+ }
+}
--- /dev/null
+/**
+ * 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.applications.frsync.util;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.FutureCallback;
+import java.util.Arrays;
+import java.util.Collection;
+import javax.annotation.Nullable;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Util methods for {@link com.google.common.util.concurrent.ListenableFuture} chaining.
+ */
+public class FxChainUtil {
+
+ private static final Logger LOG = LoggerFactory.getLogger(FxChainUtil.class);
+
+
+ public static FutureCallback<RpcResult<Void>> logResultCallback(final NodeId nodeId, final String prefix) {
+ return new FutureCallback<RpcResult<Void>>() {
+ @Override
+ public void onSuccess(@Nullable final RpcResult<Void> result) {
+ if (result != null) {
+ if (result.isSuccessful()) {
+ LOG.debug(prefix + " finished successfully: {}", nodeId.getValue());
+ } else {
+ final Collection<RpcError> errors = MoreObjects.firstNonNull(result.getErrors(), ImmutableList.<RpcError>of());
+ LOG.debug(prefix + " failed: {} -> {}", nodeId.getValue(), Arrays.toString(errors.toArray()));
+ }
+ } else {
+ LOG.debug(prefix + "reconciliation failed: {} -> null result", nodeId.getValue());
+ }
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ LOG.debug(prefix + "reconciliation failed seriously: {}", nodeId.getValue(), t);
+ }
+ };
+ }
+}
package org.opendaylight.openflowplugin.applications.frsync.util;
-import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.Set;
/**
*/
public class ItemSyncBox<I> {
- private Set<I> itemsToAdd = new HashSet<>();
- private Set<ItemUpdateTuple<I>> itemsToUpdate = new HashSet<>();
+ private Set<I> itemsToPush = new LinkedHashSet<>();
+ private Set<ItemUpdateTuple<I>> itemsToUpdate = new LinkedHashSet<>();
- public Set<I> getItemsToAdd() {
- return itemsToAdd;
+ public Set<I> getItemsToPush() {
+ return itemsToPush;
}
public Set<ItemUpdateTuple<I>> getItemsToUpdate() {
}
public boolean isEmpty() {
- return itemsToAdd.isEmpty() && itemsToUpdate.isEmpty();
+ return itemsToPush.isEmpty() && itemsToUpdate.isEmpty();
}
/**
package org.opendaylight.openflowplugin.applications.frsync.util;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import javax.annotation.Nullable;
-
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.JdkFutureAdapters;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.Collections;
+import org.opendaylight.openflowplugin.applications.frsync.markandsweep.SwitchFlowId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.GroupActionCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.FlowCapableTransactionService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.SendBarrierInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.SendBarrierInputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterId;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Function;
-import com.google.common.collect.Iterables;
-import com.google.common.util.concurrent.AsyncFunction;
-import com.google.common.util.concurrent.JdkFutureAdapters;
-import com.google.common.util.concurrent.ListenableFuture;
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
/**
* Util methods for group reconcil task (future chaining, transforms).
};
}
- public static List<ItemSyncBox<Group>> resolveAndDivideGroups(final NodeId nodeId,
- final Map<Long, Group> installedGroupsArg,
- final Collection<Group> pendingGroups) {
- return resolveAndDivideGroups(nodeId, installedGroupsArg, pendingGroups, true);
+ /**
+ * @param nodeId target node
+ * @param installedGroupsArg groups resent on device
+ * @param pendingGroups groups configured for device
+ * @return list of safe synchronization steps with updates
+ */
+ public static List<ItemSyncBox<Group>> resolveAndDivideGroupDiffs(final NodeId nodeId,
+ final Map<Long, Group> installedGroupsArg,
+ final Collection<Group> pendingGroups) {
+ return resolveAndDivideGroupDiffs(nodeId, installedGroupsArg, pendingGroups, true);
}
- public static List<ItemSyncBox<Group>> resolveAndDivideGroups(final NodeId nodeId,
- final Map<Long, Group> installedGroupsArg,
- final Collection<Group> pendingGroups,
- final boolean gatherUpdates) {
+ /**
+ * @param nodeId target node
+ * @param installedGroupsArg groups resent on device
+ * @param pendingGroups groups configured for device
+ * @param gatherUpdates check content of pending item if present on device (and create update task eventually)
+ * @return list of safe synchronization steps
+ */
+ public static List<ItemSyncBox<Group>> resolveAndDivideGroupDiffs(final NodeId nodeId,
+ final Map<Long, Group> installedGroupsArg,
+ final Collection<Group> pendingGroups,
+ final boolean gatherUpdates) {
final Map<Long, Group> installedGroups = new HashMap<>(installedGroupsArg);
final List<ItemSyncBox<Group>> plan = new ArrayList<>();
} else if (checkGroupPrecondition(installedGroups.keySet(), group)) {
iterator.remove();
installIncrement.put(group.getGroupId().getValue(), group);
- stepPlan.getItemsToAdd().add(group);
+ stepPlan.getItemsToPush().add(group);
}
}
public static <E> int countTotalAdds(final List<ItemSyncBox<E>> groupsAddPlan) {
int count = 0;
for (ItemSyncBox<E> groupItemSyncBox : groupsAddPlan) {
- count += groupItemSyncBox.getItemsToAdd().size();
+ count += groupItemSyncBox.getItemsToPush().size();
}
return count;
}
}
return count;
}
+
+ /**
+ * @param nodeId target node
+ * @param meterOperationalMap meters present on device
+ * @param metersConfigured meters configured for device
+ * @param gatherUpdates check content of pending item if present on device (and create update task eventually)
+ * @return synchronization box
+ */
+ public static ItemSyncBox<Meter> resolveMeterDiffs(final NodeId nodeId,
+ final Map<MeterId, Meter> meterOperationalMap,
+ final List<Meter> metersConfigured,
+ final boolean gatherUpdates) {
+ LOG.trace("resolving meters for {}", nodeId);
+ final ItemSyncBox<Meter> syncBox = new ItemSyncBox<>();
+ for (Meter meter : metersConfigured) {
+ final Meter existingMeter = meterOperationalMap.get(meter.getMeterId());
+ if (existingMeter == null) {
+ syncBox.getItemsToPush().add(meter);
+ } else {
+ // compare content and eventually update
+ if (gatherUpdates && !meter.equals(existingMeter)) {
+ syncBox.getItemsToUpdate().add(new ItemSyncBox.ItemUpdateTuple<>(existingMeter, meter));
+ }
+ }
+ }
+ return syncBox;
+ }
+
+ /**
+ * @param flowsConfigured flows resent on device
+ * @param flowOperationalMap flows configured for device
+ * @param gatherUpdates check content of pending item if present on device (and create update task eventually)
+ * @return list of safe synchronization steps
+ */
+ @VisibleForTesting
+ static ItemSyncBox<Flow> resolveFlowDiffsInTable(final List<Flow> flowsConfigured,
+ final Map<SwitchFlowId, Flow> flowOperationalMap,
+ final boolean gatherUpdates) {
+ final ItemSyncBox<Flow> flowsSyncBox = new ItemSyncBox<>();
+ // loop configured flows and check if already present on device
+ for (final Flow flow : flowsConfigured) {
+ final Flow existingFlow = FlowCapableNodeLookups.flowMapLookupExisting(flow, flowOperationalMap);
+
+ if (existingFlow == null) {
+ flowsSyncBox.getItemsToPush().add(flow);
+ } else {
+ // check instructions and eventually update
+ if (gatherUpdates && !Objects.equals(flow.getInstructions(), existingFlow.getInstructions())) {
+ flowsSyncBox.getItemsToUpdate().add(new ItemSyncBox.ItemUpdateTuple<>(existingFlow, flow));
+ }
+ }
+ }
+ return flowsSyncBox;
+ }
+
+ /**
+ * @param nodeId target node
+ * @param tableOperationalMap flow-tables resent on device
+ * @param tablesConfigured flow-tables configured for device
+ * @param gatherUpdates check content of pending item if present on device (and create update task eventually)
+ * @return map : key={@link TableKey}, value={@link ItemSyncBox} of safe synchronization steps
+ */
+ public static Map<TableKey, ItemSyncBox<Flow>> resolveFlowDiffsInAllTables(final NodeId nodeId,
+ final Map<Short, Table> tableOperationalMap,
+ final List<Table> tablesConfigured,
+ final boolean gatherUpdates) {
+ LOG.trace("resolving flows in tables for {}", nodeId);
+ final Map<TableKey, ItemSyncBox<Flow>> tableFlowSyncBoxes = new HashMap<>();
+ for (final Table tableConfigured : tablesConfigured) {
+ final List<Flow> flowsConfigured = tableConfigured.getFlow();
+ if (flowsConfigured == null || flowsConfigured.isEmpty()) {
+ continue;
+ }
+
+ // lookup table (on device)
+ final Table tableOperational = tableOperationalMap.get(tableConfigured.getId());
+ // wrap existing (on device) flows in current table into map
+ final Map<SwitchFlowId, Flow> flowOperationalMap = FlowCapableNodeLookups.wrapFlowsToMap(
+ tableOperational != null
+ ? tableOperational.getFlow()
+ : null);
+
+
+ final ItemSyncBox<Flow> flowsSyncBox = resolveFlowDiffsInTable(
+ flowsConfigured, flowOperationalMap, gatherUpdates);
+ if (!flowsSyncBox.isEmpty()) {
+ tableFlowSyncBoxes.put(tableConfigured.getKey(), flowsSyncBox);
+ }
+ }
+ return tableFlowSyncBoxes;
+ }
+
+ public static List<Group> safeGroups(FlowCapableNode node) {
+ if (node == null) {
+ return Collections.emptyList();
+ }
+
+ return MoreObjects.firstNonNull(node.getGroup(), ImmutableList.<Group>of());
+ }
+
+ public static List<Table> safeTables(FlowCapableNode node) {
+ if (node == null) {
+ return Collections.emptyList();
+ }
+
+ return MoreObjects.firstNonNull(node.getTable(), ImmutableList.<Table>of());
+ }
+
+ public static List<Meter> safeMeters(FlowCapableNode node) {
+ if (node == null) {
+ return Collections.emptyList();
+ }
+
+ return MoreObjects.firstNonNull(node.getMeter(), ImmutableList.<Meter>of());
+ }
}
* 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.applications.frsync.util;
/**
* 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.applications.frsync.impl;
import org.junit.Before;
@RunWith(MockitoJUnitRunner.class)
public class SimplifiedOperationalListenerTest {
+ public static final NodeId NODE_ID = new NodeId("testNode");
@Mock
private SyncReactor reactor;
@Mock
@Mock
private DataObjectModification<Node> operationalModification;
- private NodeId nodeId;
private InstanceIdentifier<Node> nodePath;
private InstanceIdentifier<FlowCapableNode> fcNodePath;
private SimplifiedOperationalListener nodeListenerOperational;
final FlowCapableNodeDao configDao = new FlowCapableNodeCachedDao(configSnaphot,
new FlowCapableNodeOdlDao(db, LogicalDatastoreType.CONFIGURATION));
- nodeId = new NodeId("testNode");
+
nodeListenerOperational = new SimplifiedOperationalListener(reactor, operationalSnaphot, configDao);
nodePath = InstanceIdentifier.create(Nodes.class)
- .child(Node.class, new NodeKey(nodeId));
+ .child(Node.class, new NodeKey(NODE_ID));
fcNodePath = nodePath.augmentation(FlowCapableNode.class);
}
final FlowCapableNode mockOperationalFlowCapableNode = Mockito.mock(FlowCapableNode.class);
Mockito.when(mockOperationalNode.getAugmentation(FlowCapableNode.class))
.thenReturn(mockOperationalFlowCapableNode);
- Mockito.when(mockOperationalNode.getId()).thenReturn(nodeId);
-
+ Mockito.when(mockOperationalNode.getId()).thenReturn(NODE_ID);
+
final DataTreeIdentifier<Node> dataTreeIdentifier =
new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL, nodePath);
package org.opendaylight.openflowplugin.applications.frsync.impl;
-import com.google.common.util.concurrent.ListenableFuture;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
-import org.mockito.InOrder;
-import org.mockito.Matchers;
import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner;
-import org.mockito.stubbing.Answer;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
-import org.opendaylight.openflowplugin.applications.frsync.util.SyncCrudCounters;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.GroupActionCase;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.GroupActionCaseBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCaseBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.group.action._case.GroupAction;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.group.action._case.GroupActionBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.output.action._case.OutputActionBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
+import org.opendaylight.openflowplugin.applications.frsync.SyncPlanPushStrategy;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.MeterBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowOutputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowOutputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.FlowCapableTransactionService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.SendBarrierInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupOutputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.RemoveGroupOutputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.UpdateGroupOutputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.Buckets;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.BucketsBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.Bucket;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.BucketBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.AddMeterOutputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.RemoveMeterOutputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.UpdateMeterOutputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.BandId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.band.type.band.type.DropBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.MeterBandHeadersBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.meter.band.headers.MeterBandHeaderBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.table.service.rev131026.UpdateTableOutputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeatures;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeaturesBuilder;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Mock
private DataBroker db;
@Mock
- private GroupForwarder groupCommitter;
- @Mock
- private FlowForwarder flowCommitter;
- @Mock
- private MeterForwarder meterCommitter;
- @Mock
- private TableForwarder tableCommitter;
- @Mock
- private FlowCapableTransactionService flowCapableTxService;
+ private SyncPlanPushStrategy syncPlanPushStrategy;
@Captor
private ArgumentCaptor<Group> groupCaptor;
@Captor
private ArgumentCaptor<TableFeatures> tableFeaturesCaptor;
- private SyncCrudCounters counters;
-
@Before
public void setUp() throws Exception {
- Mockito.when(flowCapableTxService.sendBarrier(Matchers.<SendBarrierInput>any()))
- .thenReturn(RpcResultBuilder.success((Void) null).buildFuture());
-
- Mockito.doAnswer(createSalServiceFutureAnswer()).when(groupCommitter).add(
- Matchers.<InstanceIdentifier<Group>>any(), Matchers.<Group>any(),
- Matchers.<InstanceIdentifier<FlowCapableNode>>any());
- Mockito.doAnswer(createSalServiceFutureAnswer()).when(groupCommitter).update(
- Matchers.<InstanceIdentifier<Group>>any(), Matchers.<Group>any(), Matchers.<Group>any(),
- Matchers.<InstanceIdentifier<FlowCapableNode>>any());
- Mockito.doAnswer(createSalServiceFutureAnswer()).when(groupCommitter).remove(
- Matchers.<InstanceIdentifier<Group>>any(), Matchers.<Group>any(),
- Matchers.<InstanceIdentifier<FlowCapableNode>>any());
-
- Mockito.doAnswer(createSalServiceFutureAnswer()).when(flowCommitter).add(
- Matchers.<InstanceIdentifier<Flow>>any(), Matchers.<Flow>any(),
- Matchers.<InstanceIdentifier<FlowCapableNode>>any());
- Mockito.doAnswer(createSalServiceFutureAnswer()).when(flowCommitter).update(
- Matchers.<InstanceIdentifier<Flow>>any(), Matchers.<Flow>any(), Matchers.<Flow>any(),
- Matchers.<InstanceIdentifier<FlowCapableNode>>any());
- Mockito.doAnswer(createSalServiceFutureAnswer()).when(flowCommitter).remove(
- Matchers.<InstanceIdentifier<Flow>>any(), Matchers.<Flow>any(),
- Matchers.<InstanceIdentifier<FlowCapableNode>>any());
-
- Mockito.doAnswer(createSalServiceFutureAnswer()).when(meterCommitter).add(
- Matchers.<InstanceIdentifier<Meter>>any(), Matchers.<Meter>any(),
- Matchers.<InstanceIdentifier<FlowCapableNode>>any());
- Mockito.doAnswer(createSalServiceFutureAnswer()).when(meterCommitter).update(
- Matchers.<InstanceIdentifier<Meter>>any(), Matchers.<Meter>any(), Matchers.<Meter>any(),
- Matchers.<InstanceIdentifier<FlowCapableNode>>any());
- Mockito.doAnswer(createSalServiceFutureAnswer()).when(meterCommitter).remove(
- Matchers.<InstanceIdentifier<Meter>>any(), Matchers.<Meter>any(),
- Matchers.<InstanceIdentifier<FlowCapableNode>>any());
-
- Mockito.doAnswer(createSalServiceFutureAnswer()).when(tableCommitter).update(
- Matchers.<InstanceIdentifier<TableFeatures>>any(), Matchers.<TableFeatures>any(), Matchers.<TableFeatures>any(),
- Matchers.<InstanceIdentifier<FlowCapableNode>>any());
-
- reactor = new SyncReactorImpl();
- reactor.setMeterForwarder(meterCommitter);
- reactor.setTableForwarder(tableCommitter);
- reactor.setGroupForwarder(groupCommitter);
- reactor.setFlowForwarder(flowCommitter);
- reactor.setTransactionService(flowCapableTxService);
-
- counters = new SyncCrudCounters();
- }
-
- private <O> Answer<Future<RpcResult<O>>> createSalServiceFutureAnswer() {
- return new Answer<Future<RpcResult<O>>>() {
- @Override
- public Future<RpcResult<O>> answer(final InvocationOnMock invocation) throws Throwable {
- return RpcResultBuilder.<O>success().buildFuture();
- }
- };
- }
-
- private Group createGroup(final long groupIdValue) {
- final Buckets buckets = new BucketsBuilder()
- .setBucket(Collections.<Bucket>emptyList())
- .build();
- return new GroupBuilder()
- .setGroupId(new GroupId(groupIdValue))
- .setBuckets(buckets)
- .build();
- }
-
- private Group createGroupWithAction(final long groupIdValue) {
- final Buckets buckets = new BucketsBuilder()
- .setBucket(Collections.singletonList(new BucketBuilder()
- .setAction(Collections.singletonList(new ActionBuilder()
- .setAction(new OutputActionCaseBuilder()
- .setOutputAction(new OutputActionBuilder()
- .setOutputNodeConnector(new Uri("ut-port-1"))
- .build())
- .build())
- .build()))
- .build()))
- .build();
- return new GroupBuilder()
- .setGroupId(new GroupId(groupIdValue))
- .setBuckets(buckets)
- .build();
- }
-
- private Flow createFlow(final String flowIdValue, final int priority) {
- return new FlowBuilder()
- .setId(new FlowId(flowIdValue))
- .setPriority(priority)
- .setTableId((short) 42)
- .setMatch(new MatchBuilder().build())
- .build();
- }
-
- private Flow createFlowWithInstruction(final String flowIdValue, final int priority) {
- return new FlowBuilder()
- .setId(new FlowId(flowIdValue))
- .setPriority(priority)
- .setTableId((short) 42)
- .setMatch(new MatchBuilder().build())
- .setInstructions(new InstructionsBuilder()
- .setInstruction(Collections.singletonList(new InstructionBuilder()
- .setInstruction(new ApplyActionsCaseBuilder()
- .setApplyActions(new ApplyActionsBuilder()
- .setAction(Collections.singletonList(new ActionBuilder()
- .setAction(new OutputActionCaseBuilder()
- .setOutputAction(new OutputActionBuilder()
- .setOutputNodeConnector(new Uri("ut-port-1"))
- .build())
- .build())
- .build()))
- .build())
- .build())
- .build()))
- .build())
- .build();
- }
-
- private Meter createMeter(final Long meterIdValue) {
- return new MeterBuilder()
- .setMeterId(new MeterId(meterIdValue))
- .build();
- }
-
- private Meter createMeterWithBody(final Long meterIdValue) {
- return new MeterBuilder()
- .setMeterId(new MeterId(meterIdValue))
- .setMeterBandHeaders(new MeterBandHeadersBuilder()
- .setMeterBandHeader(Collections.singletonList(new MeterBandHeaderBuilder()
- .setBandId(new BandId(42L))
- .setBandType(new DropBuilder()
- .setDropRate(43L)
- .build())
- .build()))
- .build())
- .build();
- }
-
- private Group createGroupWithPreconditions(final long groupIdValue, final long... requiredId) {
- final List<Action> actionBag = new ArrayList<>();
- for (long groupIdPrecondition : requiredId) {
- final GroupAction groupAction = new GroupActionBuilder()
- .setGroupId(groupIdPrecondition)
- .build();
- final GroupActionCase groupActionCase = new GroupActionCaseBuilder()
- .setGroupAction(groupAction)
- .build();
- final Action action = new ActionBuilder()
- .setAction(groupActionCase)
- .build();
- actionBag.add(action);
- }
-
- final Bucket bucket = new BucketBuilder()
- .setAction(actionBag)
- .build();
- final Buckets buckets = new BucketsBuilder()
- .setBucket(Collections.singletonList(bucket))
- .build();
-
- return new GroupBuilder()
- .setGroupId(new GroupId(groupIdValue))
- .setBuckets(buckets)
- .build();
- }
-
- @Test
- public void testRemoveRedundantFlows() throws Exception {
- Mockito.when(flowCommitter.remove(Matchers.<InstanceIdentifier<Flow>>any(), flowCaptor.capture(),
- Matchers.same(NODE_IDENT)))
- .thenReturn(RpcResultBuilder.success(new RemoveFlowOutputBuilder().build()).buildFuture());
-
- final Table tableCfg = new TableBuilder()
- .setId((short) 0)
- .setFlow(Arrays.asList(
- createFlow("f1", 1), createFlow("f2", 2)))
- .build();
- final FlowCapableNode config = new FlowCapableNodeBuilder()
- .setTable(Collections.singletonList(tableCfg))
- .build();
-
- final Table tableOpe = new TableBuilder()
- .setId((short) 0)
- .setFlow(Arrays.asList(
- createFlow("f1", 1), createFlow("f2", 2), createFlow("f3", 3), createFlow("f4", 4)))
- .build();
- final FlowCapableNode operational = new FlowCapableNodeBuilder()
- .setTable(Collections.singletonList(tableOpe))
- .build();
-
- final ListenableFuture<RpcResult<Void>> result = reactor.removeRedundantFlows(
- NODE_IDENT, config, operational, counters);
-
- Assert.assertTrue(result.isDone());
- Assert.assertTrue(result.get().isSuccessful());
-
- final List<Flow> flowCaptorAllValues = flowCaptor.getAllValues();
- Assert.assertEquals(2, flowCaptorAllValues.size());
- Assert.assertEquals("f3", flowCaptorAllValues.get(0).getId().getValue());
- Assert.assertEquals("f4", flowCaptorAllValues.get(1).getId().getValue());
-
- final InOrder inOrderFlow = Mockito.inOrder(flowCapableTxService, flowCommitter);
- inOrderFlow.verify(flowCommitter, Mockito.times(2)).remove(Matchers.<InstanceIdentifier<Flow>>any(),
- Matchers.<Flow>any(), Matchers.eq(NODE_IDENT));
- inOrderFlow.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
- inOrderFlow.verifyNoMoreInteractions();
- }
-
- @Test
- public void testAddMissingFlows() throws Exception {
- Mockito.when(flowCommitter.add(Matchers.<InstanceIdentifier<Flow>>any(), flowCaptor.capture(),
- Matchers.same(NODE_IDENT)))
- .thenReturn(RpcResultBuilder.success(new AddFlowOutputBuilder().build()).buildFuture());
-
- final Table tableCfg = new TableBuilder()
- .setId((short) 0)
- .setFlow(Arrays.asList(
- createFlow("f1", 1), createFlow("f2", 2), createFlow("f3", 3), createFlow("f4", 4)))
- .build();
- final FlowCapableNode config = new FlowCapableNodeBuilder()
- .setTable(Collections.singletonList(tableCfg))
- .build();
-
- final Table tableOpe = new TableBuilder()
- .setId((short) 0)
- .setFlow(Arrays.asList(
- createFlow("f1", 1), createFlow("f2", 2)))
- .build();
- final FlowCapableNode operational = new FlowCapableNodeBuilder()
- .setTable(Collections.singletonList(tableOpe))
- .build();
-
- final ListenableFuture<RpcResult<Void>> result = reactor.addMissingFlows(
- NODE_IDENT, config, operational, counters);
-
- Assert.assertTrue(result.isDone());
- Assert.assertTrue(result.get().isSuccessful());
-
- final List<Flow> flowCaptorAllValues = flowCaptor.getAllValues();
- Assert.assertEquals(2, flowCaptorAllValues.size());
- Assert.assertEquals("f3", flowCaptorAllValues.get(0).getId().getValue());
- Assert.assertEquals("f4", flowCaptorAllValues.get(1).getId().getValue());
-
- final InOrder inOrderFlow = Mockito.inOrder(flowCapableTxService, flowCommitter);
- inOrderFlow.verify(flowCommitter, Mockito.times(2)).add(Matchers.<InstanceIdentifier<Flow>>any(),
- Matchers.<Flow>any(), Matchers.eq(NODE_IDENT));
- //inOrderFlow.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
- inOrderFlow.verifyNoMoreInteractions();
- }
-
- @Test
- public void testAddMissingFlows_withUpdate() throws Exception {
- Mockito.when(flowCommitter.add(Matchers.<InstanceIdentifier<Flow>>any(), flowCaptor.capture(),
- Matchers.same(NODE_IDENT)))
- .thenReturn(RpcResultBuilder.success(new AddFlowOutputBuilder().build()).buildFuture());
-
- Mockito.when(flowCommitter.update(Matchers.<InstanceIdentifier<Flow>>any(),
- flowUpdateCaptor.capture(), flowUpdateCaptor.capture(),
- Matchers.same(NODE_IDENT)))
- .thenReturn(RpcResultBuilder.success(new UpdateFlowOutputBuilder().build()).buildFuture());
-
- final Table tableCfg = new TableBuilder()
- .setId((short) 0)
- .setFlow(Arrays.asList(
- createFlowWithInstruction("f1", 1), createFlow("f2", 2),
- createFlow("f3", 3), createFlow("f4", 4)))
- .build();
- final FlowCapableNode config = new FlowCapableNodeBuilder()
- .setTable(Collections.singletonList(tableCfg))
- .build();
-
- final Table tableOpe = new TableBuilder()
- .setId((short) 0)
- .setFlow(Arrays.asList(
- createFlow("f1", 1), createFlow("f2", 2)))
- .build();
- final FlowCapableNode operational = new FlowCapableNodeBuilder()
- .setTable(Collections.singletonList(tableOpe))
- .build();
-
- final ListenableFuture<RpcResult<Void>> result = reactor.addMissingFlows(
- NODE_IDENT, config, operational, counters);
-
- Assert.assertTrue(result.isDone());
- Assert.assertTrue(result.get().isSuccessful());
-
- final List<Flow> flowCaptorAllValues = flowCaptor.getAllValues();
- Assert.assertEquals(2, flowCaptorAllValues.size());
- Assert.assertEquals("f3", flowCaptorAllValues.get(0).getId().getValue());
- Assert.assertEquals("f4", flowCaptorAllValues.get(1).getId().getValue());
-
- final List<Flow> flowUpdateCaptorAllValues = flowUpdateCaptor.getAllValues();
- Assert.assertEquals(2, flowUpdateCaptorAllValues.size());
- Assert.assertEquals("f1", flowUpdateCaptorAllValues.get(0).getId().getValue());
- Assert.assertEquals("f1", flowUpdateCaptorAllValues.get(1).getId().getValue());
-
- final InOrder inOrderFlow = Mockito.inOrder(flowCapableTxService, flowCommitter);
- // update f1
- inOrderFlow.verify(flowCommitter).update(Matchers.<InstanceIdentifier<Flow>>any(),
- Matchers.<Flow>any(), Matchers.<Flow>any(), Matchers.eq(NODE_IDENT));
- // add f3, f4
- inOrderFlow.verify(flowCommitter, Mockito.times(2)).add(Matchers.<InstanceIdentifier<Flow>>any(),
- Matchers.<Flow>any(), Matchers.eq(NODE_IDENT));
- //inOrderFlow.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
-
- inOrderFlow.verifyNoMoreInteractions();
- }
-
- @Test
- public void testAddMissingMeters() throws Exception {
- Mockito.when(meterCommitter.add(Matchers.<InstanceIdentifier<Meter>>any(), meterCaptor.capture(),
- Matchers.same(NODE_IDENT)))
- .thenReturn(RpcResultBuilder.success(new AddMeterOutputBuilder().build()).buildFuture());
-
- final FlowCapableNode config = new FlowCapableNodeBuilder()
- .setMeter(Arrays.asList(
- createMeter(1L), createMeter(2L), createMeter(3L), createMeter(4L)
- ))
- .build();
-
- final FlowCapableNode operational = new FlowCapableNodeBuilder()
- .setMeter(Arrays.asList(
- createMeter(1L), createMeter(3L)
- ))
- .build();
-
- final ListenableFuture<RpcResult<Void>> result = reactor.addMissingMeters(
- NODE_IDENT, config, operational, counters);
-
- Assert.assertTrue(result.isDone());
- Assert.assertTrue(result.get().isSuccessful());
-
- final List<Meter> metercaptorAllValues = meterCaptor.getAllValues();
- Assert.assertEquals(2, metercaptorAllValues.size());
- Assert.assertEquals(2L, metercaptorAllValues.get(0).getMeterId().getValue().longValue());
- Assert.assertEquals(4L, metercaptorAllValues.get(1).getMeterId().getValue().longValue());
-
- final InOrder inOrderMeter = Mockito.inOrder(flowCapableTxService, meterCommitter);
- inOrderMeter.verify(meterCommitter, Mockito.times(2)).add(Matchers.<InstanceIdentifier<Meter>>any(),
- Matchers.<Meter>any(), Matchers.eq(NODE_IDENT));
- //inOrderMeter.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
- inOrderMeter.verifyNoMoreInteractions();
- }
-
- @Test
- public void testAddMissingMeters_withUpdate() throws Exception {
- Mockito.when(meterCommitter.add(Matchers.<InstanceIdentifier<Meter>>any(), meterCaptor.capture(),
- Matchers.same(NODE_IDENT)))
- .thenReturn(RpcResultBuilder.success(new AddMeterOutputBuilder().build()).buildFuture());
-
- Mockito.when(meterCommitter.update(Matchers.<InstanceIdentifier<Meter>>any(),
- meterUpdateCaptor.capture(), meterUpdateCaptor.capture(), Matchers.same(NODE_IDENT)))
- .thenReturn(RpcResultBuilder.success(new UpdateMeterOutputBuilder().build()).buildFuture());
-
- final FlowCapableNode config = new FlowCapableNodeBuilder()
- .setMeter(Arrays.asList(
- createMeterWithBody(1L), createMeter(2L), createMeter(3L), createMeter(4L)
- ))
- .build();
-
- final FlowCapableNode operational = new FlowCapableNodeBuilder()
- .setMeter(Arrays.asList(
- createMeter(1L), createMeter(3L)
- ))
- .build();
-
- final ListenableFuture<RpcResult<Void>> result = reactor.addMissingMeters(
- NODE_IDENT, config, operational, counters);
-
- Assert.assertTrue(result.isDone());
- Assert.assertTrue(result.get().isSuccessful());
-
- final List<Meter> meterCaptorAllValues = meterCaptor.getAllValues();
- Assert.assertEquals(2, meterCaptorAllValues.size());
- Assert.assertEquals(2L, meterCaptorAllValues.get(0).getMeterId().getValue().longValue());
- Assert.assertEquals(4L, meterCaptorAllValues.get(1).getMeterId().getValue().longValue());
-
-
- final List<Meter> meterUpdateCaptorAllValues = meterUpdateCaptor.getAllValues();
- Assert.assertEquals(2, meterUpdateCaptorAllValues.size());
- Assert.assertEquals(1L, meterUpdateCaptorAllValues.get(0).getMeterId().getValue().longValue());
- Assert.assertEquals(1L, meterUpdateCaptorAllValues.get(1).getMeterId().getValue().longValue());
-
- final InOrder inOrderMeters = Mockito.inOrder(flowCapableTxService, meterCommitter);
- inOrderMeters.verify(meterCommitter).update(Matchers.<InstanceIdentifier<Meter>>any(),
- Matchers.<Meter>any(), Matchers.<Meter>any(), Matchers.eq(NODE_IDENT));
- inOrderMeters.verify(meterCommitter, Mockito.times(2)).add(Matchers.<InstanceIdentifier<Meter>>any(),
- Matchers.<Meter>any(), Matchers.eq(NODE_IDENT));
- //inOrderMeters.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
-
- inOrderMeters.verifyNoMoreInteractions();
- }
-
- @Test
- public void testRemoveRedundantMeters() throws Exception {
- Mockito.when(meterCommitter.remove(Matchers.<InstanceIdentifier<Meter>>any(), meterCaptor.capture(),
- Matchers.same(NODE_IDENT)))
- .thenReturn(RpcResultBuilder.success(new RemoveMeterOutputBuilder().build()).buildFuture());
-
- final FlowCapableNode config = new FlowCapableNodeBuilder()
- .setMeter(Arrays.asList(
- createMeter(1L), createMeter(3L)
- ))
- .build();
-
- final FlowCapableNode operational = new FlowCapableNodeBuilder()
- .setMeter(Arrays.asList(
- createMeter(1L), createMeter(2L), createMeter(3L), createMeter(4L)
- ))
- .build();
-
- final ListenableFuture<RpcResult<Void>> result = reactor.removeRedundantMeters(
- NODE_IDENT, config, operational, counters);
-
- Assert.assertTrue(result.isDone());
- Assert.assertTrue(result.get().isSuccessful());
-
- final List<Meter> metercaptorAllValues = meterCaptor.getAllValues();
- Assert.assertEquals(2, metercaptorAllValues.size());
- Assert.assertEquals(2L, metercaptorAllValues.get(0).getMeterId().getValue().longValue());
- Assert.assertEquals(4L, metercaptorAllValues.get(1).getMeterId().getValue().longValue());
-
- final InOrder inOrderMeter = Mockito.inOrder(flowCapableTxService, meterCommitter);
- inOrderMeter.verify(meterCommitter, Mockito.times(2)).remove(Matchers.<InstanceIdentifier<Meter>>any(),
- Matchers.<Meter>any(), Matchers.eq(NODE_IDENT));
- //inOrderMeter.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
- inOrderMeter.verifyNoMoreInteractions();
- }
-
- @Test
- public void testAddMissingGroups() throws Exception {
- Mockito.when(groupCommitter.add(Matchers.<InstanceIdentifier<Group>>any(), groupCaptor.capture(),
- Matchers.same(NODE_IDENT)))
- .thenReturn(RpcResultBuilder.success(new AddGroupOutputBuilder().build()).buildFuture());
-
- final FlowCapableNode config = new FlowCapableNodeBuilder()
- .setGroup(Arrays.asList(
- createGroup(1L), createGroup(2L),
- createGroupWithPreconditions(3L, 2L),
- createGroupWithPreconditions(4L, 2L),
- createGroupWithPreconditions(5L, 3L, 4L)))
- .build();
- final FlowCapableNode operational = new FlowCapableNodeBuilder()
- .setGroup(Collections.singletonList(createGroup(1L)))
- .build();
-
- final ListenableFuture<RpcResult<Void>> result = reactor.addMissingGroups(
- NODE_IDENT, config, operational, counters);
-
- Assert.assertTrue(result.isDone());
- Assert.assertTrue(result.get().isSuccessful());
-
- final List<Group> groupCaptorAllValues = groupCaptor.getAllValues();
- Assert.assertEquals(4, groupCaptorAllValues.size());
- Assert.assertEquals(2L, groupCaptorAllValues.get(0).getGroupId().getValue().longValue());
- Assert.assertEquals(3L, groupCaptorAllValues.get(1).getGroupId().getValue().longValue());
- Assert.assertEquals(4L, groupCaptorAllValues.get(2).getGroupId().getValue().longValue());
- Assert.assertEquals(5L, groupCaptorAllValues.get(3).getGroupId().getValue().longValue());
-
- final InOrder inOrderGroups = Mockito.inOrder(flowCapableTxService, groupCommitter);
- // add 2
- inOrderGroups.verify(groupCommitter).add(Matchers.<InstanceIdentifier<Group>>any(),
- Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
- inOrderGroups.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
- // add 3, 4
- inOrderGroups.verify(groupCommitter, Mockito.times(2)).add(Matchers.<InstanceIdentifier<Group>>any(),
- Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
- inOrderGroups.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
- // add 5
- inOrderGroups.verify(groupCommitter).add(Matchers.<InstanceIdentifier<Group>>any(),
- Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
- inOrderGroups.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
-
- inOrderGroups.verifyNoMoreInteractions();
- }
-
- @Test
- public void testAddMissingGroups_withUpdate() throws Exception {
- Mockito.when(groupCommitter.add(Matchers.<InstanceIdentifier<Group>>any(), groupCaptor.capture(),
- Matchers.same(NODE_IDENT)))
- .thenReturn(RpcResultBuilder.success(new AddGroupOutputBuilder().build()).buildFuture());
-
- Mockito.when(groupCommitter.update(Matchers.<InstanceIdentifier<Group>>any(),
- groupUpdateCaptor.capture(), groupUpdateCaptor.capture(),
- Matchers.same(NODE_IDENT)))
- .thenReturn(RpcResultBuilder.success(new UpdateGroupOutputBuilder().build()).buildFuture());
-
- final FlowCapableNode config = new FlowCapableNodeBuilder()
- .setGroup(Arrays.asList(
- createGroupWithAction(1L), createGroup(2L),
- createGroupWithPreconditions(3L, 2L),
- createGroupWithPreconditions(4L, 2L),
- createGroupWithPreconditions(5L, 3L, 4L)))
- .build();
- final FlowCapableNode operational = new FlowCapableNodeBuilder()
- .setGroup(Collections.singletonList(createGroup(1L)))
- .build();
-
- final ListenableFuture<RpcResult<Void>> result = reactor.addMissingGroups(
- NODE_IDENT, config, operational, counters);
-
- Assert.assertTrue(result.isDone());
- Assert.assertTrue(result.get().isSuccessful());
-
- final List<Group> groupCaptorAllValues = groupCaptor.getAllValues();
- Assert.assertEquals(4, groupCaptorAllValues.size());
- Assert.assertEquals(2L, groupCaptorAllValues.get(0).getGroupId().getValue().longValue());
- Assert.assertEquals(3L, groupCaptorAllValues.get(1).getGroupId().getValue().longValue());
- Assert.assertEquals(4L, groupCaptorAllValues.get(2).getGroupId().getValue().longValue());
- Assert.assertEquals(5L, groupCaptorAllValues.get(3).getGroupId().getValue().longValue());
-
- final List<Group> groupUpdateCaptorAllValues = groupUpdateCaptor.getAllValues();
- Assert.assertEquals(2, groupUpdateCaptorAllValues.size());
- Assert.assertEquals(1L, groupUpdateCaptorAllValues.get(0).getGroupId().getValue().longValue());
- Assert.assertEquals(1L, groupUpdateCaptorAllValues.get(1).getGroupId().getValue().longValue());
-
- final InOrder inOrderGroups = Mockito.inOrder(flowCapableTxService, groupCommitter);
-
- // add 2, update 1
- inOrderGroups.verify(groupCommitter).add(Matchers.<InstanceIdentifier<Group>>any(),
- Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
- inOrderGroups.verify(groupCommitter).update(Matchers.<InstanceIdentifier<Group>>any(),
- Matchers.<Group>any(), Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
- inOrderGroups.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
-
- // add 3, 4
- inOrderGroups.verify(groupCommitter, Mockito.times(2)).add(Matchers.<InstanceIdentifier<Group>>any(),
- Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
- inOrderGroups.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
- // add 5
- inOrderGroups.verify(groupCommitter).add(Matchers.<InstanceIdentifier<Group>>any(),
- Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
- inOrderGroups.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
-
- inOrderGroups.verifyNoMoreInteractions();
- }
-
- @Test
- public void testRemoveRedundantGroups() throws Exception {
- Mockito.when(groupCommitter.remove(Matchers.<InstanceIdentifier<Group>>any(), groupCaptor.capture(),
- Matchers.same(NODE_IDENT)))
- .thenReturn(RpcResultBuilder.success(new RemoveGroupOutputBuilder().build()).buildFuture());
-
- final FlowCapableNode operational = new FlowCapableNodeBuilder()
- .setGroup(new ArrayList<>(Arrays.asList(
- createGroup(1L), createGroup(2L),
- createGroupWithPreconditions(3L, 2L),
- createGroupWithPreconditions(4L, 2L),
- createGroupWithPreconditions(5L, 3L, 4L))))
- .build();
- final FlowCapableNode config = new FlowCapableNodeBuilder()
- .setGroup(Collections.singletonList(createGroup(1L)))
- .build();
-
- final ListenableFuture<RpcResult<Void>> result = reactor.removeRedundantGroups(
- NODE_IDENT, config, operational, counters);
-
- Assert.assertTrue(result.isDone());
- Assert.assertTrue(result.get().isSuccessful());
-
- final List<Group> groupCaptorAllValues = groupCaptor.getAllValues();
- Assert.assertEquals(4, groupCaptorAllValues.size());
- Assert.assertEquals(5L, groupCaptorAllValues.get(0).getGroupId().getValue().longValue());
- Assert.assertEquals(3L, groupCaptorAllValues.get(1).getGroupId().getValue().longValue());
- Assert.assertEquals(4L, groupCaptorAllValues.get(2).getGroupId().getValue().longValue());
- Assert.assertEquals(2L, groupCaptorAllValues.get(3).getGroupId().getValue().longValue());
-
- final InOrder inOrderGroup = Mockito.inOrder(flowCapableTxService, groupCommitter);
- // remove 5
- inOrderGroup.verify(groupCommitter).remove(Matchers.<InstanceIdentifier<Group>>any(),
- Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
- inOrderGroup.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
- // remove 3, 4
- inOrderGroup.verify(groupCommitter, Mockito.times(2)).remove(Matchers.<InstanceIdentifier<Group>>any(),
- Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
- inOrderGroup.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
- // remove 2
- inOrderGroup.verify(groupCommitter).remove(Matchers.<InstanceIdentifier<Group>>any(),
- Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
- inOrderGroup.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
-
- inOrderGroup.verifyNoMoreInteractions();
+ reactor = new SyncReactorImpl(syncPlanPushStrategy);
}
@Test
public void testSyncup() throws Exception {
- final FlowCapableNode configFcn = new FlowCapableNodeBuilder()
- .setGroup(Collections.singletonList(createGroup(1L)))
- .setTable(Collections.singletonList(new TableBuilder()
- .setFlow(Collections.singletonList(createFlow("f1", 1)))
- .build()))
- .setMeter(Collections.singletonList(createMeter(1L)))
- .build();
-
- final FlowCapableNode operationalFcn = new FlowCapableNodeBuilder()
- .setGroup(Collections.singletonList(createGroup(2L)))
- .setTable(Collections.singletonList(new TableBuilder()
- .setFlow(Collections.singletonList(createFlow("f2", 2)))
- .build()))
- .setMeter(Collections.singletonList(createMeter(2L)))
- .build();
-
- final ListenableFuture<Boolean> syncupResult = reactor.syncup(NODE_IDENT, configFcn, operationalFcn);
- try {
- final Boolean voidRpcResult = syncupResult.get(2, TimeUnit.SECONDS);
- Assert.assertTrue(voidRpcResult);
-
- final InOrder inOrder = Mockito.inOrder(flowCommitter, meterCommitter, groupCommitter,
- tableCommitter, flowCapableTxService);
-
- inOrder.verify(groupCommitter).add(Matchers.<InstanceIdentifier<Group>>any(),
- Matchers.<Group>any(), Matchers.same(NODE_IDENT));
- inOrder.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
- inOrder.verify(meterCommitter).add(Matchers.<InstanceIdentifier<Meter>>any(),
- Matchers.<Meter>any(), Matchers.same(NODE_IDENT));
- //inOrder.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
- inOrder.verify(flowCommitter).add(Matchers.<InstanceIdentifier<Flow>>any(),
- Matchers.<Flow>any(), Matchers.same(NODE_IDENT));
- //inOrder.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
-
- inOrder.verify(flowCommitter).remove(Matchers.<InstanceIdentifier<Flow>>any(),
- Matchers.<Flow>any(), Matchers.same(NODE_IDENT));
- inOrder.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
- inOrder.verify(meterCommitter).remove(Matchers.<InstanceIdentifier<Meter>>any(),
- Matchers.<Meter>any(), Matchers.same(NODE_IDENT));
- //inOrder.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
- inOrder.verify(groupCommitter).remove(Matchers.<InstanceIdentifier<Group>>any(),
- Matchers.<Group>any(), Matchers.same(NODE_IDENT));
- inOrder.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
-
- inOrder.verifyNoMoreInteractions();
-
- } catch (Exception e) {
- LOG.warn("syncup failed", e);
- Assert.fail("syncup failed: " + e.getMessage());
- }
- }
-
- @Test
- public void testUpdateTableFeatures() throws Exception {
- Mockito.when(tableCommitter.update(Matchers.<InstanceIdentifier<TableFeatures>>any(),
- Matchers.isNull(TableFeatures.class), tableFeaturesCaptor.capture(),
- Matchers.same(NODE_IDENT)))
- .thenReturn(RpcResultBuilder.success(new UpdateTableOutputBuilder().build()).buildFuture());
-
- final FlowCapableNode operational = new FlowCapableNodeBuilder()
- .setTable(Collections.singletonList(new TableBuilder()
- .setId((short) 1)
- .build()))
- .setTableFeatures(Collections.singletonList(new TableFeaturesBuilder()
- .setName("test table features")
- .setTableId((short) 1)
- .build()))
- .build();
-
- final ListenableFuture<RpcResult<Void>> result = reactor.updateTableFeatures(
- NODE_IDENT, operational);
-
- Assert.assertTrue(result.isDone());
- Assert.assertTrue(result.get().isSuccessful());
-
- final List<TableFeatures> groupCaptorAllValues = tableFeaturesCaptor.getAllValues();
- Assert.assertEquals(0, groupCaptorAllValues.size());
- //Assert.assertEquals("test table features", groupCaptorAllValues.get(0).getName());
- //Assert.assertEquals(1, groupCaptorAllValues.get(0).getTableId().intValue());
-
- Mockito.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ // TODO: add test body as soon as strategies are settled
}
}
\ No newline at end of file
--- /dev/null
+/**
+ * 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.applications.frsync.impl.strategy;
+
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InOrder;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.mockito.stubbing.Answer;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.openflowplugin.applications.frsync.impl.FlowForwarder;
+import org.opendaylight.openflowplugin.applications.frsync.impl.GroupForwarder;
+import org.opendaylight.openflowplugin.applications.frsync.impl.MeterForwarder;
+import org.opendaylight.openflowplugin.applications.frsync.impl.TableForwarder;
+import org.opendaylight.openflowplugin.applications.frsync.util.ItemSyncBox;
+import org.opendaylight.openflowplugin.applications.frsync.util.SyncCrudCounters;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.GroupActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.GroupActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.group.action._case.GroupAction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.group.action._case.GroupActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.output.action._case.OutputActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.MeterBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.FlowCapableTransactionService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.SendBarrierInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.RemoveGroupOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.UpdateGroupOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.Buckets;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.BucketsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.Bucket;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.BucketBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.AddMeterOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.RemoveMeterOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.UpdateMeterOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.BandId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.band.type.band.type.DropBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.MeterBandHeadersBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.meter.band.headers.MeterBandHeaderBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.service.rev131026.UpdateTableOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeatures;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeaturesBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Future;
+
+/**
+ * Test for {@link SyncPlanPushStrategyIncrementalImpl}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class SyncPlanPushStrategyIncrementalImplTest {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SyncPlanPushStrategyIncrementalImplTest.class);
+
+ private static final NodeId NODE_ID = new NodeId("unit-nodeId");
+ private static final InstanceIdentifier<FlowCapableNode> NODE_IDENT = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class, new NodeKey(NODE_ID)).augmentation(FlowCapableNode.class);
+
+ private SyncPlanPushStrategyIncrementalImpl syncPlanPushStrategy;
+
+ @Mock
+ private DataBroker db;
+ @Mock
+ private GroupForwarder groupCommitter;
+ @Mock
+ private FlowForwarder flowCommitter;
+ @Mock
+ private MeterForwarder meterCommitter;
+ @Mock
+ private TableForwarder tableCommitter;
+ @Mock
+ private FlowCapableTransactionService flowCapableTxService;
+
+ @Captor
+ private ArgumentCaptor<Group> groupCaptor;
+ @Captor
+ private ArgumentCaptor<Group> groupUpdateCaptor;
+ @Captor
+ private ArgumentCaptor<Flow> flowCaptor;
+ @Captor
+ private ArgumentCaptor<Flow> flowUpdateCaptor;
+ @Captor
+ private ArgumentCaptor<Meter> meterCaptor;
+ @Captor
+ private ArgumentCaptor<Meter> meterUpdateCaptor;
+ @Captor
+ private ArgumentCaptor<TableFeatures> tableFeaturesCaptor;
+
+ private SyncCrudCounters counters;
+
+ @Test
+ public void testExecuteSyncStrategy() throws Exception {
+
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ Mockito.when(flowCapableTxService.sendBarrier(Matchers.<SendBarrierInput>any()))
+ .thenReturn(RpcResultBuilder.success((Void) null).buildFuture());
+
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(groupCommitter).add(
+ Matchers.<InstanceIdentifier<Group>>any(), Matchers.<Group>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(groupCommitter).update(
+ Matchers.<InstanceIdentifier<Group>>any(), Matchers.<Group>any(), Matchers.<Group>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(groupCommitter).remove(
+ Matchers.<InstanceIdentifier<Group>>any(), Matchers.<Group>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(flowCommitter).add(
+ Matchers.<InstanceIdentifier<Flow>>any(), Matchers.<Flow>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(flowCommitter).update(
+ Matchers.<InstanceIdentifier<Flow>>any(), Matchers.<Flow>any(), Matchers.<Flow>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(flowCommitter).remove(
+ Matchers.<InstanceIdentifier<Flow>>any(), Matchers.<Flow>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(meterCommitter).add(
+ Matchers.<InstanceIdentifier<Meter>>any(), Matchers.<Meter>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(meterCommitter).update(
+ Matchers.<InstanceIdentifier<Meter>>any(), Matchers.<Meter>any(), Matchers.<Meter>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(meterCommitter).remove(
+ Matchers.<InstanceIdentifier<Meter>>any(), Matchers.<Meter>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(tableCommitter).update(
+ Matchers.<InstanceIdentifier<TableFeatures>>any(), Matchers.<TableFeatures>any(), Matchers.<TableFeatures>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+
+ syncPlanPushStrategy = new SyncPlanPushStrategyIncrementalImpl()
+ .setMeterForwarder(meterCommitter)
+ .setTableForwarder(tableCommitter)
+ .setGroupForwarder(groupCommitter)
+ .setFlowForwarder(flowCommitter)
+ .setTransactionService(flowCapableTxService);
+
+ counters = new SyncCrudCounters();
+ }
+
+ private <O> Answer<Future<RpcResult<O>>> createSalServiceFutureAnswer() {
+ return new Answer<Future<RpcResult<O>>>() {
+ @Override
+ public Future<RpcResult<O>> answer(final InvocationOnMock invocation) throws Throwable {
+ return RpcResultBuilder.<O>success().buildFuture();
+ }
+ };
+ }
+
+ private Group createGroup(final long groupIdValue) {
+ final Buckets buckets = new BucketsBuilder()
+ .setBucket(Collections.<Bucket>emptyList())
+ .build();
+ return new GroupBuilder()
+ .setGroupId(new GroupId(groupIdValue))
+ .setBuckets(buckets)
+ .build();
+ }
+
+ private Group createGroupWithAction(final long groupIdValue) {
+ final Buckets buckets = new BucketsBuilder()
+ .setBucket(Collections.singletonList(new BucketBuilder()
+ .setAction(Collections.singletonList(new ActionBuilder()
+ .setAction(new OutputActionCaseBuilder()
+ .setOutputAction(new OutputActionBuilder()
+ .setOutputNodeConnector(new Uri("ut-port-1"))
+ .build())
+ .build())
+ .build()))
+ .build()))
+ .build();
+ return new GroupBuilder()
+ .setGroupId(new GroupId(groupIdValue))
+ .setBuckets(buckets)
+ .build();
+ }
+
+ private Flow createFlow(final String flowIdValue, final int priority) {
+ return new FlowBuilder()
+ .setId(new FlowId(flowIdValue))
+ .setPriority(priority)
+ .setTableId((short) 42)
+ .setMatch(new MatchBuilder().build())
+ .build();
+ }
+
+ private Flow createFlowWithInstruction(final String flowIdValue, final int priority) {
+ return new FlowBuilder()
+ .setId(new FlowId(flowIdValue))
+ .setPriority(priority)
+ .setTableId((short) 42)
+ .setMatch(new MatchBuilder().build())
+ .setInstructions(new InstructionsBuilder()
+ .setInstruction(Collections.singletonList(new InstructionBuilder()
+ .setInstruction(new ApplyActionsCaseBuilder()
+ .setApplyActions(new ApplyActionsBuilder()
+ .setAction(Collections.singletonList(new ActionBuilder()
+ .setAction(new OutputActionCaseBuilder()
+ .setOutputAction(new OutputActionBuilder()
+ .setOutputNodeConnector(new Uri("ut-port-1"))
+ .build())
+ .build())
+ .build()))
+ .build())
+ .build())
+ .build()))
+ .build())
+ .build();
+ }
+
+ private Meter createMeter(final Long meterIdValue) {
+ return new MeterBuilder()
+ .setMeterId(new MeterId(meterIdValue))
+ .build();
+ }
+
+ private Meter createMeterWithBody(final Long meterIdValue) {
+ return new MeterBuilder()
+ .setMeterId(new MeterId(meterIdValue))
+ .setMeterBandHeaders(new MeterBandHeadersBuilder()
+ .setMeterBandHeader(Collections.singletonList(new MeterBandHeaderBuilder()
+ .setBandId(new BandId(42L))
+ .setBandType(new DropBuilder()
+ .setDropRate(43L)
+ .build())
+ .build()))
+ .build())
+ .build();
+ }
+
+ private Group createGroupWithPreconditions(final long groupIdValue, final long... requiredId) {
+ final List<Action> actionBag = new ArrayList<>();
+ for (long groupIdPrecondition : requiredId) {
+ final GroupAction groupAction = new GroupActionBuilder()
+ .setGroupId(groupIdPrecondition)
+ .build();
+ final GroupActionCase groupActionCase = new GroupActionCaseBuilder()
+ .setGroupAction(groupAction)
+ .build();
+ final Action action = new ActionBuilder()
+ .setAction(groupActionCase)
+ .build();
+ actionBag.add(action);
+ }
+
+ final Bucket bucket = new BucketBuilder()
+ .setAction(actionBag)
+ .build();
+ final Buckets buckets = new BucketsBuilder()
+ .setBucket(Collections.singletonList(bucket))
+ .build();
+
+ return new GroupBuilder()
+ .setGroupId(new GroupId(groupIdValue))
+ .setBuckets(buckets)
+ .build();
+ }
+
+ @Test
+ public void testAddMissingFlows() throws Exception {
+ Mockito.when(flowCommitter.add(Matchers.<InstanceIdentifier<Flow>>any(), flowCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new AddFlowOutputBuilder().build()).buildFuture());
+
+ final ItemSyncBox<Flow> flowBox = new ItemSyncBox<>();
+ flowBox.getItemsToPush().add(createFlow("f3", 3));
+ flowBox.getItemsToPush().add(createFlow("f4", 4));
+
+ final Map<TableKey, ItemSyncBox<Flow>> flowBoxMap = new LinkedHashMap<>();
+ flowBoxMap.put(new TableKey((short) 0), flowBox);
+
+ final ListenableFuture<RpcResult<Void>> result = syncPlanPushStrategy.addMissingFlows(
+ NODE_ID, NODE_IDENT, flowBoxMap, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Flow> flowCaptorAllValues = flowCaptor.getAllValues();
+ Assert.assertEquals(2, flowCaptorAllValues.size());
+ Assert.assertEquals("f3", flowCaptorAllValues.get(0).getId().getValue());
+ Assert.assertEquals("f4", flowCaptorAllValues.get(1).getId().getValue());
+
+ final InOrder inOrderFlow = Mockito.inOrder(flowCapableTxService, flowCommitter);
+ inOrderFlow.verify(flowCommitter, Mockito.times(2)).add(Matchers.<InstanceIdentifier<Flow>>any(),
+ Matchers.<Flow>any(), Matchers.eq(NODE_IDENT));
+ //TODO: uncomment when enabled in impl
+// inOrderFlow.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ inOrderFlow.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testRemoveRedundantFlows() throws Exception {
+ Mockito.when(flowCommitter.remove(Matchers.<InstanceIdentifier<Flow>>any(), flowCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new RemoveFlowOutputBuilder().build()).buildFuture());
+
+ final ItemSyncBox<Flow> flowBox = new ItemSyncBox<>();
+ flowBox.getItemsToPush().add(createFlow("f3", 3));
+ flowBox.getItemsToPush().add(createFlow("f4", 4));
+
+ final Map<TableKey, ItemSyncBox<Flow>> flowBoxMap = new LinkedHashMap<>();
+ flowBoxMap.put(new TableKey((short) 0), flowBox);
+
+ final ListenableFuture<RpcResult<Void>> result = syncPlanPushStrategy.removeRedundantFlows(
+ NODE_ID, NODE_IDENT, flowBoxMap, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Flow> flowCaptorAllValues = flowCaptor.getAllValues();
+ Assert.assertEquals(2, flowCaptorAllValues.size());
+ Assert.assertEquals("f3", flowCaptorAllValues.get(0).getId().getValue());
+ Assert.assertEquals("f4", flowCaptorAllValues.get(1).getId().getValue());
+
+ final InOrder inOrderFlow = Mockito.inOrder(flowCapableTxService, flowCommitter);
+ inOrderFlow.verify(flowCommitter, Mockito.times(2)).remove(Matchers.<InstanceIdentifier<Flow>>any(),
+ Matchers.<Flow>any(), Matchers.eq(NODE_IDENT));
+ inOrderFlow.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ inOrderFlow.verifyNoMoreInteractions();
+ }
+
+
+ @Test
+ public void testAddMissingFlows_withUpdate() throws Exception {
+ Mockito.when(flowCommitter.add(Matchers.<InstanceIdentifier<Flow>>any(), flowCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new AddFlowOutputBuilder().build()).buildFuture());
+
+ Mockito.when(flowCommitter.update(Matchers.<InstanceIdentifier<Flow>>any(),
+ flowUpdateCaptor.capture(), flowUpdateCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new UpdateFlowOutputBuilder().build()).buildFuture());
+
+ final ItemSyncBox<Flow> flowBox = new ItemSyncBox<>();
+ flowBox.getItemsToPush().add(createFlow("f3", 3));
+ flowBox.getItemsToPush().add(createFlow("f4", 4));
+ flowBox.getItemsToUpdate().add(new ItemSyncBox.ItemUpdateTuple<>(
+ createFlow("f1", 1), createFlowWithInstruction("f1", 1)));
+
+ final Map<TableKey, ItemSyncBox<Flow>> flowBoxMap = new LinkedHashMap<>();
+ flowBoxMap.put(new TableKey((short) 0), flowBox);
+
+
+ //TODO: replace null
+ final ListenableFuture<RpcResult<Void>> result = syncPlanPushStrategy.addMissingFlows(
+ NODE_ID, NODE_IDENT, flowBoxMap, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Flow> flowCaptorAllValues = flowCaptor.getAllValues();
+ Assert.assertEquals(2, flowCaptorAllValues.size());
+ Assert.assertEquals("f3", flowCaptorAllValues.get(0).getId().getValue());
+ Assert.assertEquals("f4", flowCaptorAllValues.get(1).getId().getValue());
+
+ final List<Flow> flowUpdateCaptorAllValues = flowUpdateCaptor.getAllValues();
+ Assert.assertEquals(2, flowUpdateCaptorAllValues.size());
+ Assert.assertEquals("f1", flowUpdateCaptorAllValues.get(0).getId().getValue());
+ Assert.assertEquals("f1", flowUpdateCaptorAllValues.get(1).getId().getValue());
+
+ final InOrder inOrderFlow = Mockito.inOrder(flowCapableTxService, flowCommitter);
+ // add f3, f4
+ inOrderFlow.verify(flowCommitter, Mockito.times(2)).add(Matchers.<InstanceIdentifier<Flow>>any(),
+ Matchers.<Flow>any(), Matchers.eq(NODE_IDENT));
+ // update f1
+ inOrderFlow.verify(flowCommitter).update(Matchers.<InstanceIdentifier<Flow>>any(),
+ Matchers.<Flow>any(), Matchers.<Flow>any(), Matchers.eq(NODE_IDENT));
+ //TODO: uncomment when enabled in impl
+// inOrderFlow.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+
+ inOrderFlow.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testAddMissingMeters() throws Exception {
+ Mockito.when(meterCommitter.add(Matchers.<InstanceIdentifier<Meter>>any(), meterCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new AddMeterOutputBuilder().build()).buildFuture());
+
+ final ItemSyncBox<Meter> meterSyncBox = new ItemSyncBox<>();
+ meterSyncBox.getItemsToPush().add(createMeter(2L));
+ meterSyncBox.getItemsToPush().add(createMeter(4L));
+
+ final ListenableFuture<RpcResult<Void>> result = syncPlanPushStrategy.addMissingMeters(
+ NODE_ID, NODE_IDENT, meterSyncBox, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Meter> metercaptorAllValues = meterCaptor.getAllValues();
+ Assert.assertEquals(2, metercaptorAllValues.size());
+ Assert.assertEquals(2L, metercaptorAllValues.get(0).getMeterId().getValue().longValue());
+ Assert.assertEquals(4L, metercaptorAllValues.get(1).getMeterId().getValue().longValue());
+
+ final InOrder inOrderMeter = Mockito.inOrder(flowCapableTxService, meterCommitter);
+ inOrderMeter.verify(meterCommitter, Mockito.times(2)).add(Matchers.<InstanceIdentifier<Meter>>any(),
+ Matchers.<Meter>any(), Matchers.eq(NODE_IDENT));
+ //TODO: uncomment when enabled in impl
+// inOrderMeter.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ inOrderMeter.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testAddMissingMeters_withUpdate() throws Exception {
+ Mockito.when(meterCommitter.add(Matchers.<InstanceIdentifier<Meter>>any(), meterCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new AddMeterOutputBuilder().build()).buildFuture());
+
+ Mockito.when(meterCommitter.update(Matchers.<InstanceIdentifier<Meter>>any(),
+ meterUpdateCaptor.capture(), meterUpdateCaptor.capture(), Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new UpdateMeterOutputBuilder().build()).buildFuture());
+
+ final ItemSyncBox<Meter> meterSyncBox = new ItemSyncBox<>();
+ meterSyncBox.getItemsToPush().add(createMeter(2L));
+ meterSyncBox.getItemsToPush().add(createMeter(4L));
+ meterSyncBox.getItemsToUpdate().add(new ItemSyncBox.ItemUpdateTuple<>(
+ createMeter(1L), createMeterWithBody(1L)));
+
+ final ListenableFuture<RpcResult<Void>> result = syncPlanPushStrategy.addMissingMeters(
+ NODE_ID, NODE_IDENT, meterSyncBox, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Meter> meterCaptorAllValues = meterCaptor.getAllValues();
+ Assert.assertEquals(2, meterCaptorAllValues.size());
+ Assert.assertEquals(2L, meterCaptorAllValues.get(0).getMeterId().getValue().longValue());
+ Assert.assertEquals(4L, meterCaptorAllValues.get(1).getMeterId().getValue().longValue());
+
+
+ final List<Meter> meterUpdateCaptorAllValues = meterUpdateCaptor.getAllValues();
+ Assert.assertEquals(2, meterUpdateCaptorAllValues.size());
+ Assert.assertEquals(1L, meterUpdateCaptorAllValues.get(0).getMeterId().getValue().longValue());
+ Assert.assertEquals(1L, meterUpdateCaptorAllValues.get(1).getMeterId().getValue().longValue());
+
+ final InOrder inOrderMeters = Mockito.inOrder(flowCapableTxService, meterCommitter);
+ inOrderMeters.verify(meterCommitter, Mockito.times(2)).add(Matchers.<InstanceIdentifier<Meter>>any(),
+ Matchers.<Meter>any(), Matchers.eq(NODE_IDENT));
+ inOrderMeters.verify(meterCommitter).update(Matchers.<InstanceIdentifier<Meter>>any(),
+ Matchers.<Meter>any(), Matchers.<Meter>any(), Matchers.eq(NODE_IDENT));
+ //TODO: uncomment when enabled in impl
+// inOrderMeters.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+
+ inOrderMeters.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testRemoveRedundantMeters() throws Exception {
+ Mockito.when(meterCommitter.remove(Matchers.<InstanceIdentifier<Meter>>any(), meterCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new RemoveMeterOutputBuilder().build()).buildFuture());
+
+ final ItemSyncBox<Meter> meterSyncBox = new ItemSyncBox<>();
+ meterSyncBox.getItemsToPush().add(createMeter(2L));
+ meterSyncBox.getItemsToPush().add(createMeter(4L));
+ meterSyncBox.getItemsToUpdate().add(new ItemSyncBox.ItemUpdateTuple<>(
+ createMeter(1L), createMeterWithBody(1L)));
+
+ final ListenableFuture<RpcResult<Void>> result = syncPlanPushStrategy.removeRedundantMeters(
+ NODE_ID, NODE_IDENT, meterSyncBox, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Meter> metercaptorAllValues = meterCaptor.getAllValues();
+ Assert.assertEquals(2, metercaptorAllValues.size());
+ Assert.assertEquals(2L, metercaptorAllValues.get(0).getMeterId().getValue().longValue());
+ Assert.assertEquals(4L, metercaptorAllValues.get(1).getMeterId().getValue().longValue());
+
+ final InOrder inOrderMeter = Mockito.inOrder(flowCapableTxService, meterCommitter);
+ inOrderMeter.verify(meterCommitter, Mockito.times(2)).remove(Matchers.<InstanceIdentifier<Meter>>any(),
+ Matchers.<Meter>any(), Matchers.eq(NODE_IDENT));
+ //TODO: uncomment when enabled in impl
+// inOrderMeter.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ inOrderMeter.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testAddMissingGroups() throws Exception {
+ Mockito.when(groupCommitter.add(Matchers.<InstanceIdentifier<Group>>any(), groupCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new AddGroupOutputBuilder().build()).buildFuture());
+
+ ItemSyncBox<Group> groupBox1 = new ItemSyncBox<>();
+ groupBox1.getItemsToPush().add(createGroup(2L));
+
+ ItemSyncBox<Group> groupBox2 = new ItemSyncBox<>();
+ groupBox2.getItemsToPush().add(createGroupWithPreconditions(3L, 2L));
+ groupBox2.getItemsToPush().add(createGroupWithPreconditions(4L, 2L));
+
+ ItemSyncBox<Group> groupBox3 = new ItemSyncBox<>();
+ groupBox3.getItemsToPush().add(createGroupWithPreconditions(5L, 3L, 4L));
+
+ final List<ItemSyncBox<Group>> groupBoxLot = Lists.newArrayList(groupBox1, groupBox2, groupBox3);
+
+ final ListenableFuture<RpcResult<Void>> result = syncPlanPushStrategy.addMissingGroups(
+ NODE_ID, NODE_IDENT, groupBoxLot, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Group> groupCaptorAllValues = groupCaptor.getAllValues();
+ Assert.assertEquals(4, groupCaptorAllValues.size());
+ Assert.assertEquals(2L, groupCaptorAllValues.get(0).getGroupId().getValue().longValue());
+ Assert.assertEquals(3L, groupCaptorAllValues.get(1).getGroupId().getValue().longValue());
+ Assert.assertEquals(4L, groupCaptorAllValues.get(2).getGroupId().getValue().longValue());
+ Assert.assertEquals(5L, groupCaptorAllValues.get(3).getGroupId().getValue().longValue());
+
+ final InOrder inOrderGroups = Mockito.inOrder(flowCapableTxService, groupCommitter);
+ // add 2
+ inOrderGroups.verify(groupCommitter).add(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroups.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ // add 3, 4
+ inOrderGroups.verify(groupCommitter, Mockito.times(2)).add(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroups.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ // add 5
+ inOrderGroups.verify(groupCommitter).add(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroups.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+
+ inOrderGroups.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testAddMissingGroups_withUpdate() throws Exception {
+ Mockito.when(groupCommitter.add(Matchers.<InstanceIdentifier<Group>>any(), groupCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new AddGroupOutputBuilder().build()).buildFuture());
+
+ Mockito.when(groupCommitter.update(Matchers.<InstanceIdentifier<Group>>any(),
+ groupUpdateCaptor.capture(), groupUpdateCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new UpdateGroupOutputBuilder().build()).buildFuture());
+
+ ItemSyncBox<Group> groupBox1 = new ItemSyncBox<>();
+ groupBox1.getItemsToPush().add(createGroup(2L));
+ groupBox1.getItemsToUpdate().add(new ItemSyncBox.ItemUpdateTuple<>(
+ createGroup(1L), createGroupWithAction(1L)));
+
+ ItemSyncBox<Group> groupBox2 = new ItemSyncBox<>();
+ groupBox2.getItemsToPush().add(createGroupWithPreconditions(3L, 2L));
+ groupBox2.getItemsToPush().add(createGroupWithPreconditions(4L, 2L));
+
+ ItemSyncBox<Group> groupBox3 = new ItemSyncBox<>();
+ groupBox3.getItemsToPush().add(createGroupWithPreconditions(5L, 3L, 4L));
+
+ final List<ItemSyncBox<Group>> groupBoxLot = Lists.newArrayList(groupBox1, groupBox2, groupBox3);
+ final ListenableFuture<RpcResult<Void>> result = syncPlanPushStrategy.addMissingGroups(
+ NODE_ID, NODE_IDENT, groupBoxLot, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Group> groupCaptorAllValues = groupCaptor.getAllValues();
+ Assert.assertEquals(4, groupCaptorAllValues.size());
+ Assert.assertEquals(2L, groupCaptorAllValues.get(0).getGroupId().getValue().longValue());
+ Assert.assertEquals(3L, groupCaptorAllValues.get(1).getGroupId().getValue().longValue());
+ Assert.assertEquals(4L, groupCaptorAllValues.get(2).getGroupId().getValue().longValue());
+ Assert.assertEquals(5L, groupCaptorAllValues.get(3).getGroupId().getValue().longValue());
+
+ final List<Group> groupUpdateCaptorAllValues = groupUpdateCaptor.getAllValues();
+ Assert.assertEquals(2, groupUpdateCaptorAllValues.size());
+ Assert.assertEquals(1L, groupUpdateCaptorAllValues.get(0).getGroupId().getValue().longValue());
+ Assert.assertEquals(1L, groupUpdateCaptorAllValues.get(1).getGroupId().getValue().longValue());
+
+ final InOrder inOrderGroups = Mockito.inOrder(flowCapableTxService, groupCommitter);
+
+ // add 2, update 1
+ inOrderGroups.verify(groupCommitter).add(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroups.verify(groupCommitter).update(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroups.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+
+ // add 3, 4
+ inOrderGroups.verify(groupCommitter, Mockito.times(2)).add(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroups.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ // add 5
+ inOrderGroups.verify(groupCommitter).add(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroups.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+
+ inOrderGroups.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testRemoveRedundantGroups() throws Exception {
+ Mockito.when(groupCommitter.remove(Matchers.<InstanceIdentifier<Group>>any(), groupCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new RemoveGroupOutputBuilder().build()).buildFuture());
+
+ ItemSyncBox<Group> groupBox1 = new ItemSyncBox<>();
+ groupBox1.getItemsToPush().add(createGroup(2L));
+ groupBox1.getItemsToUpdate().add(new ItemSyncBox.ItemUpdateTuple<>(
+ createGroup(1L), createGroupWithAction(1L)));
+
+ ItemSyncBox<Group> groupBox2 = new ItemSyncBox<>();
+ groupBox2.getItemsToPush().add(createGroupWithPreconditions(3L, 2L));
+ groupBox2.getItemsToPush().add(createGroupWithPreconditions(4L, 2L));
+
+ ItemSyncBox<Group> groupBox3 = new ItemSyncBox<>();
+ groupBox3.getItemsToPush().add(createGroupWithPreconditions(5L, 3L, 4L));
+
+ final List<ItemSyncBox<Group>> groupBoxLot = Lists.newArrayList(groupBox1, groupBox2, groupBox3);
+ final ListenableFuture<RpcResult<Void>> result = syncPlanPushStrategy.removeRedundantGroups(
+ NODE_ID, NODE_IDENT, groupBoxLot, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Group> groupCaptorAllValues = groupCaptor.getAllValues();
+ Assert.assertEquals(4, groupCaptorAllValues.size());
+ Assert.assertEquals(5L, groupCaptorAllValues.get(0).getGroupId().getValue().longValue());
+ Assert.assertEquals(3L, groupCaptorAllValues.get(1).getGroupId().getValue().longValue());
+ Assert.assertEquals(4L, groupCaptorAllValues.get(2).getGroupId().getValue().longValue());
+ Assert.assertEquals(2L, groupCaptorAllValues.get(3).getGroupId().getValue().longValue());
+
+ final InOrder inOrderGroup = Mockito.inOrder(flowCapableTxService, groupCommitter);
+ // remove 5
+ inOrderGroup.verify(groupCommitter).remove(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroup.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ // remove 3, 4
+ inOrderGroup.verify(groupCommitter, Mockito.times(2)).remove(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroup.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ // remove 2
+ inOrderGroup.verify(groupCommitter).remove(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroup.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+
+ inOrderGroup.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testUpdateTableFeatures() throws Exception {
+ Mockito.when(tableCommitter.update(Matchers.<InstanceIdentifier<TableFeatures>>any(),
+ Matchers.isNull(TableFeatures.class), tableFeaturesCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new UpdateTableOutputBuilder().build()).buildFuture());
+
+ final FlowCapableNode operational = new FlowCapableNodeBuilder()
+ .setTable(Collections.singletonList(new TableBuilder()
+ .setId((short) 1)
+ .build()))
+ .setTableFeatures(Collections.singletonList(new TableFeaturesBuilder()
+ .setName("test table features")
+ .setTableId((short) 1)
+ .build()))
+ .build();
+
+ final ListenableFuture<RpcResult<Void>> result = syncPlanPushStrategy.updateTableFeatures(
+ NODE_IDENT, operational);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<TableFeatures> groupCaptorAllValues = tableFeaturesCaptor.getAllValues();
+ //TODO: uncomment when enabled in impl
+// Assert.assertEquals(1, groupCaptorAllValues.size());
+// Assert.assertEquals("test table features", groupCaptorAllValues.get(0).getName());
+// Assert.assertEquals(1, groupCaptorAllValues.get(0).getTableId().intValue());
+
+ Mockito.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ }
+}
\ No newline at end of file
package org.opendaylight.openflowplugin.applications.frsync.util;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.SettableFuture;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
/**
* Test for {@link ReconcileUtil}.
* @throws Exception
*/
@Test
- public void testResolveAndDivideGroups1() throws Exception {
+ public void testResolveAndDivideGroupDiffs1() throws Exception {
final Map<Long, Group> installedGroups = new HashMap<>();
installedGroups.put(1L, createGroup(1L));
installedGroups.put(2L, createGroup(2L));
pendingGroups.add(createGroup(3L));
pendingGroups.add(createGroup(4L));
- final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroups(
+ final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroupDiffs(
NODE_ID, installedGroups, pendingGroups);
Assert.assertEquals(1, plan.size());
- Assert.assertEquals(1, plan.get(0).getItemsToAdd().size());
- Assert.assertEquals(4L, plan.get(0).getItemsToAdd().iterator().next().getKey().getGroupId().getValue().longValue());
+ Assert.assertEquals(1, plan.get(0).getItemsToPush().size());
+ Assert.assertEquals(4L, plan.get(0).getItemsToPush().iterator().next().getKey().getGroupId().getValue().longValue());
Assert.assertEquals(0, plan.get(0).getItemsToUpdate().size());
}
* @throws Exception
*/
@Test
- public void testResolveAndDivideGroups2() throws Exception {
+ public void testResolveAndDivideGroupDiffs2() throws Exception {
final Map<Long, Group> installedGroups = new HashMap<>();
installedGroups.put(1L, createGroup(1L));
pendingGroups.add(createGroupWithPreconditions(3L, 2L, 4L));
pendingGroups.add(createGroupWithPreconditions(4L, 2L));
- final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroups(
+ final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroupDiffs(
NODE_ID, installedGroups, pendingGroups);
Assert.assertEquals(3, plan.size());
- Assert.assertEquals(1, plan.get(0).getItemsToAdd().size());
- Assert.assertEquals(2L, plan.get(0).getItemsToAdd().iterator().next().getKey().getGroupId().getValue().longValue());
+ Assert.assertEquals(1, plan.get(0).getItemsToPush().size());
+ Assert.assertEquals(2L, plan.get(0).getItemsToPush().iterator().next().getKey().getGroupId().getValue().longValue());
Assert.assertEquals(0, plan.get(0).getItemsToUpdate().size());
- Assert.assertEquals(1, plan.get(1).getItemsToAdd().size());
- Assert.assertEquals(4L, plan.get(1).getItemsToAdd().iterator().next().getKey().getGroupId().getValue().longValue());
+ Assert.assertEquals(1, plan.get(1).getItemsToPush().size());
+ Assert.assertEquals(4L, plan.get(1).getItemsToPush().iterator().next().getKey().getGroupId().getValue().longValue());
Assert.assertEquals(0, plan.get(1).getItemsToUpdate().size());
- Assert.assertEquals(1, plan.get(2).getItemsToAdd().size());
- Assert.assertEquals(3L, plan.get(2).getItemsToAdd().iterator().next().getKey().getGroupId().getValue().longValue());
+ Assert.assertEquals(1, plan.get(2).getItemsToPush().size());
+ Assert.assertEquals(3L, plan.get(2).getItemsToPush().iterator().next().getKey().getGroupId().getValue().longValue());
Assert.assertEquals(0, plan.get(2).getItemsToUpdate().size());
}
* @throws Exception
*/
@Test
- public void testResolveAndDivideGroups3() throws Exception {
+ public void testResolveAndDivideGroupDiffs3() throws Exception {
final Map<Long, Group> installedGroups = new HashMap<>();
installedGroups.put(1L, createGroup(1L));
installedGroups.put(2L, createGroupWithPreconditions(2L, 1L));
pendingGroups.add(createGroup(1L));
pendingGroups.add(createGroupWithPreconditions(2L, 1L));
- final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroups(
+ final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroupDiffs(
NODE_ID, installedGroups, pendingGroups);
Assert.assertEquals(0, plan.size());
* @throws Exception
*/
@Test
- public void testResolveAndDivideGroups4() throws Exception {
+ public void testResolveAndDivideGroupDiffs4() throws Exception {
final Map<Long, Group> installedGroups = new HashMap<>();
installedGroups.put(1L, createGroup(1L));
installedGroups.put(2L, createGroup(2L));
pendingGroups.add(createGroupWithPreconditions(1L, 2L));
pendingGroups.add(createGroup(2L));
- final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroups(
+ final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroupDiffs(
NODE_ID, installedGroups, pendingGroups);
Assert.assertEquals(1, plan.size());
- Assert.assertEquals(0, plan.get(0).getItemsToAdd().size());
+ Assert.assertEquals(0, plan.get(0).getItemsToPush().size());
Assert.assertEquals(1, plan.get(0).getItemsToUpdate().size());
final ItemSyncBox.ItemUpdateTuple<Group> firstItemUpdateTuple = plan.get(0).getItemsToUpdate().iterator().next();
Assert.assertEquals(1L, firstItemUpdateTuple.getOriginal().getGroupId().getValue().longValue());
* @throws Exception
*/
@Test
- public void testResolveAndDivideGroups5() throws Exception {
+ public void testResolveAndDivideGroupDiffs5() throws Exception {
final Map<Long, Group> installedGroups = new HashMap<>();
installedGroups.put(1L, createGroup(1L));
installedGroups.put(2L, createGroup(2L));
pendingGroups.add(createGroupWithPreconditions(1L, 2L));
pendingGroups.add(createGroup(2L));
- final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroups(
+ final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroupDiffs(
NODE_ID, installedGroups, pendingGroups, false);
Assert.assertEquals(0, plan.size());
* @throws Exception
*/
@Test
- public void testResolveAndDivideGroups_negative1() throws Exception {
+ public void testResolveAndDivideGroupDiffs_negative1() throws Exception {
final Map<Long, Group> installedGroups = new HashMap<>();
installedGroups.put(1L, createGroup(1L));
installedGroups.put(2L, createGroup(2L));
pendingGroups.add(createGroupWithPreconditions(3L, 4L));
thrown.expect(IllegalStateException.class);
- final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroups(
+ final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroupDiffs(
NODE_ID, installedGroups, pendingGroups);
}
* @throws Exception
*/
@Test
- public void testResolveAndDivideGroups_negative2() throws Exception {
+ public void testResolveAndDivideGroupDiffs_negative2() throws Exception {
final Map<Long, Group> installedGroups = new HashMap<>();
installedGroups.put(1L, createGroup(1L));
installedGroups.put(2L, createGroup(2L));
pendingGroups.add(createGroupWithPreconditions(1L, 3L));
thrown.expect(IllegalStateException.class);
- final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroups(
+ final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroupDiffs(
NODE_ID, installedGroups, pendingGroups);
}
.setBuckets(buckets)
.build();
}
+
+ /**
+ * covers {@link ReconcileUtil#countTotalUpdated(List)} too
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testCountTotalAdds() throws Exception {
+ List<ItemSyncBox<String>> syncPlan = new ArrayList<>();
+ ItemSyncBox<String> syncBox1 = createSyncBox("a,b", "x,y,z");
+ syncPlan.add(syncBox1);
+ syncPlan.add(syncBox1);
+ Assert.assertEquals(4, ReconcileUtil.countTotalAdds(syncPlan));
+ Assert.assertEquals(6, ReconcileUtil.countTotalUpdated(syncPlan));
+ }
+
+ private ItemSyncBox<String> createSyncBox(final String pushes, final String updates) {
+ ItemSyncBox<String> syncBox1 = new ItemSyncBox<>();
+ syncBox1.getItemsToPush().addAll(Arrays.asList(pushes.split(",")));
+ for (String orig : updates.split(",")) {
+ syncBox1.getItemsToUpdate().add(new ItemSyncBox.ItemUpdateTuple<>(orig, orig + "_updated"));
+ }
+ return syncBox1;
+ }
+
+ @Test
+ public void testResolveMeterDiffs() throws Exception {
+
+ }
+
+ @Test
+ public void testResolveFlowDiffsInTable() throws Exception {
+
+ }
+
+ @Test
+ public void testResolveFlowDiffsInAllTables() throws Exception {
+
+ }
}
\ No newline at end of file