*/
private boolean checkHost(MacVlan mvlan, NodeConnector port,
VNodePath mpath) {
- short vlan = mvlan.getVlan();
PortVlan pvlan = new PortVlan(port, mvlan.getVlan());
MapReference ref = resourceManager.getMapReference(pvlan);
if (ref != null && ref.getMapType() == MapType.PORT) {
import org.opendaylight.vtn.manager.internal.flow.add.PutFlowTxTask;
import org.opendaylight.vtn.manager.internal.flow.reader.FlowCountFuture;
import org.opendaylight.vtn.manager.internal.flow.reader.ReadFlowFuture;
+import org.opendaylight.vtn.manager.internal.flow.remove.ClearNodeFlowsTask;
import org.opendaylight.vtn.manager.internal.flow.remove.DeleteFlowTxTask;
import org.opendaylight.vtn.manager.internal.flow.remove.FlowRemoveContext;
import org.opendaylight.vtn.manager.internal.flow.remove.NodeFlowRemover;
import org.opendaylight.vtn.manager.internal.flow.remove.PortFlowRemover;
import org.opendaylight.vtn.manager.internal.flow.remove.RemovedFlowRemover;
import org.opendaylight.vtn.manager.internal.flow.stats.SalFlowIdResolver;
+import org.opendaylight.vtn.manager.internal.flow.stats.StatsReaderService;
import org.opendaylight.vtn.manager.internal.flow.stats.StatsTimerTask;
import org.opendaylight.vtn.manager.internal.inventory.VTNInventoryListener;
import org.opendaylight.vtn.manager.internal.inventory.VtnNodeEvent;
import org.opendaylight.vtn.manager.internal.util.flow.FlowUtils;
import org.opendaylight.vtn.manager.internal.util.flow.VTNFlowBuilder;
import org.opendaylight.vtn.manager.internal.util.inventory.SalNode;
-import org.opendaylight.vtn.manager.internal.util.rpc.RpcErrorCallback;
import org.opendaylight.vtn.manager.internal.util.rpc.RpcException;
import org.opendaylight.vtn.manager.internal.util.rpc.RpcFuture;
import org.opendaylight.vtn.manager.internal.util.rpc.RpcUtils;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.VtnFlows;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.VtnFlowsBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.tenant.flow.info.VtnDataFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.VtnOpenflowVersion;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.types.rev150209.VtnUpdateType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowAdded;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowRemoved;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowTableRef;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowUpdated;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeErrorNotification;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeExperimenterErrorNotification;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowListener;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SwitchFlowRemoved;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
-
/**
* Flow entry manager.
*/
private final ConcurrentMap<FlowCookie, VtnFlowId> removedCookies =
new ConcurrentHashMap<>();
+ /**
+ * Flow statistics reader service.
+ */
+ private final StatsReaderService statsReader;
+
/**
* MD-SAL transaction task to initialize internal flow containers.
*/
vtnProvider = provider;
txQueue = new TxQueueImpl("VTN Flow DS", provider);
addCloseable(txQueue);
- flowThread = new VTNThreadPool("VTN Flow Thread", 1, 0);
+ flowThread = new VTNThreadPool("VTN Flow Thread");
addCloseable(flowThread);
try {
// Register MD-SAL flow ID resolver.
addCloseable(new SalFlowIdResolver(provider, txQueue));
+
+ // Start flow statistics reader service.
+ statsReader = new StatsReaderService(provider, nsv);
+ addCloseable(statsReader);
} catch (Exception e) {
String msg = "Failed to initialize VTN flow service.";
LOG.error(msg, e);
/**
* Shut down the VTN flow service.
*/
- public synchronized void shutdown() {
- if (flowService != null) {
- // No more flow transaction will be accepted.
- flowService = null;
+ public void shutdown() {
+ shutdownFlowService();
- // Wait for all flow transactions to complete.
- TimeoutCounter tc = TimeoutCounter.
- newTimeout(SHUTDOWN_TIMEOUT, TimeUnit.SECONDS);
- try {
- while (txCount > 0) {
- tc.await(this);
- }
- return;
- } catch (TimeoutException e) {
- if (txCount > 0) {
- LOG.warn("Flow transaction did not complete: {}", txCount);
- }
- } catch (InterruptedException e) {
- LOG.warn("Shutdown wait has been interrupted.");
- }
+ StatsReaderService srs = statsReader;
+ if (srs != null) {
+ srs.shutdown();
}
}
}
/**
- * Return the MD-SAL flow service.
- *
- * @return The MD-SAL flow service.
- * {@code null} if the flow service is already closed.
+ * Shut down the VTN flow service.
*/
- private synchronized SalFlowService getFlowService() {
- return flowService;
+ private synchronized void shutdownFlowService() {
+ if (flowService != null) {
+ // No more flow transaction will be accepted.
+ flowService = null;
+
+ // Wait for all flow transactions to complete.
+ TimeoutCounter tc = TimeoutCounter.
+ newTimeout(SHUTDOWN_TIMEOUT, TimeUnit.SECONDS);
+ try {
+ while (txCount > 0) {
+ tc.await(this);
+ }
+ return;
+ } catch (TimeoutException e) {
+ if (txCount > 0) {
+ LOG.warn("Flow transaction did not complete: {}", txCount);
+ }
+ } catch (InterruptedException e) {
+ LOG.warn("Shutdown wait has been interrupted.");
+ }
+ }
}
/**
}
}
+ /**
+ * Clear the flow table in the specified switch.
+ *
+ * @param snode A {@link SalNode} instance which specifies the target
+ * switch.
+ * @param ofver A {@link VtnOpenflowVersion} instance which specifies the
+ * OpenFlow protocol version. {@code null} means the protocol
+ * version is not yet determined.
+ */
+ private synchronized void clearFlowTable(SalNode snode,
+ VtnOpenflowVersion ofver) {
+ SalFlowService sfs = flowService;
+ if (sfs == null) {
+ return;
+ }
+
+ // Increment the transaction counter.
+ assert txCount >= 0;
+ txCount++;
+
+ // Start ClearNodeFlowsTask.
+ ClearNodeFlowsTask task = new ClearNodeFlowsTask(
+ vtnProvider, sfs, statsReader, snode, ofver);
+ VTNFuture<Void> f = task.getFuture();
+ if (!flowThread.executeTask(task)) {
+ f.cancel(false);
+ }
+
+ // Add callback which will decrement the transaction counter when
+ // the future completes.
+ TxCounterCallback<Void> cb = new TxCounterCallback<>();
+ Futures.addCallback(f, cb);
+ }
+
/**
* Search for the VTN data flow associated with the given ID.
*
// AutoCloseable
/**
- * Close the internal packet service.
+ * Close the VTN flow manager service.
*/
@Override
public void close() {
VtnUpdateType type = ev.getUpdateType();
if (type == VtnUpdateType.CREATED) {
// Clear the flow table in the new switch.
- SalFlowService sfs = getFlowService();
- if (sfs != null) {
- SalNode snode = ev.getSalNode();
- LOG.debug("{}: Clean up the flow table.", snode);
- Short tid = Short.valueOf(FlowUtils.TABLE_ID);
- FlowTableRef tref = new FlowTableRef(
- snode.getFlowTableIdentifier(tid));
- StringBuilder sb = new StringBuilder("clean-up:").
- append(snode);
- RemoveFlowInput input = new RemoveFlowInputBuilder().
- setNode(snode.getNodeRef()).
- setFlowTable(tref).
- setTransactionUri(new Uri(sb.toString())).
- setTableId(tid).
- setStrict(false).
- setBarrier(true).
- build();
- Future<RpcResult<RemoveFlowOutput>> f = sfs.removeFlow(input);
- RpcErrorCallback<RemoveFlowOutput> cb = new RpcErrorCallback<>(
- LOG, "clean-up-flow",
- "Failed to clean up the flow table: %s", snode);
- vtnProvider.setCallback(f, cb);
- }
+ VtnOpenflowVersion ofver = ev.getVtnNode().getOpenflowVersion();
+ clearFlowTable(ev.getSalNode(), ofver);
}
// Uninstall VTN flows affected by the node.
GetDataFlowInput input) {
TxContext ctx = vtnProvider.newTxContext();
try {
- ReadFlowFuture f = ReadFlowFuture.create(ctx, txQueue, input);
+ ReadFlowFuture f =
+ ReadFlowFuture.create(ctx, txQueue, statsReader, input);
return new RpcFuture<List<DataFlowInfo>, GetDataFlowOutput>(f, f);
} catch (VTNException | RuntimeException e) {
ctx.cancelTransaction();
import org.opendaylight.vtn.manager.internal.TxContext;
import org.opendaylight.vtn.manager.internal.TxQueue;
import org.opendaylight.vtn.manager.internal.cluster.MacVlan;
+import org.opendaylight.vtn.manager.internal.flow.stats.FlowStatsReader;
+import org.opendaylight.vtn.manager.internal.flow.stats.StatsReaderService;
import org.opendaylight.vtn.manager.internal.util.flow.FlowCache;
import org.opendaylight.vtn.manager.internal.util.flow.FlowUtils;
import org.opendaylight.vtn.manager.internal.util.inventory.SalNode;
*/
@Override
public void onSuccess(List<Optional<VtnDataFlow>> result) {
- List<DataFlowInfo> flows = new ArrayList<>();
+ int size = result.size();
+ List<DataFlowInfo> flows = new ArrayList<>(size);
int index = 0;
+ FlowStatsReader stReader = getFlowStatsReader(result.size());
for (Optional<VtnDataFlow> opt: result) {
if (opt.isPresent()) {
- FlowCache fc = new FlowCache(opt.get());
- if (select(fc)) {
- DataFlowInfo df = toDataFlowInfo(fc, null);
- if (df == null) {
- return;
- }
- flows.add(df);
- }
+ addDataFlowInfo(flows, opt.get(), stReader);
} else {
// This should never happen.
Logger logger = LoggerFactory.
* @param ctx A {@link TxContext} instance.
* @param txq A {@link TxQueue} instance used to update the MD-SAL
* datastore.
+ * @param srs The flow statistics reader service.
* @param input Input of the RPC call.
* @throws VTNException An error occurred.
*/
public ReadDataFlowFuture(TxContext ctx, TxQueue txq,
- GetDataFlowInput input) throws VTNException {
- super(ctx, txq, input);
+ StatsReaderService srs, GetDataFlowInput input)
+ throws VTNException {
+ super(ctx, txq, srs, input);
// Determine flow index to be used.
- if (readIndex(input) == null) {
+ if (!isDone() && readIndex(input) == null) {
// Scan all the data flows present in the VTN.
ReadTransaction rtx = getTxContext().getTransaction();
LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
* index read operation. {@code null} if no index should be used.
*/
private IndexCallback<?> readIndex(GetDataFlowInput input) {
- if (!isDone()) {
- MacVlan src = getSourceHost();
- if (src != null) {
- // Use source host inedx.
- SourceHostFlowsKey hostId = src.getSourceHostFlowsKey();
- clearSourceHost();
- InstanceIdentifier<SourceHostFlows> path =
- FlowUtils.getIdentifier(input.getTenantName(), hostId);
- return new IndexCallback<SourceHostFlows>(path);
- }
+ MacVlan src = getSourceHost();
+ if (src != null) {
+ // Use source host inedx.
+ SourceHostFlowsKey hostId = src.getSourceHostFlowsKey();
+ clearSourceHost();
+ InstanceIdentifier<SourceHostFlows> path =
+ FlowUtils.getIdentifier(input.getTenantName(), hostId);
+ return new IndexCallback<SourceHostFlows>(path);
+ }
- SalPort sport = getFlowPort();
- if (sport != null) {
- // Use switch port index.
- clearFlowPort();
- InstanceIdentifier<PortFlows> path =
- FlowUtils.getIdentifier(input.getTenantName(),
- sport.getNodeConnectorId());
- return new IndexCallback<PortFlows>(path);
- }
+ SalPort sport = getFlowPort();
+ if (sport != null) {
+ // Use switch port index.
+ clearFlowPort();
+ InstanceIdentifier<PortFlows> path = FlowUtils.getIdentifier(
+ input.getTenantName(), sport.getNodeConnectorId());
+ return new IndexCallback<PortFlows>(path);
+ }
- SalNode snode = getFlowNode();
- if (snode != null) {
- // Use switch index.
- clearFlowNode();
- InstanceIdentifier<NodeFlows> path =
- FlowUtils.getIdentifier(input.getTenantName(),
- snode.getNodeId());
- return new IndexCallback<NodeFlows>(path);
- }
+ SalNode snode = getFlowNode();
+ if (snode != null) {
+ // Use switch index.
+ clearFlowNode();
+ InstanceIdentifier<NodeFlows> path = FlowUtils.getIdentifier(
+ input.getTenantName(), snode.getNodeId());
+ return new IndexCallback<NodeFlows>(path);
}
return null;
}
+ /**
+ * Return a flow statistics reader.
+ *
+ * @param count The estimated number of flow entries present in switches.
+ * @return A {@link FlowStatsReader} service.
+ */
+ private FlowStatsReader getFlowStatsReader(int count) {
+ StatsReaderService srs = getStatsReaderService();
+ return FlowStatsReader.getInstance(srs, count);
+ }
+
+ /**
+ * Convert the given VTN data flow into a {@link DataFlowInfo} instance,
+ * and add it to the given list.
+ *
+ * @param list A list to store converted instances.
+ * @param vdf A {@link VtnDataFlow} instance.
+ * @param stReader A {@link FlowStatsReader} instance used to update
+ * flow statistics.
+ */
+ private void addDataFlowInfo(List<DataFlowInfo> list, VtnDataFlow vdf,
+ FlowStatsReader stReader) {
+ FlowCache fc = new FlowCache(vdf);
+ if (select(fc)) {
+ DataFlowInfo df = toDataFlowInfo(fc, stReader.get(fc));
+ if (df != null) {
+ list.add(df);
+ }
+ }
+ }
+
// FutureCallback
/**
// Select data flows which meet the condition.
List<VtnDataFlow> flows = result.get().getVtnDataFlow();
if (flows != null) {
- List<DataFlowInfo> list = new ArrayList<>();
+ int size = flows.size();
+ FlowStatsReader stReader = getFlowStatsReader(size);
+ List<DataFlowInfo> list = new ArrayList<>(size);
for (VtnDataFlow vdf: flows) {
- FlowCache fc = new FlowCache(vdf);
- if (select(fc)) {
- DataFlowInfo df = toDataFlowInfo(fc, null);
- if (df == null) {
- return;
- }
- list.add(df);
- }
+ addDataFlowInfo(list, vdf, stReader);
}
set(list);
return;
import org.opendaylight.vtn.manager.internal.TxContext;
import org.opendaylight.vtn.manager.internal.TxQueue;
import org.opendaylight.vtn.manager.internal.cluster.MacVlan;
+import org.opendaylight.vtn.manager.internal.flow.stats.AddFlowStatsTask;
+import org.opendaylight.vtn.manager.internal.flow.stats.StatsReaderService;
import org.opendaylight.vtn.manager.internal.util.MiscUtils;
+import org.opendaylight.vtn.manager.internal.util.concurrent.VTNFuture;
import org.opendaylight.vtn.manager.internal.util.flow.FlowCache;
import org.opendaylight.vtn.manager.internal.util.inventory.InventoryReader;
import org.opendaylight.vtn.manager.internal.util.inventory.SalNode;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.flow.rev150410.vtn.data.flow.info.AveragedDataFlowStats;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.flow.rev150410.vtn.data.flow.info.AveragedDataFlowStatsBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.flow.rev150410.vtn.data.flow.info.DataFlowStatsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.tenant.flow.info.VtnDataFlow;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.FlowStatsHistory;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.flow.stats.history.FlowStatsRecord;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.types.rev150209.VlanHost;
/**
* Default value for the averaged statistics interval.
*/
- private static final long DEFAULT_AVERAGE_INTERVAL = 10;
+ private static final long DEFAULT_AVERAGE_INTERVAL = 10L;
/**
* The number of milliseconds in a second.
*/
private static final double MILLISEC = 1000D;
+ /**
+ * The number of milliseconds to wait for completion of transaction for
+ * getting flow statistics.
+ */
+ private static final long STATS_READ_TIMEOUT = 10000L;
+
/**
* The MD-SAL transaction queue used to update the MD-SAL datastore.
*/
private final TxQueue txQueue;
+ /**
+ * Flow statistics reader service.
+ */
+ private final StatsReaderService statsReader;
+
/**
* A {@link SalNode} instance which specifies the physical switch.
*/
* @param ctx A {@link TxContext} instance.
* @param txq A {@link TxQueue} instance used to update the MD-SAL
* datastore.
+ * @param srs The flow statistics reader service.
* @param input Input of the RPC call.
* @return A {@link ReadFlowFuture} instance.
* @throws VTNException An error occurred.
*/
public static final ReadFlowFuture create(TxContext ctx, TxQueue txq,
+ StatsReaderService srs,
GetDataFlowInput input)
throws VTNException {
if (input == null) {
}
return (input.getFlowId() == null)
- ? new ReadDataFlowFuture(ctx, txq, input)
- : new ReadSingleFlowFuture(ctx, txq, input);
+ ? new ReadDataFlowFuture(ctx, txq, srs, input)
+ : new ReadSingleFlowFuture(ctx, txq, srs, input);
}
/**
* @param ctx A {@link TxContext} instance.
* @param txq A {@link TxQueue} instance used to update the MD-SAL
* datastore.
+ * @param srs The flow statistics reader service.
* @param input Input of the RPC call.
* @throws VTNException An error occurred.
*/
protected ReadFlowFuture(TxContext ctx, TxQueue txq,
- GetDataFlowInput input) throws VTNException {
+ StatsReaderService srs, GetDataFlowInput input)
+ throws VTNException {
super(ctx, input.getTenantName());
txQueue = txq;
}
flowMode = mode;
+ statsReader = (mode == DataFlowMode.UPDATESTATS)
+ ? srs : null;
+
Integer interval = input.getAverageInterval();
long ival = (interval == null) ? 0 : interval.longValue();
if (ival <= 0) {
setCondition(ctx, input);
}
+ /**
+ * Return the flow statistics reader service.
+ *
+ * @return A {@link StatsReaderService} instance if flow statistics
+ * information needs to be fetched from switches.
+ * Otherwise {@code null}.
+ */
+ protected final StatsReaderService getStatsReaderService() {
+ return statsReader;
+ }
+
/**
* Return a {@link SalNode} instance corresponding to the physical switch
* specified by the RPC input.
* </p>
*
* @param fc A {@link FlowCache} instance.
- * @param current A {@link FlowStatsRecord} that contains the current
- * statistics information. If {@code null}, only flow
+ * @param current A future which will return the current flow statistics
+ * for the given data flow. If {@code null}, only flow
* statistics cached in {@code fc} are used.
* @return A {@link DataFlowInfo} instance on success.
* {@code null} on failure.
*/
- protected final DataFlowInfo toDataFlowInfo(FlowCache fc,
- FlowStatsRecord current) {
+ protected final DataFlowInfo toDataFlowInfo(
+ FlowCache fc, VTNFuture<FlowStatsRecord> current) {
try {
InventoryReader reader = getTxContext().getInventoryReader();
DataFlowInfoBuilder builder =
}
}
+ /**
+ * Return a list of flow statistics history records in the given VTN
+ * data flow.
+ *
+ * @param vdf The target VTN data flow.
+ * @return A list of {@link FlowStatsRecord} instance.
+ */
+ private List<FlowStatsRecord> getFlowStatsRecords(VtnDataFlow vdf) {
+ FlowStatsHistory history = vdf.getFlowStatsHistory();
+ if (history != null) {
+ List<FlowStatsRecord> list = history.getFlowStatsRecord();
+ if (list != null) {
+ return list;
+ }
+ }
+
+ // No statistics information is available.
+ return Collections.<FlowStatsRecord>emptyList();
+ }
+
+ /**
+ * Return the flow statistics record from the given future.
+ *
+ * @param vdf The target VTN data flow.
+ * @param current A future which will return the current flow statistics
+ * for the given data flow. Note that {@code null} is
+ * returned if {@code null} is specified.
+ * @return A {@link FlowStatsRecord} instance returned by the given
+ * future. {@code null} if the flow statistics is not available.
+ */
+ private FlowStatsRecord getFlowStatsRecord(
+ VtnDataFlow vdf, VTNFuture<FlowStatsRecord> current) {
+ if (current != null) {
+ try {
+ return current.checkedGet(STATS_READ_TIMEOUT,
+ TimeUnit.MILLISECONDS);
+ } catch (VTNException | RuntimeException e) {
+ Logger logger = LoggerFactory.getLogger(getClass());
+ String msg = "Flow statistics is unavailable: flowId={}" +
+ vdf.getFlowId().getValue();
+ logger.warn(msg, e);
+ }
+ }
+
+ return null;
+ }
+
/**
* Set the flow statistics information into the given data flow info
* builder.
*
* @param builder A {@link DataFlowInfoBuilder} instance.
* @param fc A {@link FlowCache} instance.
- * @param current A {@link FlowStatsRecord} that contains the current
- * statistics information. If {@code null}, only flow
+ * @param current A future which will return the current flow statistics
+ * for the given data flow. If {@code null}, only flow
* statistics cached in {@code fc} are used.
*/
private void setStatistics(DataFlowInfoBuilder builder, FlowCache fc,
- FlowStatsRecord current) {
- FlowStatsHistory history = fc.getDataFlow().getFlowStatsHistory();
- if (history == null) {
- // No statistics information is available.
- return;
- }
-
- List<FlowStatsRecord> list = history.getFlowStatsRecord();
- if (list == null || list.isEmpty()) {
- // No statistics information is available.
- return;
- }
-
+ VTNFuture<FlowStatsRecord> current) {
// Sort history records in ascending order of the system time.
+ VtnDataFlow vdf = fc.getDataFlow();
+ List<FlowStatsRecord> list = getFlowStatsRecords(vdf);
TreeMap<Long, FlowStatsRecord> map = new TreeMap<>();
for (FlowStatsRecord fsr: list) {
map.put(fsr.getTime(), fsr);
}
// Add the current statistics if specified.
- if (current != null) {
- map.put(current.getTime(), current);
+ FlowStatsRecord cfsr = getFlowStatsRecord(vdf, current);
+ if (cfsr != null) {
+ map.put(cfsr.getTime(), cfsr);
+ AddFlowStatsTask task = new AddFlowStatsTask(
+ getTenantName(), vdf.getFlowId(), cfsr);
+ txQueue.post(task);
+ }
+
+ if (map.isEmpty()) {
+ // No statistics information is available.
+ return;
}
FlowStatsRecord latest = map.lastEntry().getValue();
import org.opendaylight.vtn.manager.internal.TxContext;
import org.opendaylight.vtn.manager.internal.TxQueue;
+import org.opendaylight.vtn.manager.internal.flow.stats.StatsReaderService;
+import org.opendaylight.vtn.manager.internal.util.concurrent.VTNFuture;
import org.opendaylight.vtn.manager.internal.util.flow.FlowCache;
import org.opendaylight.vtn.manager.internal.util.flow.FlowUtils;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.flow.rev150410.GetDataFlowInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.flow.rev150410.get.data.flow.output.DataFlowInfo;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.tenant.flow.info.VtnDataFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.VtnFlowEntry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.flow.stats.history.FlowStatsRecord;
/**
* {@code ReadSingleFlowFuture} describes a future associated with the task
* @param ctx A {@link TxContext} instance.
* @param txq A {@link TxQueue} instance used to update the MD-SAL
* datastore.
+ * @param srs The flow statistics reader service.
* @param input Input of the RPC call.
* @throws VTNException An error occurred.
*/
public ReadSingleFlowFuture(TxContext ctx, TxQueue txq,
- GetDataFlowInput input) throws VTNException {
- super(ctx, txq, input);
+ StatsReaderService srs, GetDataFlowInput input)
+ throws VTNException {
+ super(ctx, txq, srs, input);
if (!isDone()) {
InstanceIdentifier<VtnDataFlow> path = FlowUtils.
}
}
+ /**
+ * Return a future associated with a task which reads flow statistics for
+ * the target VTN data flow.
+ *
+ * @param fc The target VTN data flow.
+ * @return A future which will return flow statistics for the target
+ * VTN data flow. Note that {@code null} is returned if flow
+ * statistics does not need to be read.
+ */
+ private VTNFuture<FlowStatsRecord> readStats(FlowCache fc) {
+ StatsReaderService srs = getStatsReaderService();
+ if (srs != null) {
+ // Try to update statistics for the ingress flow entry.
+ VtnFlowEntry ingress = fc.getIngressFlow();
+ if (ingress != null) {
+ return srs.get(ingress);
+ }
+ }
+
+ return null;
+ }
+
// FutureCallback
/**
// Ensure that this data flow meets the condition.
FlowCache fc = new FlowCache(result.get());
if (select(fc)) {
- DataFlowInfo df = toDataFlowInfo(fc, null);
+ DataFlowInfo df = toDataFlowInfo(fc, readStats(fc));
if (df != null) {
setResult(Collections.singletonList(df));
}
--- /dev/null
+/*
+ * Copyright (c) 2015 NEC Corporation
+ * 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.vtn.manager.internal.flow.remove;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.opendaylight.vtn.manager.VTNException;
+
+import org.opendaylight.vtn.manager.internal.VTNConfig;
+import org.opendaylight.vtn.manager.internal.VTNManagerProvider;
+import org.opendaylight.vtn.manager.internal.flow.stats.StatsReaderCallback;
+import org.opendaylight.vtn.manager.internal.flow.stats.StatsReaderService;
+import org.opendaylight.vtn.manager.internal.util.concurrent.SettableVTNFuture;
+import org.opendaylight.vtn.manager.internal.util.concurrent.VTNFuture;
+import org.opendaylight.vtn.manager.internal.util.flow.FlowUtils;
+import org.opendaylight.vtn.manager.internal.util.flow.RemoveFlowRpc;
+import org.opendaylight.vtn.manager.internal.util.inventory.SalNode;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.flow.rev150410.VtnFlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.VtnOpenflowVersion;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowTableRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowAndStatisticsMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+
+/**
+ * {@code ClearNodeFlowsTask} describes a task which uninstalls VTN flow
+ * entries present in the given switch.
+ *
+ * <p>
+ * This task will remove only flow entries installed by the VTN Manager, and
+ * will keep flow entries installed by another component.
+ * </p>
+ */
+public final class ClearNodeFlowsTask
+ implements Runnable, StatsReaderCallback {
+ /**
+ * Logger instance.
+ */
+ private static final Logger LOG =
+ LoggerFactory.getLogger(ClearNodeFlowsTask.class);
+
+ /**
+ * The timeout, in seconds, of flow statistics read transaction.
+ */
+ private static final long STATS_READ_TIMEOUT = 20;
+
+ /**
+ * VTN Manager provider service.
+ */
+ private final VTNManagerProvider vtnProvider;
+
+ /**
+ * The MD-SAL flow service.
+ */
+ private final SalFlowService flowService;
+
+ /**
+ * The target switch.
+ */
+ private final SalNode targetNode;
+
+ /**
+ * The OpenFlow protocol version used to communicate with the target
+ * switch.
+ */
+ private final VtnOpenflowVersion ofVersion;
+
+ /**
+ * A future associated with this task.
+ */
+ private final SettableVTNFuture<Void> taskFuture =
+ new SettableVTNFuture<>();
+
+ /**
+ * A future used to wait for RPC invocations.
+ */
+ private final SettableVTNFuture<Void> rpcFuture =
+ new SettableVTNFuture<>();
+
+ /**
+ * A list of RPC invocations which uninstalls VTN flows.
+ */
+ private final List<RemoveFlowRpc> rpcList;
+
+ /**
+ * Construct a new instance.
+ *
+ * @param provider VTN Manager provider service.
+ * @param sfs The MD-SAL flow service.
+ * @param srs Flow statistics reader service.
+ * @param snode A {@link SalNode} instance which specifies the target
+ * switch.
+ * @param ofver A {@link VtnOpenflowVersion} instance which indicates
+ * the OpenFlow protocol version.
+ */
+ public ClearNodeFlowsTask(VTNManagerProvider provider, SalFlowService sfs,
+ StatsReaderService srs, SalNode snode,
+ VtnOpenflowVersion ofver) {
+ vtnProvider = provider;
+ flowService = sfs;
+ targetNode = snode;
+ ofVersion = ofver;
+
+ if (ofver == VtnOpenflowVersion.OF13) {
+ LOG.debug("Remove all VTN flows by cookie mask: {}", snode);
+ RemoveFlowInput input = FlowUtils.createRemoveFlowInput(snode);
+ rpcList = Collections.singletonList(new RemoveFlowRpc(sfs, input));
+ rpcFuture.set(null);
+ } else {
+ // Read flow entries in the given switch.
+ LOG.debug("Scanning flow table to remove VTN flows: {}", snode);
+ rpcList = new ArrayList<>();
+ if (!srs.start(snode, this)) {
+ transactionCanceled();
+ }
+ }
+ }
+
+ /**
+ * Return a future associated with this task.
+ *
+ * @return A {@link VTNFuture} instance associated with this task.
+ */
+ public VTNFuture<Void> getFuture() {
+ return taskFuture;
+ }
+
+ // Runnable
+
+ /**
+ * Wait for all VTN flows in the target switch to be uninstalled.
+ */
+ @Override
+ public void run() {
+ // Wait for remove-flow request to be sent to the target switch.
+ try {
+ rpcFuture.checkedGet(STATS_READ_TIMEOUT, TimeUnit.SECONDS);
+ } catch (VTNException e) {
+ if (rpcFuture.cancel(false)) {
+ // This should never happen because the flow statistics read
+ // transaction will be timed out in 10 seconds.
+ LOG.error("Failed to determine flow entries to be removed: " +
+ targetNode, e);
+ return;
+ }
+ }
+
+ // Wait for completion of RPC tasks.
+ TimeUnit nano = TimeUnit.NANOSECONDS;
+ TimeUnit milli = TimeUnit.MILLISECONDS;
+ VTNConfig vcfg = vtnProvider.getVTNConfig();
+ int msec = vcfg.getBulkFlowModTimeout();
+ long timeout = milli.toNanos((long)msec);
+ long deadline = System.nanoTime() + timeout;
+
+ VTNException firstError = null;
+ for (RemoveFlowRpc rpc: rpcList) {
+ try {
+ rpc.getResult(timeout, nano, LOG);
+ } catch (VTNException e) {
+ if (firstError == null) {
+ firstError = e;
+ }
+ continue;
+ }
+ LOG.trace("remove-flow has completed successfully: input={}",
+ rpc.getInput());
+
+ timeout = deadline - System.nanoTime();
+ if (timeout <= 0) {
+ // Wait one more millisecond.
+ timeout = milli.toNanos(1L);
+ }
+ }
+
+ if (firstError == null) {
+ LOG.debug("VTN flows has been cleared: {}", targetNode);
+ } else {
+ LOG.error("Failed to remove VTN flows in " + targetNode,
+ firstError);
+ }
+ }
+
+ // StatsReaderCallback
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void flowStatsReceived(NodeId node, FlowAndStatisticsMap fstats) {
+ assert node.equals(targetNode.getNodeId());
+
+ FlowCookie cookie = fstats.getCookie();
+ VtnFlowId fid = FlowUtils.getVtnFlowId(cookie);
+ if (fid != null) {
+ LOG.trace("Remove VTN flow entry: node={}, flow={}",
+ node.getValue(), fstats);
+
+ StringBuilder builder = new StringBuilder("clear-node-flows:").
+ append(fid.getValue());
+ Uri uri = new Uri(builder.toString());
+ RemoveFlowInput input =
+ FlowUtils.createRemoveFlowInput(targetNode, fstats, uri);
+ rpcList.add(new RemoveFlowRpc(flowService, input));
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void transactionCompleted() {
+ LOG.debug("All the flow entries have been scanned: {}", targetNode);
+ rpcFuture.set(null);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void transactionCanceled() {
+ LOG.warn("Failed to read VTN flows: {}", targetNode);
+
+ if (!rpcFuture.isDone()) {
+ LOG.warn("All flow entries in {} will be removed.", targetNode);
+
+ Short tid = Short.valueOf(FlowUtils.TABLE_ID);
+ FlowTableRef tref =
+ new FlowTableRef(targetNode.getFlowTableIdentifier(tid));
+ StringBuilder sb = new StringBuilder("clean-up:").
+ append(targetNode);
+ RemoveFlowInput input = new RemoveFlowInputBuilder().
+ setNode(targetNode.getNodeRef()).
+ setFlowTable(tref).
+ setTransactionUri(new Uri(sb.toString())).
+ setTableId(tid).
+ setStrict(false).
+ setBarrier(true).
+ build();
+
+ rpcList.add(new RemoveFlowRpc(flowService, input));
+ rpcFuture.set(null);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 NEC Corporation
+ * 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.vtn.manager.internal.flow.stats;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+
+import org.opendaylight.vtn.manager.VTNException;
+
+import org.opendaylight.vtn.manager.internal.TxContext;
+import org.opendaylight.vtn.manager.internal.VTNManagerProvider;
+import org.opendaylight.vtn.manager.internal.util.DataStoreUtils;
+import org.opendaylight.vtn.manager.internal.util.flow.FlowStatsUtils;
+import org.opendaylight.vtn.manager.internal.util.flow.FlowUtils;
+import org.opendaylight.vtn.manager.internal.util.tx.AbstractTxTask;
+
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.flow.rev150410.VtnFlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.tenant.flow.info.VtnDataFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.FlowStatsHistory;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.flow.stats.history.FlowStatsRecord;
+
+/**
+ * {@code AddFlowStatsTask} describes the MD-SAL datastore transaction task
+ * that adds the given flow statistics history record to the VTN data flow.
+ *
+ * <p>
+ * This task returns {@link Boolean#TRUE} if the given flow statistics was
+ * added successfully.
+ * </p>
+ */
+public final class AddFlowStatsTask extends AbstractTxTask<Boolean> {
+ /**
+ * Logger instance.
+ */
+ public static final Logger LOG =
+ LoggerFactory.getLogger(AddFlowStatsTask.class);
+
+ /**
+ * The name of the target VTN.
+ */
+ private final String tenantName;
+
+ /**
+ * The identifier of the target VTN data flow.
+ */
+ private final VtnFlowId flowId;
+
+ /**
+ * The flow statistics record to be added.
+ */
+ private final FlowStatsRecord statsRecord;
+
+ /**
+ * Construct a new instance.
+ *
+ * @param tname The name of the target VTN.
+ * @param fid The identifier of the target VTN data flow.
+ * @param fsr The flow statistics record to be added.
+ */
+ public AddFlowStatsTask(String tname, VtnFlowId fid, FlowStatsRecord fsr) {
+ tenantName = tname;
+ flowId = fid;
+ statsRecord = fsr;
+ }
+
+ /**
+ * Update the flow statistics history.
+ *
+ * @param ctx A runtime context for MD-SAL datastore transaction task.
+ * @return {@link Boolean#TRUE} if the flow statistics history was
+ * updated. {@link Boolean#FALSE} if not updated.
+ * @throws VTNException An error occurred.
+ */
+ private Boolean update(TxContext ctx) throws VTNException {
+ // Read the current statistics history.
+ InstanceIdentifier<FlowStatsHistory> path = FlowUtils.
+ getIdentifier(tenantName, flowId).
+ child(FlowStatsHistory.class);
+ LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
+ ReadWriteTransaction tx = ctx.getReadWriteTransaction();
+ Optional<FlowStatsHistory> opt = DataStoreUtils.read(tx, oper, path);
+ FlowStatsHistory history;
+ if (opt.isPresent()) {
+ history = opt.get();
+ } else {
+ // Check to see if the target VTN data flow is present.
+ InstanceIdentifier<VtnDataFlow> fpath =
+ path.firstIdentifierOf(VtnDataFlow.class);
+ Optional<VtnDataFlow> fopt = DataStoreUtils.read(tx, oper, fpath);
+ if (!fopt.isPresent()) {
+ // The target VTN data flow is no longer present.
+ return Boolean.FALSE;
+ }
+ history = null;
+ }
+
+ // Update the statistics history.
+ FlowStatsHistory newHist =
+ FlowStatsUtils.addNonPeriodic(history, statsRecord);
+ if (newHist != null) {
+ tx.put(oper, path, newHist, false);
+ return Boolean.TRUE;
+ }
+
+ return Boolean.FALSE;
+ }
+
+ // AbstractTxTask
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Boolean execute(TxContext ctx) throws VTNException {
+ Boolean ret = update(ctx);
+ if (ret == null) {
+ ctx.cancelTransaction();
+ }
+
+ return ret;
+ }
+
+ // TxTask
+
+ /**
+ * Invoked when the task has been completed successfully.
+ *
+ * @param provider VTN Manager provider service.
+ * @param result The result of this task.
+ */
+ @Override
+ public void onSuccess(VTNManagerProvider provider, Boolean result) {
+ if (Boolean.TRUE.equals(result)) {
+ LOG.debug("Flow statistics has been added: id={}, time={}",
+ flowId.getValue(), statsRecord.getTime());
+ } else if (LOG.isTraceEnabled()) {
+ LOG.trace("Flow statistics was ignored: id={}, record={}",
+ flowId.getValue(), statsRecord);
+ }
+ }
+
+ /**
+ * Invoked when the task has failed.
+ *
+ * @param provider VTN Manager provider service.
+ * @param t A {@link Throwable} thrown by the task.
+ */
+ @Override
+ public void onFailure(VTNManagerProvider provider, Throwable t) {
+ StringBuilder builder =
+ new StringBuilder("Failed to add flow statistics: id=");
+ String msg = builder.append(flowId.getValue()).
+ append(", record=").append(statsRecord).toString();
+ LOG.error(msg, t);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 NEC Corporation
+ * 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.vtn.manager.internal.flow.stats;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.opendaylight.vtn.manager.internal.util.concurrent.VTNFuture;
+import org.opendaylight.vtn.manager.internal.util.flow.FlowCache;
+import org.opendaylight.vtn.manager.internal.util.inventory.SalNode;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.VtnFlowEntry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.flow.stats.history.FlowStatsRecord;
+
+/**
+ * {@code FlowStatsReader} reads statistics information about the specified
+ * flow entries from switches.
+ */
+public abstract class FlowStatsReader {
+ /**
+ * A threshold to switch method to read flow statistics.
+ *
+ * <p>
+ * If the estimated number of flow entries exceeds this value, this class
+ * will read statistics for all flow entries in switches by one RPC.
+ * Otherwise this class will read statistics for each flow entry one by
+ * one.
+ * </p>
+ */
+ private static final int COUNT_THRESHOLD = 10;
+
+ /**
+ * A flow statistics reader which never reads flow statistics.
+ */
+ private static final NullReader NULL_READER = new NullReader();
+
+ /**
+ * Return a flow statistics reader.
+ *
+ * @param srs The flow statistics reader service.
+ * @param count The estimated number of flow entries present in switches.
+ * @return A {@link FlowStatsReader} instance.
+ */
+ public static final FlowStatsReader getInstance(
+ StatsReaderService srs, int count) {
+ if (srs == null) {
+ // No need to update flow statistics.
+ return NULL_READER;
+ }
+
+ return (count > COUNT_THRESHOLD)
+ ? new MultiReader(srs)
+ : new SingleReader(srs);
+ }
+
+ /**
+ * Construct a new instance.
+ */
+ private FlowStatsReader() {
+ }
+
+ /**
+ * Return a future associated with the transaction for reading flow
+ * statistics information.
+ *
+ * @param fc The target flow entry.
+ * @return A {@link VTNFuture} instance which returns the flow statistics
+ * about the specified flow entry.
+ */
+ public abstract VTNFuture<FlowStatsRecord> get(FlowCache fc);
+
+ /**
+ * An implementation of {@link FlowStatsReader} which never reads flow
+ * statistics.
+ *
+ * <p>
+ * This reader class is used when flow statistics does not need to be
+ * updated.
+ * </p>
+ */
+ private static final class NullReader extends FlowStatsReader {
+ // FlowStatsReader
+
+ /**
+ * {@code null} is always returned because this class never reads
+ * flow statistics.
+ *
+ * @param fc The target flow entry.
+ * @return {@code null}.
+ */
+ @Override
+ public VTNFuture<FlowStatsRecord> get(FlowCache fc) {
+ return null;
+ }
+ }
+
+ /**
+ * An implementation of {@link FlowStatsReader} which reads flow statistics
+ * one by one.
+ */
+ private static final class SingleReader extends FlowStatsReader {
+ /**
+ * The flow statistics reader service.
+ */
+ private final StatsReaderService statsReader;
+
+ /**
+ * Construct a new instance.
+ *
+ * @param srs The flow statistics reader service.
+ */
+ private SingleReader(StatsReaderService srs) {
+ statsReader = srs;
+ }
+
+ // FlowStatsReader
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public VTNFuture<FlowStatsRecord> get(FlowCache fc) {
+ VtnFlowEntry ingress = fc.getIngressFlow();
+ return (ingress == null) ? null : statsReader.get(ingress);
+ }
+ }
+
+ /**
+ * An implementation of {@link FlowStatsReader} which reads flow statistics
+ * by reading all flow entries in the target switches.
+ */
+ private static final class MultiReader extends FlowStatsReader {
+ /**
+ * The flow statistics reader service.
+ */
+ private final StatsReaderService statsReader;
+
+ /**
+ * A map which keeps statistics read transaction per switch.
+ */
+ private final ConcurrentMap<SalNode, NodeFlowStatsReader> txMap =
+ new ConcurrentHashMap<>();
+
+ /**
+ * Construct a new instance.
+ *
+ * @param srs The flow statistics reader service.
+ */
+ private MultiReader(StatsReaderService srs) {
+ statsReader = srs;
+ }
+
+ /**
+ * Return a flow statistics reader which read all flow statistics
+ * present in the given switch.
+ *
+ * @param snode A {@link SalNode} instance which specifies the switch.
+ * @return A {@link NodeFlowStatsReader} instance.
+ */
+ private NodeFlowStatsReader getNodeFlowStatsReader(SalNode snode) {
+ NodeFlowStatsReader reader = txMap.get(snode);
+ if (reader == null) {
+ // Create a new flow statistics reader for the given node.
+ reader = new NodeFlowStatsReader(snode);
+ NodeFlowStatsReader old = txMap.putIfAbsent(snode, reader);
+ if (old == null) {
+ // Start the read transaction.
+ reader.start(statsReader);
+ } else {
+ // Lost the race.
+ reader = old;
+ }
+ }
+
+ return reader;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public VTNFuture<FlowStatsRecord> get(FlowCache fc) {
+ VtnFlowEntry ingress = fc.getIngressFlow();
+ if (ingress == null) {
+ return null;
+ }
+
+ SalNode snode = SalNode.create(ingress.getNode());
+ if (snode == null) {
+ // This should never happen.
+ return null;
+ }
+
+ return getNodeFlowStatsReader(snode).get(ingress);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 NEC Corporation
+ * 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.vtn.manager.internal.flow.stats;
+
+import java.math.BigInteger;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.opendaylight.vtn.manager.internal.util.concurrent.SettableVTNFuture;
+import org.opendaylight.vtn.manager.internal.util.concurrent.VTNFuture;
+import org.opendaylight.vtn.manager.internal.util.inventory.SalNode;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowAndStatisticsMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.GenericStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.VtnFlowEntry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.flow.stats.history.FlowStatsRecord;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.flow.stats.history.FlowStatsRecordBuilder;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
+
+/**
+ * {@code NodeFlowStatsReader} reads statistics information about the specified
+ * flow entries in the same switch.
+ *
+ * <p>
+ * Note that this method reads all flow statistics present in the specified
+ * switch.
+ * </p>
+ */
+public final class NodeFlowStatsReader implements StatsReaderCallback {
+ /**
+ * Initial transaction state.
+ */
+ private static final int STATE_INITIAL = 0;
+
+ /**
+ * Transaction state which indicates the transaction is being started.
+ */
+ private static final int STATE_STARTING = 1;
+
+ /**
+ * Transaction state which indicates the transaction was started
+ * successfully.
+ */
+ private static final int STATE_STARTED = 2;
+
+ /**
+ * Transaction state which indicates the transaction completed.
+ */
+ private static final int STATE_COMPLETED = 3;
+
+ /**
+ * Transaction state which indicates the transaction failed to start.
+ */
+ private static final int STATE_FAILED = 4;
+
+ /**
+ * A map that keeps statistics read transactions and results.
+ */
+ private final ConcurrentMap<BigInteger, SettableVTNFuture<FlowStatsRecord>> txMap =
+ new ConcurrentHashMap<>();
+
+ /**
+ * The target switch.
+ */
+ private final SalNode targetNode;
+
+ /**
+ * The statistics read transaction state.
+ */
+ private final AtomicInteger txState = new AtomicInteger(STATE_INITIAL);
+
+ /**
+ * Construct a new instance.
+ *
+ * @param snode A {@link SalNode} which specifies the target switch.
+ */
+ public NodeFlowStatsReader(SalNode snode) {
+ targetNode = snode;
+ }
+
+ /**
+ * Return a future associated with the task to read flow statistics
+ * about the given VTN flow entry.
+ *
+ * @param vfent The target flow entry.
+ * @return A {@link VTNFuture} instance which returns the flow statistics
+ * about the specified flow entry.
+ */
+ public VTNFuture<FlowStatsRecord> get(VtnFlowEntry vfent) {
+ assert vfent.getNode().equals(targetNode.getNodeId());
+
+ BigInteger cookie = vfent.getCookie().getValue();
+ SettableVTNFuture<FlowStatsRecord> f = getFuture(cookie);
+ if (txState.get() >= STATE_COMPLETED) {
+ // No more statistics will be received.
+ f.set(null);
+ }
+
+ return f;
+ }
+
+ /**
+ * Start the flow statistics read transaction.
+ *
+ * <p>
+ * This method must be called only once.
+ * No flow statistics will be returned if this method is not called.
+ * </p>
+ *
+ * @param srs The flow statistics reader service.
+ */
+ public void start(StatsReaderService srs) {
+ if (txState.compareAndSet(STATE_INITIAL, STATE_STARTING)) {
+ boolean succeeded = false;
+ try {
+ if (srs.start(targetNode, this)) {
+ txState.compareAndSet(STATE_STARTING, STATE_STARTED);
+ succeeded = true;
+ }
+ } finally {
+ if (!succeeded) {
+ txState.set(STATE_FAILED);
+ canceled();
+ }
+ }
+ }
+ }
+
+ /**
+ * Return a future which returns the flow statistics information about the
+ * given VTN data flow.
+ *
+ * @param cookie The flow cookie which specifies the target flow entry.
+ * @return A {@link SettableVTNFuture} instance.
+ */
+ private SettableVTNFuture<FlowStatsRecord> getFuture(BigInteger cookie) {
+ SettableVTNFuture<FlowStatsRecord> f = txMap.get(cookie);
+ if (f == null) {
+ // Associate a new feature with the given VTN data flow.
+ f = new SettableVTNFuture<>();
+ SettableVTNFuture<FlowStatsRecord> old =
+ txMap.putIfAbsent(cookie, f);
+ if (old != null) {
+ // Lost the race.
+ f = old;
+ }
+ }
+
+ return f;
+ }
+
+ /**
+ * Invoked when the statistics read transaction has completed.
+ */
+ private void completed() {
+ txState.set(STATE_COMPLETED);
+ canceled();
+ }
+
+ /**
+ * Make all requests for statistics fail.
+ */
+ private void canceled() {
+ for (SettableVTNFuture<FlowStatsRecord> f: txMap.values()) {
+ // This does nothing if this future has a result.
+ f.set(null);
+ }
+ }
+
+ // StatsReaderCallback
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void flowStatsReceived(NodeId node, FlowAndStatisticsMap fstats) {
+ assert node.equals(targetNode.getNodeId());
+
+ FlowCookie cookie = fstats.getCookie();
+ if (cookie != null) {
+ Long time = System.currentTimeMillis();
+ GenericStatistics gen = fstats;
+ FlowStatsRecord fsr = new FlowStatsRecordBuilder(gen).
+ setTime(time).build();
+ getFuture(cookie.getValue()).set(fsr);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void transactionCompleted() {
+ completed();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void transactionCanceled() {
+ completed();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 NEC Corporation
+ * 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.vtn.manager.internal.flow.stats;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowAndStatisticsMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+
+/**
+ * {@code StatsReaderCallback} is a callback for accepting flow statistics
+ * information sent by a switch.
+ *
+ * <p>
+ * This callback is always invoked on a global single thread.
+ * </p>
+ */
+public interface StatsReaderCallback {
+ /**
+ * Invoked when a flow statistics information has been received.
+ *
+ * @param node A {@link NodeId} instance which specifies the switch
+ * which contains the flow entry.
+ * @param fstats A {@link FlowAndStatisticsMap} instance which contains
+ * the flow statistics send by the switch.
+ */
+ void flowStatsReceived(NodeId node, FlowAndStatisticsMap fstats);
+
+ /**
+ * Invoked when the transaction for reading flow statistics has been
+ * completed successfully.
+ */
+ void transactionCompleted();
+
+ /**
+ * Invoked when the transaction for reading flow statistics has been
+ * canceled.
+ */
+ void transactionCanceled();
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 NEC Corporation
+ * 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.vtn.manager.internal.flow.stats;
+
+import static org.opendaylight.vtn.manager.internal.util.MiscUtils.VERBOSE_LOG;
+import static org.opendaylight.vtn.manager.internal.util.MiscUtils.checkNotNull;
+
+import java.math.BigInteger;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.opendaylight.vtn.manager.VTNException;
+
+import org.opendaylight.vtn.manager.internal.VTNManagerProvider;
+import org.opendaylight.vtn.manager.internal.util.SalNotificationListener;
+import org.opendaylight.vtn.manager.internal.util.concurrent.SettableVTNFuture;
+import org.opendaylight.vtn.manager.internal.util.concurrent.VTNFuture;
+import org.opendaylight.vtn.manager.internal.util.concurrent.VTNThreadPool;
+import org.opendaylight.vtn.manager.internal.util.flow.FlowEntryDesc;
+import org.opendaylight.vtn.manager.internal.util.flow.FlowStatsUtils;
+import org.opendaylight.vtn.manager.internal.util.flow.GetFlowStatsRpc;
+import org.opendaylight.vtn.manager.internal.util.inventory.SalNode;
+
+import org.opendaylight.controller.sal.binding.api.NotificationService;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.VtnFlowEntry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.flow.stats.history.FlowStatsRecord;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.flow.stats.history.FlowStatsRecordBuilder;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.AggregateFlowStatisticsUpdate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowAndStatisticsMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowsStatisticsUpdate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetFlowStatisticsFromFlowTableInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.OpendaylightFlowStatisticsListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.OpendaylightFlowStatisticsService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.and.statistics.map.list.FlowAndStatisticsMapList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.TransactionId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.GenericStatistics;
+
+/**
+ * {@code StatsReaderService} provides interfaces to fetch flow statistics
+ * information from OpenFlow switch.
+ */
+public final class StatsReaderService extends SalNotificationListener
+ implements OpendaylightFlowStatisticsListener {
+ /**
+ * Logger instance.
+ */
+ private static final Logger LOG =
+ LoggerFactory.getLogger(StatsReaderService.class);
+
+ /**
+ * The capacity of the task queue.
+ */
+ private static final int TASK_CAPACITY = 1000000;
+
+ /**
+ * The maximum number of milliseconds to wait for completion of RPC.
+ */
+ private static final long RPC_TIMEOUT = 5000L;
+
+ /**
+ * The timeout for transaction in milliseconds.
+ */
+ private static final long TX_TIMEOUT = 10000L;
+
+ /**
+ * The number of milliseconds to wait for completion of the task thread.
+ */
+ private static final long SHUTDOWN_TIMEOUT = 10000L;
+
+ /**
+ * MD-SAL flow statistics service.
+ */
+ private OpendaylightFlowStatisticsService statsService;
+
+ /**
+ * A timer thread.
+ */
+ private final Timer timer;
+
+ /**
+ * Active transactions.
+ */
+ private final ConcurrentMap<String, ReaderContext> txMap =
+ new ConcurrentHashMap<>();
+
+ /**
+ * A thread to execute transaction tasks.
+ */
+ private final VTNThreadPool taskThread;
+
+ /**
+ * {@code ReaderContext} describes a transaction associated with a request
+ * for flow statistics.
+ */
+ private final class ReaderContext extends TimerTask {
+ /**
+ * The identifier associated with this transaction.
+ */
+ private final String transactionKey;
+
+ /**
+ * The callback to be invoked when flow statistics are received.
+ */
+ private final StatsReaderCallback callback;
+
+ /**
+ * Construct a new instance.
+ *
+ * @param xkey The key string which identifies the transaction.
+ * @param cb The callback to be invoked when flow statistics are
+ * received.
+ */
+ private ReaderContext(String xkey, StatsReaderCallback cb) {
+ transactionKey = xkey;
+ callback = cb;
+ }
+
+ /**
+ * Return the transaction key.
+ *
+ * @return The transaction key.
+ */
+ private String getTransactionKey() {
+ return transactionKey;
+ }
+
+ /**
+ * Return the callback.
+ *
+ * @return A {@link StatsReaderCallback} instance.
+ */
+ private StatsReaderCallback getCallback() {
+ return callback;
+ }
+
+ // Runnable
+
+ /**
+ * Post a task which expires this transaction.
+ */
+ @Override
+ public void run() {
+ if (!taskThread.executeTask(new ExpireTask(this))) {
+ expire(this);
+ }
+ }
+ }
+
+ /**
+ * {@code ExpireTask} expires the transaction.
+ */
+ private final class ExpireTask implements Runnable {
+ /**
+ * The target reader context.
+ */
+ private final ReaderContext context;
+
+ /**
+ * Construct a new instance.
+ *
+ * @param ctx The target reader context.
+ */
+ private ExpireTask(ReaderContext ctx) {
+ context = ctx;
+ }
+
+ // Runnable
+
+ /**
+ * Expire the target transaction.
+ */
+ @Override
+ public void run() {
+ expire(context);
+ }
+ }
+
+ /**
+ * {@code RpcTask} is a base class for tasks which issue an RPC request.
+ */
+ private abstract class RpcTask implements Runnable {
+ /**
+ * Return a MD-SAL node identifier which indicates the target switch.
+ *
+ * @return A MD-SAL node identifier.
+ */
+ protected abstract String getTargetNodeId();
+
+ /**
+ * Return the callback associated with the transaction.
+ *
+ * @return A {@link StatsReaderCallback} instance.
+ */
+ protected abstract StatsReaderCallback getCallback();
+
+ /**
+ * Return an RPC input for getting flow statistics.
+ *
+ * @return A {@link GetFlowStatisticsFromFlowTableInput} instance.
+ */
+ protected abstract GetFlowStatisticsFromFlowTableInput getInput();
+
+ /**
+ * Return a brief description about the request.
+ *
+ * <p>
+ * This method is used only for debugging.
+ * </p>
+ *
+ * @return A brief description about the request.
+ */
+ protected abstract String getDescription();
+
+ // Runnable
+
+ /**
+ * Issue an RPC request for getting flow statistics.
+ */
+ @Override
+ public final void run() {
+ String nid = getTargetNodeId();
+ GetFlowStatisticsFromFlowTableInput input = getInput();
+ GetFlowStatsRpc rpc = new GetFlowStatsRpc(statsService, input);
+ StatsReaderCallback cb = getCallback();
+ String xkey = startTransaction(nid, rpc, cb);
+ if (xkey == null) {
+ cb.transactionCanceled();
+ } else if (LOG.isDebugEnabled()) {
+ LOG.debug("Read transaction has started: xid={}, input={}",
+ xkey, getDescription());
+ }
+ }
+ }
+
+ /**
+ * {@code SingleFlowStatsTask} issues an RPC request which requests
+ * statistics information about the given flow entry.
+ */
+ private final class SingleFlowStatsTask extends RpcTask
+ implements StatsReaderCallback {
+ /**
+ * The target flow entry.
+ */
+ private final VtnFlowEntry targetFlow;
+
+ /**
+ * The node identifier which specifies the target node.
+ */
+ private final String targetNodeId;
+
+ /**
+ * The flow cookie configured in the target VTN flow entry.
+ */
+ private final FlowCookie targetCookie;
+
+ /**
+ * A future associated with the transaction for getting the statistics.
+ */
+ private final SettableVTNFuture<FlowStatsRecord> future =
+ new SettableVTNFuture<>();
+
+ /**
+ * Construct a new instance.
+ *
+ * @param vfent The target VTN flow entry.
+ */
+ private SingleFlowStatsTask(VtnFlowEntry vfent) {
+ NodeId nid = checkNotNull(vfent.getNode(), LOG,
+ "No node in VTN flow entry.");
+ targetFlow = vfent;
+ targetNodeId = nid.getValue();
+ targetCookie = vfent.getCookie();
+ }
+
+ /**
+ * Return the future associated with the read transaction.
+ *
+ * @return A {@link VTNFuture} instance.
+ */
+ private SettableVTNFuture<FlowStatsRecord> getFuture() {
+ return future;
+ }
+
+ // RpcTask
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected String getTargetNodeId() {
+ return targetNodeId;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected StatsReaderCallback getCallback() {
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected GetFlowStatisticsFromFlowTableInput getInput() {
+ return FlowStatsUtils.createGetFlowStatsInput(targetFlow);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected String getDescription() {
+ return new StringBuilder("SingleFlow[").
+ append(new FlowEntryDesc(targetFlow, false)).append(']').
+ toString();
+ }
+
+ // StatsReaderCallback
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void flowStatsReceived(NodeId node,
+ FlowAndStatisticsMap fstats) {
+ if (targetNodeId.equals(node.getValue()) &&
+ targetCookie.equals(fstats.getCookie())) {
+ Long time = System.currentTimeMillis();
+ GenericStatistics gen = fstats;
+ FlowStatsRecord fsr = new FlowStatsRecordBuilder(gen).
+ setTime(time).build();
+ future.set(fsr);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void transactionCompleted() {
+ // Set null as the result of the future.
+ if (future.set(null) && LOG.isDebugEnabled()) {
+ LOG.debug("Flow statistics not found: {}", getDescription());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void transactionCanceled() {
+ future.cancel(false);
+ }
+ }
+
+ /**
+ * {@code AllFlowStatsTask} issues an RPC request which requests statistics
+ * information about all the flow entries present in the given switch.
+ */
+ private final class AllFlowStatsTask extends RpcTask {
+ /**
+ * The target switch.
+ */
+ private final SalNode targetNode;
+
+ /**
+ * The flow stats reader callback.
+ */
+ private final StatsReaderCallback callback;
+
+ /**
+ * Construct a new instance.
+ *
+ * @param snode The target switch.
+ * @param cb The callback to be invoked when flow statistics are
+ * received.
+ */
+ private AllFlowStatsTask(SalNode snode, StatsReaderCallback cb) {
+ targetNode =
+ checkNotNull(snode, LOG, "Target node cannot be null.");
+ callback = cb;
+ }
+
+ // RpcTask
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected String getTargetNodeId() {
+ return targetNode.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected StatsReaderCallback getCallback() {
+ return callback;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected GetFlowStatisticsFromFlowTableInput getInput() {
+ return FlowStatsUtils.createGetFlowStatsInput(targetNode);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected String getDescription() {
+ return new StringBuilder("AllFlows[").
+ append(targetNode).append(']').toString();
+ }
+ }
+
+ /**
+ * {@code NotificationTask} notifies the flow-statistics-update
+ * notification to callback associated with the notification.
+ */
+ private final class NotificationTask implements Runnable {
+ /**
+ * The key string which specifies the tranaction.
+ */
+ private final String transactionKey;
+
+ /**
+ * A flow-statistics-update notification.
+ */
+ private final FlowsStatisticsUpdate notification;
+
+ /**
+ * Construct a new instance.
+ *
+ * @param xkey The key string which identifies the transaction.
+ * @param update A flow-statistics-update notification.
+ */
+ private NotificationTask(String xkey, FlowsStatisticsUpdate update) {
+ transactionKey = xkey;
+ notification = update;
+ }
+
+ // Runnable
+
+ /**
+ * Notifiy the flow statistics to the callback associated with the
+ * transaction.
+ */
+ @Override
+ public void run() {
+ invoke(transactionKey, notification);
+ }
+ }
+
+ /**
+ * Construct a new instance.
+ *
+ * @param provider A VTN Manager provider service.
+ * @param nsv A {@link NotificationService} service instance.
+ */
+ public StatsReaderService(VTNManagerProvider provider,
+ NotificationService nsv) {
+ timer = provider.getTimer();
+ taskThread = new VTNThreadPool("StatsReader Task Thread");
+ addCloseable(taskThread);
+
+ try {
+ // Get MD-SAL RPC services.
+ statsService = provider.
+ getRpcService(OpendaylightFlowStatisticsService.class);
+
+ // Register MD-SAL notification listener.
+ registerListener(nsv);
+ } catch (Exception e) {
+ String msg = "Failed to initialize VTN flow statistics reader.";
+ LOG.error(msg, e);
+ close();
+ throw new IllegalStateException(msg, e);
+ }
+ }
+
+ /**
+ * Get the statistics information about the given VTN flow entry.
+ *
+ * @param vfent The target VTN flow entry.
+ * @return A {@link VTNFuture} instance associated with the stats read
+ * transaction.
+ */
+ public VTNFuture<FlowStatsRecord> get(VtnFlowEntry vfent) {
+ SingleFlowStatsTask task = new SingleFlowStatsTask(vfent);
+ SettableVTNFuture<FlowStatsRecord> f = task.getFuture();
+ if (!taskThread.executeTask(task)) {
+ f.set(null);
+ }
+
+ return f;
+ }
+
+ /**
+ * Start the flow stats reader transaction for getting statistics
+ * information about all flow entries present in the given switch.
+ *
+ * @param snode The target switch.
+ * @param cb The callback to be invoked when flow statistics are
+ * received.
+ * @return {@code true} on success. {@code false} on failure.
+ */
+ public boolean start(SalNode snode, StatsReaderCallback cb) {
+ return taskThread.executeTask(new AllFlowStatsTask(snode, cb));
+ }
+
+ /**
+ * Shut down the flow statistics reader service.
+ */
+ public void shutdown() {
+ taskThread.shutdown();
+ }
+
+ /**
+ * Wait for completion of the given RPC, and return the transaction ID
+ * return by the RPC.
+ *
+ * @param nid A MD-SAL node identifier which specifies the target switch.
+ * @param rpc A {@link GetFlowStatsRpc} instance.
+ * @param cb A callback associated with the given RPC.
+ * @return The key string associated with the read transaction on success.
+ * {@code null} on failure.
+ */
+ private String startTransaction(String nid, GetFlowStatsRpc rpc,
+ StatsReaderCallback cb) {
+ try {
+ BigInteger xid = rpc.getTransactionId(
+ RPC_TIMEOUT, TimeUnit.MILLISECONDS, LOG);
+ String xkey = FlowStatsUtils.createTransactionKey(nid, xid);
+ ReaderContext ctx = new ReaderContext(xkey, cb);
+ if (txMap.putIfAbsent(xkey, ctx) == null) {
+ // Register expiration timer.
+ timer.schedule(ctx, TX_TIMEOUT);
+ return xkey;
+ }
+ ctx.cancel();
+ LOG.error("Duplicated XID: {}", xkey);
+ } catch (VTNException | RuntimeException e) {
+ LOG.error("Failed to issue flow stats RPC: " + rpc.getName(), e);
+ }
+
+ return null;
+ }
+
+ /**
+ * Invoke the callback associated with the given transaction.
+ *
+ * @param xkey The key string which identifies the transaction.
+ * @param update A flow-statistics-update notification.
+ */
+ private void invoke(String xkey, FlowsStatisticsUpdate update) {
+ ReaderContext ctx = txMap.get(xkey);
+ if (ctx == null) {
+ VERBOSE_LOG.trace("Ignore unwanted notification: xid={}", xkey);
+ return;
+ }
+
+ StatsReaderCallback cb = ctx.getCallback();
+ List<FlowAndStatisticsMapList> mapList =
+ update.getFlowAndStatisticsMapList();
+ if (mapList != null) {
+ NodeId node = update.getId();
+ for (FlowAndStatisticsMapList map: mapList) {
+ String err = FlowStatsUtils.check(map);
+ if (err == null) {
+ cb.flowStatsReceived(node, map);
+ } else {
+ LOG.warn("Ignore broken flow statistics: {}: xid={}, " +
+ "flow={}", err, xkey, map);
+ }
+ }
+ }
+
+ if (!Boolean.TRUE.equals(update.isMoreReplies()) &&
+ txMap.remove(xkey, ctx)) {
+ // All statistics has been received.
+ ctx.cancel();
+ LOG.debug("Read transaction has completed: xid={}", xkey);
+ cb.transactionCompleted();
+ }
+ }
+
+ /**
+ * Expire the given transaction.
+ *
+ * @param ctx The target reader context.
+ */
+ private void expire(ReaderContext ctx) {
+ String xkey = ctx.getTransactionKey();
+ if (txMap.remove(xkey, ctx)) {
+ LOG.error("Transaction has expired: xid={}", xkey);
+ ctx.getCallback().transactionCanceled();
+ }
+ }
+
+ /**
+ * Record a broken notification as a warning message.
+ * ignored.
+ *
+ * @param notification A {@link FlowsStatisticsUpdate} instance.
+ * @param msg A warning message.
+ */
+ private void broken(FlowsStatisticsUpdate notification, String msg) {
+ LOG.warn("Ignore broken flow-statistics-update notification: {}: {}",
+ msg, notification);
+ }
+
+ // AutoCloseable
+
+ /**
+ * Close the flow statistics reader service.
+ */
+ @Override
+ public void close() {
+ shutdown();
+
+ // Cancel all transactions.
+ while (!txMap.isEmpty()) {
+ Set<String> xkeys = new HashSet<>(txMap.keySet());
+ for (String xkey: xkeys) {
+ ReaderContext ctx = txMap.remove(xkey);
+ if (ctx != null) {
+ ctx.cancel();
+ ctx.getCallback().transactionCanceled();
+ }
+ }
+ }
+
+ super.close();
+ }
+
+ // CloseableContainer
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected Logger getLogger() {
+ return LOG;
+ }
+
+ // OpendaylightFlowStatisticsListener
+
+ /**
+ * Invoked when an aggregated flow statistics for a flow table has been
+ * send by a switch.
+ *
+ * @param notification An {@link AggregateFlowStatisticsUpdate} instance.
+ */
+ @Override
+ public void onAggregateFlowStatisticsUpdate(
+ AggregateFlowStatisticsUpdate notification) {
+ // Nothing to do.
+ }
+
+ /**
+ * Invoked when a flow statistics has been send by a switch.
+ *
+ * @param notification A {@link FlowsStatisticsUpdate} instance.
+ */
+ @Override
+ public void onFlowsStatisticsUpdate(FlowsStatisticsUpdate notification) {
+ if (notification == null) {
+ LOG.warn("Null flow-statistics-update notification.");
+ return;
+ }
+
+ NodeId node = notification.getId();
+ if (node == null) {
+ broken(notification, "No node-id");
+ return;
+ }
+
+ TransactionId txId = notification.getTransactionId();
+ if (txId == null) {
+ broken(notification, "No XID");
+ return;
+ }
+
+ String xkey = FlowStatsUtils.createTransactionKey(
+ node.getValue(), txId.getValue());
+ taskThread.executeTask(new NotificationTask(xkey, notification));
+ }
+}
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.tenant.flow.info.VtnDataFlowKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.FlowStatsHistory;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.VtnFlowEntry;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.flow.stats.history.FlowStatsRecord;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.flow.stats.history.FlowStatsRecordBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.flows.VtnFlowTable;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
}
// Update the hisotry of flow statistics if needed.
- FlowStatsRecord fsr = new FlowStatsRecordBuilder(fstats).
- setTime(systemTime).setPeriodic(true).build();
- FlowStatsHistory history =
- FlowStatsUtils.update(vdf.getFlowStatsHistory(), fsr);
- if (history == null) {
- traceLog("Skip flow entry: %s: Too rapid.", id);
- } else {
- traceLog("Flow statistics has been updated: %s, %s",
- id, fid.getValue());
- InstanceIdentifier<FlowStatsHistory> path =
- FlowStatsUtils.getIdentifier(tname, key);
- LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
- tx.put(oper, path, history, false);
- }
+ FlowStatsHistory history = FlowStatsUtils.addPeriodic(
+ vdf.getFlowStatsHistory(), systemTime, fstats);
+ InstanceIdentifier<FlowStatsHistory> path =
+ FlowStatsUtils.getIdentifier(tname, key);
+ LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
+ tx.put(oper, path, history, false);
+ traceLog("Flow statistics has been updated: %s, %s",
+ id, fid.getValue());
}
/**
package org.opendaylight.vtn.manager.internal.inventory;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
return !updated.isEmpty();
}
+ /**
+ * Return a map which keeps updated inventories.
+ *
+ * <p>
+ * This method is used only for test.
+ * </p>
+ *
+ * @return A map which keeps updated inventories.
+ */
+ final Map<L, InstanceIdentifier<T>> getUpdatedMap() {
+ return Collections.unmodifiableMap(updated);
+ }
+
/**
* Return a {@link Logger} instance.
*
import org.slf4j.Logger;
+import com.google.common.base.Optional;
+
import org.opendaylight.vtn.manager.VTNException;
import org.opendaylight.vtn.manager.internal.TxContext;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.vtn.nodes.VtnNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.vtn.nodes.VtnNodeBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
/**
* A MD-SAL datastore transaction task that updates VTN node information.
protected void add(TxContext ctx, ReadWriteTransaction tx, SalNode snode,
InstanceIdentifier<FlowCapableNode> path,
FlowCapableNode fcn) throws VTNException {
+ // Read MD-SAL node.
+ InstanceIdentifier<Node> npath = snode.getNodeIdentifier();
+ LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
+ Optional<Node> opt = DataStoreUtils.read(tx, oper, npath);
+ VtnNodeBuilder builder;
+ if (opt.isPresent()) {
+ builder = InventoryUtils.toVtnNodeBuilder(opt.get());
+ } else {
+ // The given MD-SAL node is no longer present.
+ // VTN node will be removed soon.
+ builder = InventoryUtils.toVtnNodeBuilder(snode.getNodeId());
+ }
+
// Create a VTN node.
- VtnNode vnode = InventoryUtils.toVtnNodeBuilder(snode.getNodeId()).
- build();
- tx.merge(LogicalDatastoreType.OPERATIONAL,
- snode.getVtnNodeIdentifier(), vnode, true);
+ tx.merge(oper, snode.getVtnNodeIdentifier(), builder.build(), true);
}
/**
*/
static {
PATH_COMPARATOR = new IdentifierTargetComparator().
- setOrder(VtnPort.class, 1).
- setOrder(VtnNode.class, 2);
+ setOrder(VtnNode.class, 1).
+ setOrder(VtnPort.class, 2);
}
/**
*/
@Override
protected boolean getOrder(VtnUpdateType type) {
- // Creation events should be processed from outer to inner.
- // Other events should be processed from inner to outer.
- return (type != VtnUpdateType.CREATED);
+ // Removal events should be processed from inner to outer.
+ // Other events should be processed from outer to inner.
+ return (type != VtnUpdateType.REMOVED);
}
/**
*/
private int poolState = STATE_RUNNING;
+ /**
+ * Construct a new thread pool which has a single worker thread.
+ *
+ * @param prefix A prefix for the name of worker threads.
+ */
+ public VTNThreadPool(String prefix) {
+ this(prefix, 1, 0);
+ }
+
/**
* Construct a new thread pool.
*
* Main routine of worker thread.
*/
@Override
- public void run() {
+ public final void run() {
LOG.trace("Start");
for (Runnable r = waitFor(); r != null; r = waitFor()) {
/**
* {@code MainThread} class implements the main thread of the pool.
*/
- protected class MainThread extends WorkerThread {
+ private final class MainThread extends WorkerThread {
/**
* Construct a main thread.
*
* @param name The name of this thread.
*/
- protected MainThread(String name) {
+ private MainThread(String name) {
super(name);
}
private final VtnFlowEntry flowEntry;
/**
- * Construct a new instance.
+ * Determine whether flow instructions should be embedded into a log
+ * record or not.
+ */
+ private final boolean needInstruction;
+
+ /**
+ * Construct a new instance which describes a log record including
+ * flow instructions.
*
* @param vfent A {@link VtnFlowEntry} instance.
*/
public FlowEntryDesc(VtnFlowEntry vfent) {
+ this(vfent, true);
+ }
+
+ /**
+ * Construct a new instance which describes a log record.
+ *
+ * @param vfent A {@link VtnFlowEntry} instance.
+ * @param inst Flow instructions are embedded into a log record
+ * if {@code true}.
+ */
+ public FlowEntryDesc(VtnFlowEntry vfent, boolean inst) {
flowEntry = vfent;
+ needInstruction = inst;
}
// Object
append(',').append(flowEntry.getHardTimeout()).
append("), node=").append(flowEntry.getNode().getValue()).
append(", ingress=").append(ingress).
- append(", cond={").append(cond).
- append("}, actions={");
+ append(", cond={").append(cond);
- FlowActionConverter converter = FlowActionConverter.getInstance();
- OrderedComparator comp = new OrderedComparator();
- List<Action> actions = FlowActionUtils.
- getActions(flowEntry.getInstructions(), comp);
- String sep = "";
- for (Action action: actions) {
- builder.append(sep).
- append(converter.getDescription(action.getAction()));
- sep = ", ";
+ if (needInstruction) {
+ builder.append("}, actions={");
+ FlowActionConverter converter = FlowActionConverter.getInstance();
+ OrderedComparator comp = new OrderedComparator();
+ List<Action> actions = FlowActionUtils.
+ getActions(flowEntry.getInstructions(), comp);
+ String sep = "";
+ for (Action action: actions) {
+ builder.append(sep).
+ append(converter.getDescription(action.getAction()));
+ sep = ", ";
+ }
}
return builder.append('}').toString();
package org.opendaylight.vtn.manager.internal.util.flow;
+import static org.opendaylight.vtn.manager.internal.util.flow.FlowUtils.COOKIE_MASK_ALL;
+import static org.opendaylight.vtn.manager.internal.util.flow.FlowUtils.COOKIE_MASK_VTN;
+import static org.opendaylight.vtn.manager.internal.util.flow.FlowUtils.COOKIE_VTN;
+import static org.opendaylight.vtn.manager.internal.util.flow.FlowUtils.TABLE_ID;
+
+import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.NavigableMap;
import java.util.TreeMap;
import org.opendaylight.vtn.manager.VTNException;
import org.opendaylight.vtn.manager.internal.util.DataStoreUtils;
+import org.opendaylight.vtn.manager.internal.util.inventory.SalNode;
import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.tenant.flow.info.VtnDataFlowKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.FlowStatsHistory;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.FlowStatsHistoryBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.VtnFlowEntry;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.flow.stats.history.FlowStatsRecord;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.flow.stats.history.FlowStatsRecordBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
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.statistics.rev130819.FlowStatisticsData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetFlowStatisticsFromFlowTableInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetFlowStatisticsFromFlowTableInputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.statistics.FlowStatistics;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
*/
public final class FlowStatsUtils {
/**
- * The lifetime, in milliseconds, of statistics history record.
+ * The lifetime of statistics history record in milliseconds.
*/
private static final long HISTORY_LIFETIME = 60000L;
*/
private FlowStatsUtils() {}
-
/**
* Create the instance identifier for the statistics history of the
* given VTN data flow.
return InstanceIdentifier.builder(Nodes.class).
child(Node.class, new NodeKey(node)).
augmentation(FlowCapableNode.class).
- child(Table.class, new TableKey(FlowUtils.TABLE_ID)).
+ child(Table.class, new TableKey(TABLE_ID)).
child(Flow.class, new FlowKey(flowId)).
augmentation(FlowStatisticsData.class).
child(FlowStatistics.class).
build();
}
+ /**
+ * Construct an RPC input which requests flow statistics for the given
+ * VTN flow entry.
+ *
+ * @param vfent The target VTN flow entry.
+ * @return A {@link GetFlowStatisticsFromFlowTableInput} instance.
+ */
+ public static GetFlowStatisticsFromFlowTableInput createGetFlowStatsInput(
+ VtnFlowEntry vfent) {
+ GetFlowStatisticsFromFlowTableInputBuilder builder =
+ new GetFlowStatisticsFromFlowTableInputBuilder(vfent);
+
+ SalNode snode = SalNode.create(vfent.getNode());
+ return builder.setNode(snode.getNodeRef()).
+ setCookieMask(COOKIE_MASK_ALL).
+ build();
+ }
+
+ /**
+ * Construct an RPC input which requests statistics information about all
+ * VTN flow entries present in the given switch.
+ *
+ * <p>
+ * Note that this method uses flow cookie to select VTN flow entries.
+ * So OpenFlow 1.0 switch will return all the flow entries when it
+ * receives an input returned by this method.
+ * </p>
+ *
+ * @param snode A {@link SalNode} instance which specifies the target
+ * switch.
+ * @return A {@link GetFlowStatisticsFromFlowTableInput} instance.
+ */
+ public static GetFlowStatisticsFromFlowTableInput createGetFlowStatsInput(
+ SalNode snode) {
+ return new GetFlowStatisticsFromFlowTableInputBuilder().
+ setNode(snode.getNodeRef()).
+ setCookie(COOKIE_VTN).
+ setCookieMask(COOKIE_MASK_VTN).
+ setTableId(TABLE_ID).
+ build();
+ }
+
/**
* Read statistics information for the given MD-SAL flow.
*
}
/**
- * Update the flow statistics history.
+ * Put all the flow statistics records in the given statistics history
+ * into a new navigable map.
*
- * @param history History of flow statistics.
- * @param newRecord A statistics record to be added.
- * @return A {@link FlowStatsHistory} instance if the history has to be
- * updated. Otherwise {@code null}.
+ * <p>
+ * All statistics records are associated with its creation time in a
+ * returned map.
+ * </p>
+ *
+ * @param history History of flow statistics.
+ * @return A {@link NavigableMap} instance.
*/
- public static FlowStatsHistory update(FlowStatsHistory history,
- FlowStatsRecord newRecord) {
+ public static NavigableMap<Long, FlowStatsRecord> toNavigableMap(
+ FlowStatsHistory history) {
TreeMap<Long, FlowStatsRecord> map = new TreeMap<>();
if (history != null) {
List<FlowStatsRecord> list = history.getFlowStatsRecord();
}
}
+ return map;
+ }
+
+ /**
+ * Add a periodic flow statistics record to the statistics history.
+ *
+ * @param history History of flow statistics.
+ * @param sysTime The system time when the statistics is derived.
+ * @param gstats A statistics record to be added.
+ * @return A {@link FlowStatsHistory} instance.
+ */
+ public static FlowStatsHistory addPeriodic(
+ FlowStatsHistory history, Long sysTime, GenericStatistics gstats) {
// Expire old records.
- Long newTime = newRecord.getTime();
- long expire = newTime.longValue() - HISTORY_LIFETIME;
- for (Long time = map.firstKey(); time != null; time = map.firstKey()) {
- if (expire > time.longValue()) {
- map.remove(time);
- } else {
- break;
+ Long expire = Long.valueOf(sysTime.longValue() - HISTORY_LIFETIME);
+ NavigableMap<Long, FlowStatsRecord> map =
+ toNavigableMap(history).tailMap(expire, true);
+
+ // Determine the latest record in the given history.
+ GenericStatistics newStats = gstats;
+ Map.Entry<Long, FlowStatsRecord> latest = map.lastEntry();
+ if (latest != null) {
+ FlowStatsRecord lfsr = latest.getValue();
+ if (!Boolean.TRUE.equals(lfsr.isPeriodic())) {
+ // Ensure that the given statistics is newer than the latest
+ // history record.
+ Duration nd = newStats.getDuration();
+ Duration ld = lfsr.getDuration();
+ if (FlowUtils.compare(nd, ld) < 0) {
+ // The latest record is newer than the given statistics.
+ // Use the latest record as a new periodic history record.
+ newStats = lfsr;
+ map.remove(latest.getKey());
+ }
}
}
- // Determine the latest record.
+ // Return a new statistics history.
+ FlowStatsRecord newRecord = new FlowStatsRecordBuilder(newStats).
+ setTime(sysTime).setPeriodic(true).build();
+ map.put(sysTime, newRecord);
+ List<FlowStatsRecord> list = new ArrayList<>(map.values());
+ return new FlowStatsHistoryBuilder().
+ setFlowStatsRecord(list).build();
+ }
+
+ /**
+ * Add a non-periodic flow statistics record to the statistics history.
+ *
+ * @param history History of flow statistics.
+ * @param newRecord A flow statistics record to be added.
+ * Note that periodic flag in this record must be false.
+ * @return A {@link FlowStatsHistory} instance if the statistics history
+ * needs to be updated. Otherwise {@code null}.
+ */
+ public static FlowStatsHistory addNonPeriodic(
+ FlowStatsHistory history, FlowStatsRecord newRecord) {
+ assert !Boolean.TRUE.equals(newRecord.isPeriodic());
+ Long newTime = newRecord.getTime();
+ NavigableMap<Long, FlowStatsRecord> map = toNavigableMap(history);
+
+ // Determine the latest record in the given history.
Map.Entry<Long, FlowStatsRecord> latest = map.lastEntry();
if (latest != null) {
- // Prevent too frequent records.
- Long ltime = latest.getKey();
- if (newTime.longValue() - ltime.longValue() < MIN_INTERVAL) {
+ // Don't add the given record if it is older than the latest
+ // record.
+ Long latestTime = latest.getKey();
+ long ltime = latestTime.longValue();
+ long time = newTime.longValue();
+ if (time <= ltime) {
+ return null;
+ }
+
+ if (time - ltime < MIN_INTERVAL) {
+ // The interval between the latest record and the given
+ // record is too short.
FlowStatsRecord lfsr = latest.getValue();
if (Boolean.TRUE.equals(lfsr.isPeriodic())) {
// Don't remove periodic record.
return null;
}
- map.remove(ltime);
+
+ // Overwrite the latest record.
+ map.remove(latestTime);
}
}
- // Return a new statistics history.
map.put(newTime, newRecord);
List<FlowStatsRecord> list = new ArrayList<>(map.values());
return new FlowStatsHistoryBuilder().
setFlowStatsRecord(list).build();
}
+
+ /**
+ * Create a key string which identifies the transaction for getting flow
+ * statistics from switch.
+ *
+ * @param nid A MD-SAL node identifier which specifies the switch.
+ * @param xid A OpenFlow transaction ID.
+ * @return A key string which identifies the read transaction.
+ */
+ public static String createTransactionKey(String nid, BigInteger xid) {
+ return new StringBuilder(xid.toString()).
+ append('@').append(nid).toString();
+ }
}
/**
* A flow cookie which contains the {@link #VTN_FLOW_COOKIE} bits.
*/
- private static final FlowCookie COOKIE_VTN =
+ public static final FlowCookie COOKIE_VTN =
new FlowCookie(NumberUtils.getUnsigned(VTN_FLOW_COOKIE));
/**
* A flow cookie mask which specifies all bits in the cookie.
*/
- private static final FlowCookie COOKIE_MASK_ALL =
+ public static final FlowCookie COOKIE_MASK_ALL =
new FlowCookie(NumberUtils.getUnsigned(-1L));
/**
* A flow cookie mask which specifies the {@link #VTN_FLOW_COOKIE} bits.
*/
- private static final FlowCookie COOKIE_MASK_VTN =
+ public static final FlowCookie COOKIE_MASK_VTN =
new FlowCookie(NumberUtils.getUnsigned(VTN_FLOW_COOKIE_MASK));
/**
return 0;
}
+ /**
+ * Compare the given two {@link Duration} instances.
+ *
+ * @param d1 The first instance to be compared.
+ * @param d2 The second instance to be compared.
+ * @return A negative integer, zero, or a positive integer as the first
+ * instance is less than, equal to, or greater than the second
+ * instance.
+ */
+ public static int compare(Duration d1, Duration d2) {
+ long s1 = MiscUtils.longValue(d1.getSecond());
+ long s2 = MiscUtils.longValue(d2.getSecond());
+ int ret = Long.compare(s1, s2);
+ if (ret == 0) {
+ long n1 = MiscUtils.longValue(d1.getNanosecond());
+ long n2 = MiscUtils.longValue(d2.getNanosecond());
+ ret = Long.compare(n1, n2);
+ }
+
+ return ret;
+ }
+
/**
* Convert the given {@link VirtualRoute} instances into a list of
* {@link VNodeRoute} instances.
* @param snode A {@link SalNode} instance which specifies the target
* switch.
* @param vfent A {@link VtnFlowEntry} instance.
- * @return A {@link RemoveFlowInput} instance on success.
- * {@code null} if the target node is not present.
+ * @return A {@link RemoveFlowInput} instance.
*/
public static RemoveFlowInput createRemoveFlowInput(
SalNode snode, VtnFlowEntry vfent) {
- Short table = vfent.getTableId();
+ Uri uri = createTxUri(vfent, "remove-flow:");
+ return createRemoveFlowInput(snode, vfent, uri);
+ }
+
+ /**
+ * Construct an RPC input to uninstall the specified MD-SAL flow entry.
+ *
+ * <p>
+ * Note that the caller must ensure that the specified switch is present.
+ * </p>
+ *
+ * @param snode A {@link SalNode} instance which specifies the target
+ * switch.
+ * @param flow A {@link Flow} instance.
+ * @param uri A {@link Uri} to be assigned to the input.
+ * @return A {@link RemoveFlowInput} instance.
+ */
+ public static RemoveFlowInput createRemoveFlowInput(
+ SalNode snode, Flow flow, Uri uri) {
+ Short table = flow.getTableId();
FlowTableRef tref =
new FlowTableRef(snode.getFlowTableIdentifier(table));
- return new RemoveFlowInputBuilder((Flow)vfent).
+ return new RemoveFlowInputBuilder(flow).
setNode(snode.getNodeRef()).
setFlowTable(tref).
- setTransactionUri(createTxUri(vfent, "remove-flow:")).
+ setTransactionUri(uri).
setCookieMask(COOKIE_MASK_ALL).
setStrict(true).
setBarrier(true).
--- /dev/null
+/*
+ * Copyright (c) 2015 NEC Corporation
+ * 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.vtn.manager.internal.util.flow;
+
+import java.math.BigInteger;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+
+import org.opendaylight.vtn.manager.VTNException;
+
+import org.opendaylight.vtn.manager.internal.util.rpc.RpcInvocation;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetFlowStatisticsFromFlowTableInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetFlowStatisticsFromFlowTableOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.OpendaylightFlowStatisticsService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.TransactionId;
+
+/**
+ * {@code GetFlowStatsRpc} describes an invocation of
+ * get-flow-statistics-from-flow-table RPC provided by the MD-SAL flow
+ * statistics service.
+ */
+public final class GetFlowStatsRpc
+ extends RpcInvocation<GetFlowStatisticsFromFlowTableInput,
+ GetFlowStatisticsFromFlowTableOutput> {
+ /**
+ * Issue a get-flow-statistics-from-flow-table RPC request.
+ *
+ * @param fss MD-SAL flow statistics service.
+ * @param in The RPC input.
+ */
+ public GetFlowStatsRpc(OpendaylightFlowStatisticsService fss,
+ GetFlowStatisticsFromFlowTableInput in) {
+ super(in, fss.getFlowStatisticsFromFlowTable(in));
+ }
+
+ /**
+ * Wait for the RPC execution to complete, and return the transaction ID
+ * returned by the RPC.
+ *
+ * @param timeout The maximum time to wait.
+ * @param unit The time unit of the {@code timeout} argument.
+ * @param logger A {@link Logger} instance.
+ * @return The transaction ID associated with the read transaction.
+ * @throws VTNException An error occurred.
+ */
+ public BigInteger getTransactionId(long timeout, TimeUnit unit,
+ Logger logger) throws VTNException {
+ GetFlowStatisticsFromFlowTableOutput result =
+ getResult(timeout, unit, logger);
+ if (result == null) {
+ String msg = "Flow stats RPC did not return the result";
+ logger.error("{}: {}", getName(), msg);
+ throw new VTNException(msg);
+ }
+
+ TransactionId txId = result.getTransactionId();
+ if (txId == null) {
+ String msg = "Flow stats RPC did not return XID";
+ logger.error("{}: {}: {}", getName(), msg, result);
+ throw new VTNException(msg);
+ }
+
+ return txId.getValue();
+ }
+
+ // RpcInvocation
+
+ /**
+ * Return the name of the RPC.
+ *
+ * @return "get-flow-stats".
+ */
+ @Override
+ public String getName() {
+ return "get-flow-stats";
+ }
+}
-
-/**
+/*
* Copyright (c) 2015 NEC Corporation
* All rights reserved.
*
* 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.vtn.manager.internal;
import org.junit.Assert;
import org.opendaylight.controller.sal.core.NodeConnector;
import org.opendaylight.vtn.manager.internal.util.inventory.SalPort;
-
/**
- * JUnit test for {@ SpecificPortFilter}
+ * JUnit test for {@link SpecificPortFilter}
*/
-
public class SpecificPortFilterTest extends TestBase {
- /**
- * Test method for
- * {@link SpecificPortFilter#accepts(NodeConnecter,VtnPort)}
- */
-
+ /**
+ * Test method for
+ * {@link SpecificPortFilter#accept(NodeConnector,VtnPort)}
+ */
@Test
public void testAccept() {
SpecificPortFilter specificPortFilter = null;
/**
* JUnit test for {@link OperationalListener}.
*/
-
public class OperationalListenerTest extends TestBase {
/**
/**
* Test method for
- * {@link OperationalListener#awaitConfig()}.
+ * {@link OperationalListener#awaitConfig(long)}.
*/
@Test
public void awaitConfigTest() {
/**
* Test method for
- * {@link OperationalListener#enterEvent()}.
+ * {@link OperationalListener#enterEvent(AsyncDataChangeEvent)}.
*/
@Test
public void enterEventTest() {
/**
* Test method for
- * {@link OperationalListener#exitEvent()}.
+ * {@link OperationalListener#exitEvent(Void)}.
*/
@Test
public void exitEventTest() {
/**
* Test method for
- * {@link OperationalListener#onCreated()}.
+ * {@link OperationalListener#onCreated(Void,IdentifiedData)}.
*/
@Test
public void onCreatedTest() {
/**
* Test method for
- * {@link OperationalListener#onUpdated()}.
+ * {@link OperationalListener#onUpdated(Void,ChangedData)}.
*/
@Test
public void onUpdatedTest() {
/**
* Test method for
- * {@link OperationalListener#onRemoved()}.
+ * {@link OperationalListener#onRemoved(Void,IdentifiedData)}.
*/
@Test
public void onRemovedTest() {
-/**
+/*
* Copyright (c) 2015 NEC Corporation
* All rights reserved.
*
package org.opendaylight.vtn.manager.internal.inventory;
-import org.junit.Test;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import java.util.HashMap;
+import java.util.Map;
+
import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.opendaylight.vtn.manager.internal.TestBase;
-import org.mockito.Mockito;
-import org.opendaylight.vtn.manager.internal.VTNManagerProvider;
-import org.opendaylight.controller.md.sal.binding.api.DataBroker;
-import org.opendaylight.yangtools.yang.binding.DataObject;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.opendaylight.vtn.manager.internal.TxQueue;
+import org.opendaylight.vtn.manager.internal.TxTask;
import org.opendaylight.vtn.manager.internal.util.ChangedData;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.vtn.manager.internal.util.IdentifiedData;
-import org.opendaylight.vtn.manager.internal.TxQueue;
-import org.opendaylight.vtn.manager.internal.util.tx.TxQueueImpl;
import org.opendaylight.vtn.manager.internal.util.inventory.SalPort;
+
+import org.opendaylight.vtn.manager.internal.TestBase;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+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.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
-
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
/**
- * JUnit test for {@link NodeConnectorListener}
- *
- * NodeConnectorListener test class is to test Listener class listens
- * the change of MD-SAL node connectors or not.
+ * JUnit test for {@link NodeConnectorListener}.
*/
-
public class NodeConnectorListenerTest extends TestBase{
/**
- * Logger instance.
- */
- private static final Logger LOG = LoggerFactory.getLogger(NodeConnectorListenerTest.class);
- /**
- * create a mock object for DataBroker class
- */
- DataBroker broker = Mockito.mock(DataBroker.class);
- /**
- * create a mock object for VTNManagerProvider class
- */
- VTNManagerProvider provider = Mockito.mock(VTNManagerProvider.class);
- /**
- * create a mock object for DataObject class
- */
- DataObject dataObject = Mockito.mock(DataObject.class);
- /**
- * create a mock object for FlowCapableNodeConnector class
- */
- FlowCapableNodeConnector fCapableNC = Mockito.mock(FlowCapableNodeConnector.class);
- /**
- * create a mock object for AsyncDataChangeEvent class
- */
- AsyncDataChangeEvent asyncDataChangeEvent = Mockito.mock(AsyncDataChangeEvent.class);
- /**
- * create a object for TxQueue
+ * Mock-up of {@link TxQueue}.
*/
- TxQueue txQueue = new TxQueueImpl("QUEUE1", provider);
+ private TxQueue txQueue;
+
/**
- * create a object for NodeConnectorListener
+ * Mock-up of {@link DataBroker}.
*/
- NodeConnectorListener nodeConnectorListener = new NodeConnectorListener(txQueue, broker);
+ private DataBroker dataBroker;
+
/**
- * create a object for PortUpdateTask
+ * Mock-up of {@link ListenerRegistration}.
*/
- PortUpdateTask portUpdateTask = new PortUpdateTask(LOG);
+ private ListenerRegistration registration;
+
/**
- * create a object for InstanceIdentifier
+ * A {@link NodeConnectorListener} instance for test.
*/
- InstanceIdentifier<FlowCapableNodeConnector> data = InstanceIdentifier.builder(Nodes.class).child(Node.class).child(NodeConnector.class).augmentation(FlowCapableNodeConnector.class).build();
+ private NodeConnectorListener ncListener;
- Long nodeId = 100L;
- Long portId = 200L;
/**
- * create a object for SalPort
+ * Set up test environment.
*/
- SalPort salPort = new SalPort(nodeId, portId, "port address");
+ @Before
+ public void setUp() {
+ dataBroker = mock(DataBroker.class);
+ txQueue = mock(TxQueue.class);
+ registration = mock(ListenerRegistration.class);
+
+ when(dataBroker.registerDataChangeListener(
+ any(LogicalDatastoreType.class), any(InstanceIdentifier.class),
+ any(NodeConnectorListener.class), any(DataChangeScope.class))).
+ thenReturn(registration);
+ ncListener = new NodeConnectorListener(txQueue, dataBroker);
+ }
/**
- * Test method for
- * {@link NodeConnectorListener#getLogger}.
+ * Test case for
+ * {@link NodeConnectorListener#NodeConnectorListener(TxQueue, DataBroker)}.
*/
@Test
- public void testGetLogger() {
-
- assertTrue(nodeConnectorListener.getLogger() instanceof Logger);
+ public void testConstructor() {
+ LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
+ DataChangeScope scope = DataChangeScope.SUBTREE;
+ InstanceIdentifier<FlowCapableNodeConnector> path = getPath();
+ verify(dataBroker).
+ registerDataChangeListener(oper, path, ncListener, scope);
+ verifyZeroInteractions(registration);
+ verifyZeroInteractions(txQueue);
+ assertEquals(FlowCapableNodeConnector.class,
+ ncListener.getTargetType());
}
/**
- * Test method for
- * {@link NodeConnectorListener#onRemoved}.
+ * Test case for
+ * {@link NodeConnectorListener#enterEvent(AsyncDataChangeEvent)}.
*/
@Test
- public void testOnRemoved() {
- try {
- portUpdateTask.addUpdated(data, salPort);
- IdentifiedData identifiedData = new IdentifiedData(data, fCapableNC);
- nodeConnectorListener.onRemoved(portUpdateTask, identifiedData);
- } catch (Exception e) {
- assertTrue(e instanceof ClassCastException);
- }
+ public void testEnterEvent() {
+ AsyncDataChangeEvent ev = null;
+ PortUpdateTask task = ncListener.enterEvent(ev);
+ assertTrue(task instanceof PortUpdateTask);
+ PortUpdateTask task1 = ncListener.enterEvent(ev);
+ assertTrue(task1 instanceof PortUpdateTask);
+ assertNotSame(task, task1);
}
+
/**
- * Test method for
- * {@link NodeConnectorListener#enterEvent}.
+ * Test case for {@link NodeConnectorListener#exitEvent(PortUpdateTask)}.
*/
@Test
- public void testEnterEvent() {
- try {
- assertTrue(nodeConnectorListener.enterEvent(asyncDataChangeEvent) instanceof PortUpdateTask);
- } catch (Exception e) {
- assertTrue(e instanceof Exception);
- }
+ public void testExitEvent() {
+ reset(txQueue);
+ Logger logger = mock(Logger.class);
+ PortUpdateTask task = new PortUpdateTask(logger);
+ ncListener.exitEvent(task);
+ verifyZeroInteractions(txQueue);
+
+ SalPort sport = new SalPort(123L, 45L);
+ InstanceIdentifier<FlowCapableNodeConnector> path = InstanceIdentifier.
+ builder(Nodes.class).
+ child(Node.class, sport.getNodeKey()).
+ child(NodeConnector.class, sport.getNodeConnectorKey()).
+ augmentation(FlowCapableNodeConnector.class).
+ build();
+ task.addUpdated(path, sport);
+ ncListener.exitEvent(task);
+ verify(txQueue, never()).postFirst(any(TxTask.class));
+ verify(txQueue).post(task);
+ verifyZeroInteractions(logger);
}
/**
- * Test method for
- * {@link NodeConnectorListener#onUpdated}.
+ * Test case for
+ * {@link NodeConnectorListener#onCreated(PortUpdateTask,IdentifiedData)}.
*/
@Test
- public void testOnUpdated() {
- try {
- portUpdateTask.addUpdated(data, salPort);
- ChangedData changedData = new ChangedData(data, fCapableNC, fCapableNC);
- nodeConnectorListener.onUpdated(portUpdateTask, changedData);
- } catch (Exception e) {
- assertTrue(e instanceof ClassCastException);
+ public void testOnCreated() {
+ reset(txQueue);
+ FlowCapableNodeConnector fcnc = mock(FlowCapableNodeConnector.class);
+
+ Logger logger = mock(Logger.class);
+ PortUpdateTask task = new PortUpdateTask(logger);
+ assertEquals(false, task.hasUpdates());
+
+ // In case of unsupported port.
+ NodeId unsupportedNode = new NodeId("unknown:1");
+ NodeConnectorId unsupportedPort = new NodeConnectorId("unknown:1:2");
+ InstanceIdentifier<FlowCapableNodeConnector> path = InstanceIdentifier.
+ builder(Nodes.class).
+ child(Node.class, new NodeKey(unsupportedNode)).
+ child(NodeConnector.class, new NodeConnectorKey(unsupportedPort)).
+ augmentation(FlowCapableNodeConnector.class).
+ build();
+ IdentifiedData<FlowCapableNodeConnector> data =
+ new IdentifiedData<>(path, fcnc);
+ ncListener.onCreated(task, data);
+ assertEquals(false, task.hasUpdates());
+
+ // In case of OpenFlow logical port.
+ NodeId nid = new NodeId("openflow:1");
+ NodeConnectorId logical = new NodeConnectorId("openflow:1:CONTROLLER");
+ path = InstanceIdentifier.builder(Nodes.class).
+ child(Node.class, new NodeKey(nid)).
+ child(NodeConnector.class, new NodeConnectorKey(logical)).
+ augmentation(FlowCapableNodeConnector.class).
+ build();
+ data = new IdentifiedData<>(path, fcnc);
+ ncListener.onCreated(task, data);
+ assertEquals(false, task.hasUpdates());
+
+ // In case of OpenFlow physical port.
+ SalPort[] ports = {
+ new SalPort(1L, 1L),
+ new SalPort(-1L, 0xffffff00L),
+ new SalPort(123L, 456L),
+ };
+ Map<SalPort, InstanceIdentifier<FlowCapableNodeConnector>> updated =
+ new HashMap<>();
+ for (SalPort sport: ports) {
+ path = InstanceIdentifier.builder(Nodes.class).
+ child(Node.class, sport.getNodeKey()).
+ child(NodeConnector.class, sport.getNodeConnectorKey()).
+ augmentation(FlowCapableNodeConnector.class).
+ build();
+ assertEquals(null, updated.put(sport, path));
+ data = new IdentifiedData<>(path, fcnc);
+ ncListener.onCreated(task, data);
+ assertEquals(true, task.hasUpdates());
}
+
+ verifyZeroInteractions(txQueue);
+ assertEquals(ports.length, updated.size());
+ assertEquals(updated, task.getUpdatedMap());
}
/**
- * Test method for
- * {@link NodeConnectorListener#onCreate}.
+ * Test case for
+ * {@link NodeConnectorListener#onUpdated(PortUpdateTask,ChangedData)}.
*/
@Test
- public void testOnCretaed() {
-
- try {
- portUpdateTask.addUpdated(data, salPort);
- IdentifiedData identifiedData = new IdentifiedData(data, fCapableNC);
- nodeConnectorListener.onCreated(portUpdateTask, identifiedData);
- } catch (Exception e) {
- assertTrue(e instanceof ClassCastException);
+ public void testOnUpdated() {
+ reset(txQueue);
+ FlowCapableNodeConnector fcnc1 = mock(FlowCapableNodeConnector.class);
+ FlowCapableNodeConnector fcnc2 = mock(FlowCapableNodeConnector.class);
+
+ Logger logger = mock(Logger.class);
+ PortUpdateTask task = new PortUpdateTask(logger);
+ assertEquals(false, task.hasUpdates());
+
+ // In case of unsupported port.
+ NodeId unsupportedNode = new NodeId("unknown:1");
+ NodeConnectorId unsupportedPort = new NodeConnectorId("unknown:1:2");
+ InstanceIdentifier<FlowCapableNodeConnector> path = InstanceIdentifier.
+ builder(Nodes.class).
+ child(Node.class, new NodeKey(unsupportedNode)).
+ child(NodeConnector.class, new NodeConnectorKey(unsupportedPort)).
+ augmentation(FlowCapableNodeConnector.class).
+ build();
+ ChangedData<FlowCapableNodeConnector> data =
+ new ChangedData<>(path, fcnc1, fcnc2);
+ ncListener.onUpdated(task, data);
+ assertEquals(false, task.hasUpdates());
+
+ // In case of OpenFlow logical port.
+ NodeId nid = new NodeId("openflow:1");
+ NodeConnectorId logical = new NodeConnectorId("openflow:1:CONTROLLER");
+ path = InstanceIdentifier.builder(Nodes.class).
+ child(Node.class, new NodeKey(nid)).
+ child(NodeConnector.class, new NodeConnectorKey(logical)).
+ augmentation(FlowCapableNodeConnector.class).
+ build();
+ data = new ChangedData<>(path, fcnc1, fcnc2);
+ ncListener.onUpdated(task, data);
+ assertEquals(false, task.hasUpdates());
+
+ // In case of OpenFlow physical port.
+ SalPort[] ports = {
+ new SalPort(1L, 1L),
+ new SalPort(-1L, 0xffffff00L),
+ new SalPort(123L, 456L),
+ };
+ Map<SalPort, InstanceIdentifier<FlowCapableNodeConnector>> updated =
+ new HashMap<>();
+ for (SalPort sport: ports) {
+ path = InstanceIdentifier.builder(Nodes.class).
+ child(Node.class, sport.getNodeKey()).
+ child(NodeConnector.class, sport.getNodeConnectorKey()).
+ augmentation(FlowCapableNodeConnector.class).
+ build();
+ assertEquals(null, updated.put(sport, path));
+ data = new ChangedData<>(path, fcnc1, fcnc2);
+ ncListener.onUpdated(task, data);
+ assertEquals(true, task.hasUpdates());
}
+
+ verifyZeroInteractions(txQueue);
+ assertEquals(ports.length, updated.size());
+ assertEquals(updated, task.getUpdatedMap());
}
/**
- * Test method for
- * {@link NodeConnectorListener#exitEvent}.
+ * Test case for
+ * {@link NodeConnectorListener#onRemoved(PortUpdateTask,IdentifiedData)}.
*/
@Test
- public void testExitEvent() {
+ public void testOnRemoved() {
+ reset(txQueue);
+ FlowCapableNodeConnector fcnc = mock(FlowCapableNodeConnector.class);
- try {
- nodeConnectorListener.exitEvent(portUpdateTask);
- } catch (Exception e) {
- assertTrue(e instanceof Exception);
- }
+ Logger logger = mock(Logger.class);
+ PortUpdateTask task = new PortUpdateTask(logger);
+ assertEquals(false, task.hasUpdates());
- try {
- portUpdateTask.addUpdated(data, salPort);
- nodeConnectorListener.exitEvent(portUpdateTask);
- } catch (Exception e) {
- assertTrue(e instanceof Exception);
+ // In case of unsupported port.
+ NodeId unsupportedNode = new NodeId("unknown:1");
+ NodeConnectorId unsupportedPort = new NodeConnectorId("unknown:1:2");
+ InstanceIdentifier<FlowCapableNodeConnector> path = InstanceIdentifier.
+ builder(Nodes.class).
+ child(Node.class, new NodeKey(unsupportedNode)).
+ child(NodeConnector.class, new NodeConnectorKey(unsupportedPort)).
+ augmentation(FlowCapableNodeConnector.class).
+ build();
+ IdentifiedData<FlowCapableNodeConnector> data =
+ new IdentifiedData<>(path, fcnc);
+ ncListener.onRemoved(task, data);
+ assertEquals(false, task.hasUpdates());
+
+ // In case of OpenFlow logical port.
+ NodeId nid = new NodeId("openflow:1");
+ NodeConnectorId logical = new NodeConnectorId("openflow:1:CONTROLLER");
+ path = InstanceIdentifier.builder(Nodes.class).
+ child(Node.class, new NodeKey(nid)).
+ child(NodeConnector.class, new NodeConnectorKey(logical)).
+ augmentation(FlowCapableNodeConnector.class).
+ build();
+ data = new IdentifiedData<>(path, fcnc);
+ ncListener.onRemoved(task, data);
+ assertEquals(false, task.hasUpdates());
+
+ // In case of OpenFlow physical port.
+ SalPort[] ports = {
+ new SalPort(1L, 1L),
+ new SalPort(-1L, 0xffffff00L),
+ new SalPort(123L, 456L),
+ };
+ Map<SalPort, InstanceIdentifier<FlowCapableNodeConnector>> updated =
+ new HashMap<>();
+ for (SalPort sport: ports) {
+ path = InstanceIdentifier.builder(Nodes.class).
+ child(Node.class, sport.getNodeKey()).
+ child(NodeConnector.class, sport.getNodeConnectorKey()).
+ augmentation(FlowCapableNodeConnector.class).
+ build();
+ assertEquals(null, updated.put(sport, path));
+ data = new IdentifiedData<>(path, fcnc);
+ ncListener.onRemoved(task, data);
+ assertEquals(true, task.hasUpdates());
}
+ verifyZeroInteractions(txQueue);
+ assertEquals(ports.length, updated.size());
+ assertEquals(updated, task.getUpdatedMap());
}
/**
- * Test method for
- * {@link NodeConnectorListener#getWildcardPath}.
+ * Test case for {@link NodeConnectorListener#getWildcardPath()}.
*/
@Test
public void testGetWildcardPath() {
+ assertEquals(getPath(), ncListener.getWildcardPath());
+ }
+
+ /**
+ * Test case for {@link NodeConnectorListener#getLogger()}.
+ */
+ @Test
+ public void testGetLogger() {
+ Logger logger = ncListener.getLogger();
+ assertEquals(NodeConnectorListener.class.getName(), logger.getName());
+ }
- nodeConnectorListener.getWildcardPath();
+ /**
+ * Test case for {@link NodeConnectorListener#close()}.
+ */
+ @Test
+ public void testClose() {
+ verifyZeroInteractions(registration);
+ ncListener.close();
+ verify(registration).close();
+ }
+ /**
+ * Return a wildcard path to the MD-SAL data model to listen.
+ */
+ private InstanceIdentifier<FlowCapableNodeConnector> getPath() {
+ return InstanceIdentifier.builder(Nodes.class).
+ child(Node.class).
+ child(NodeConnector.class).
+ augmentation(FlowCapableNodeConnector.class).
+ build();
}
}
-/**
+/*
* Copyright (c) 2015 NEC Corporation
* All rights reserved.
*
package org.opendaylight.vtn.manager.internal.inventory;
-import org.junit.Test;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.opendaylight.vtn.manager.internal.TxQueue;
+import org.opendaylight.vtn.manager.internal.TxTask;
+import org.opendaylight.vtn.manager.internal.util.ChangedData;
+import org.opendaylight.vtn.manager.internal.util.IdentifiedData;
+import org.opendaylight.vtn.manager.internal.util.inventory.SalNode;
+
import org.opendaylight.vtn.manager.internal.TestBase;
-import org.mockito.Mockito;
-import org.opendaylight.vtn.manager.internal.VTNManagerProvider;
+
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.vtn.manager.internal.util.IdentifiedData;
-import org.opendaylight.vtn.manager.internal.TxQueue;
-import org.opendaylight.vtn.manager.internal.util.tx.TxQueueImpl;
-import org.opendaylight.vtn.manager.internal.util.inventory.SalPort;
-import org.opendaylight.vtn.manager.internal.util.ChangedData;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.types.rev150209.VtnUpdateType;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+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.flow.inventory.rev130819.FlowCapableNode;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
/**
- * JUnit test for {@link NodeListener}
- *
- * NodeListener test class is to test Listener class listens
- * the change of MD-SAL nodes or not.
+ * JUnit test for {@link NodeListener}.
*/
-
-public class NodeListenerTest extends TestBase{
-
- /**
- * Logger instance.
- */
- private static final Logger LOG = LoggerFactory.getLogger(NodeListenerTest.class);
+public class NodeListenerTest extends TestBase {
/**
- * create a mock object for DataBroker class
+ * Mock-up of {@link TxQueue}.
*/
- DataBroker broker = Mockito.mock(DataBroker.class);
- /**
- * create a mock object for VTNManagerProvider class
- */
- VTNManagerProvider provider = Mockito.mock(VTNManagerProvider.class);
+ private TxQueue txQueue;
+
/**
- * create a mock object for FlowCapableNode class
+ * Mock-up of {@link DataBroker}.
*/
- FlowCapableNode fCapableNode = Mockito.mock(FlowCapableNode.class);
+ private DataBroker dataBroker;
+
/**
- * create a mock object for AsyncDataChangeEvent class
- */
- AsyncDataChangeEvent asyncDataChangeEvent = Mockito.mock(AsyncDataChangeEvent.class);
- /**
- * create a object for TxQueue
+ * Mock-up of {@link ListenerRegistration}.
*/
- TxQueue txQueue = new TxQueueImpl("QUEUE1", provider);
+ private ListenerRegistration registration;
+
/**
- * create a object for NodeListener
+ * A {@link NodeListener} instance for test.
*/
- NodeListener nodeListener = new NodeListener(txQueue, broker);
+ private NodeListener nodeListener;
+
/**
- * create a object for NodeUpdateTask
+ * Set up test environment.
*/
- NodeUpdateTask nodeUpdateTask = new NodeUpdateTask(LOG);
+ @Before
+ public void setUp() {
+ dataBroker = mock(DataBroker.class);
+ txQueue = mock(TxQueue.class);
+ registration = mock(ListenerRegistration.class);
+
+ when(dataBroker.registerDataChangeListener(
+ any(LogicalDatastoreType.class), any(InstanceIdentifier.class),
+ any(NodeListener.class), any(DataChangeScope.class))).
+ thenReturn(registration);
+ nodeListener = new NodeListener(txQueue, dataBroker);
+ }
+
/**
- * create a object for InstanceIdentifier
+ * Test case for {@link NodeListener#NodeListener(TxQueue, DataBroker)}.
*/
- InstanceIdentifier<FlowCapableNode> instanceIdentifier = InstanceIdentifier.builder(Nodes.class).child(Node.class).augmentation(FlowCapableNode.class).build();
+ @Test
+ public void testConstructor() {
+ LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
+ DataChangeScope scope = DataChangeScope.SUBTREE;
+ InstanceIdentifier<FlowCapableNode> path = getPath();
+ verify(dataBroker).
+ registerDataChangeListener(oper, path, nodeListener, scope);
+ verifyZeroInteractions(registration);
+ verify(txQueue).postFirst(any(TxTask.class));
+ verify(txQueue, never()).post(any(TxTask.class));
+ assertEquals(FlowCapableNode.class, nodeListener.getTargetType());
+ }
- Long nodeId = 100L;
- Long portId = 200L;
/**
- * create a object for SalPort
+ * Test case for {@link NodeListener#enterEvent(AsyncDataChangeEvent)}.
*/
- SalPort salPort = new SalPort(nodeId, portId, "port address");
+ @Test
+ public void testEnterEvent() {
+ AsyncDataChangeEvent ev = null;
+ NodeUpdateTask task = nodeListener.enterEvent(ev);
+ assertTrue(task instanceof NodeUpdateTask);
+ NodeUpdateTask task1 = nodeListener.enterEvent(ev);
+ assertTrue(task1 instanceof NodeUpdateTask);
+ assertNotSame(task, task1);
+ }
/**
- * Test method for
- * {@link NodeListener#getLogger}.
+ * Test case for {@link NodeListener#exitEvent(NodeUpdateTask)}.
*/
@Test
- public void testGetLogger() {
+ public void testExitEvent() {
+ reset(txQueue);
+ Logger logger = mock(Logger.class);
+ NodeUpdateTask task = new NodeUpdateTask(logger);
+ nodeListener.exitEvent(task);
+ verifyZeroInteractions(txQueue);
- assertTrue(nodeListener.getLogger() instanceof Logger);
+ SalNode snode = new SalNode(123L);
+ InstanceIdentifier<FlowCapableNode> path = InstanceIdentifier.
+ builder(Nodes.class).
+ child(Node.class, snode.getNodeKey()).
+ augmentation(FlowCapableNode.class).
+ build();
+ task.addUpdated(path, snode);
+ nodeListener.exitEvent(task);
+ verify(txQueue, never()).postFirst(any(TxTask.class));
+ verify(txQueue).post(task);
+ verifyZeroInteractions(logger);
}
/**
- * Test method for
- * {@link NodeListener#onRemoved}.
+ * Test case for {@link NodeListener#onCreated(NodeUpdateTask,IdentifiedData)}.
*/
@Test
- public void testOnRemoved() {
- try {
- nodeUpdateTask.addUpdated(instanceIdentifier, salPort);
- IdentifiedData identifiedData = new IdentifiedData(instanceIdentifier, fCapableNode);
- nodeListener.onRemoved(nodeUpdateTask, identifiedData);
- } catch (Exception e) {
- assertTrue(e instanceof ClassCastException);
+ public void testOnCreated() {
+ reset(txQueue);
+ FlowCapableNode fcn = mock(FlowCapableNode.class);
+
+ Logger logger = mock(Logger.class);
+ NodeUpdateTask task = new NodeUpdateTask(logger);
+ assertEquals(false, task.hasUpdates());
+
+ // In case of unsupported node.
+ NodeId unsupported = new NodeId("unknown:1");
+ InstanceIdentifier<FlowCapableNode> path = InstanceIdentifier.
+ builder(Nodes.class).
+ child(Node.class, new NodeKey(unsupported)).
+ augmentation(FlowCapableNode.class).
+ build();
+ IdentifiedData<FlowCapableNode> data = new IdentifiedData<>(path, fcn);
+ nodeListener.onCreated(task, data);
+ assertEquals(false, task.hasUpdates());
+
+ // In case of OpenFlow node.
+ SalNode[] nodes = {
+ new SalNode(1L),
+ new SalNode(-1L),
+ new SalNode(12345L),
+ };
+ Map<SalNode, InstanceIdentifier<FlowCapableNode>> updated =
+ new HashMap<>();
+ for (SalNode snode: nodes) {
+ path = InstanceIdentifier.builder(Nodes.class).
+ child(Node.class, snode.getNodeKey()).
+ augmentation(FlowCapableNode.class).
+ build();
+ assertEquals(null, updated.put(snode, path));
+ data = new IdentifiedData<>(path, fcn);
+ nodeListener.onCreated(task, data);
+ assertEquals(true, task.hasUpdates());
}
+
+ verifyZeroInteractions(txQueue);
+ assertEquals(nodes.length, updated.size());
+ assertEquals(updated, task.getUpdatedMap());
}
- /**
- * Test method for
- * {@link NodeListener#onUpdated}.
+ /**
+ * Test case for {@link NodeListener#onUpdated(NodeUpdateTask,ChangedData)}.
*/
@Test
public void testOnUpdated() {
+ reset(txQueue);
+ FlowCapableNode fcn1 = mock(FlowCapableNode.class);
+ FlowCapableNode fcn2 = mock(FlowCapableNode.class);
+ SalNode snode = new SalNode(12345L);
+ InstanceIdentifier<FlowCapableNode> path = InstanceIdentifier.
+ builder(Nodes.class).
+ child(Node.class, snode.getNodeKey()).
+ augmentation(FlowCapableNode.class).
+ build();
+ ChangedData<FlowCapableNode> data =
+ new ChangedData<>(path, fcn1, fcn2);
+
+ Logger logger = mock(Logger.class);
+ NodeUpdateTask task = new NodeUpdateTask(logger);
+ assertEquals(false, task.hasUpdates());
+
try {
- nodeUpdateTask.addUpdated(instanceIdentifier, salPort);
- ChangedData changedData = new ChangedData(instanceIdentifier, fCapableNode, fCapableNode);
- nodeListener.onUpdated(nodeUpdateTask, changedData);
- } catch (Exception e) {
- assertTrue(e instanceof Exception);
+ nodeListener.onUpdated(task, data);
+ unexpected();
+ } catch (IllegalStateException e) {
}
+
+ assertEquals(false, task.hasUpdates());
+ verifyZeroInteractions(txQueue);
}
/**
- * Test method for
- * {@link NodeListener#onCreated}.
+ * Test case for {@link NodeListener#onRemoved(NodeUpdateTask,IdentifiedData)}.
*/
@Test
- public void testOnCreated() {
- try {
- nodeUpdateTask.addUpdated(instanceIdentifier, salPort);
- IdentifiedData identifiedData = new IdentifiedData(instanceIdentifier, fCapableNode);
- nodeListener.onCreated(nodeUpdateTask, identifiedData);
- } catch (Exception e) {
- assertTrue(e instanceof ClassCastException);
+ public void testOnRemoved() {
+ reset(txQueue);
+ FlowCapableNode fcn = mock(FlowCapableNode.class);
+
+ Logger logger = mock(Logger.class);
+ NodeUpdateTask task = new NodeUpdateTask(logger);
+ assertEquals(false, task.hasUpdates());
+
+ // In case of unsupported node.
+ NodeId unsupported = new NodeId("unknown:1");
+ InstanceIdentifier<FlowCapableNode> path = InstanceIdentifier.
+ builder(Nodes.class).
+ child(Node.class, new NodeKey(unsupported)).
+ augmentation(FlowCapableNode.class).
+ build();
+ IdentifiedData<FlowCapableNode> data = new IdentifiedData<>(path, fcn);
+ nodeListener.onRemoved(task, data);
+ assertEquals(false, task.hasUpdates());
+
+ // In case of OpenFlow node.
+ SalNode[] nodes = {
+ new SalNode(1L),
+ new SalNode(-1L),
+ new SalNode(12345L),
+ };
+ Map<SalNode, InstanceIdentifier<FlowCapableNode>> updated =
+ new HashMap<>();
+ for (SalNode snode: nodes) {
+ path = InstanceIdentifier.builder(Nodes.class).
+ child(Node.class, snode.getNodeKey()).
+ augmentation(FlowCapableNode.class).
+ build();
+ assertEquals(null, updated.put(snode, path));
+ data = new IdentifiedData<>(path, fcn);
+ nodeListener.onRemoved(task, data);
+ assertEquals(true, task.hasUpdates());
}
+
+ verifyZeroInteractions(txQueue);
+ assertEquals(nodes.length, updated.size());
+ assertEquals(updated, task.getUpdatedMap());
}
/**
- * Test method for
- * {@link NodeListener#enterEvent}.
+ * Test case for {@link NodeListener#getWildcardPath()}.
*/
@Test
- public void testEnterEvent() {
- try {
- assertTrue(nodeListener.enterEvent(asyncDataChangeEvent) instanceof NodeUpdateTask);
- } catch (Exception e) {
- assertTrue(e instanceof Exception);
- }
+ public void testGetWildcardPath() {
+ assertEquals(getPath(), nodeListener.getWildcardPath());
}
- /**
- * Test method for
- * {@link NodeListener#exitEvent}.
+ /**
+ * Test case for {@link NodeListener#getRequiredEvents()}.
*/
@Test
- public void testExitEvent() {
-
- try {
- nodeListener.exitEvent(nodeUpdateTask);
- } catch (Exception e) {
- assertTrue(e instanceof Exception);
- }
-
- try {
- nodeUpdateTask.addUpdated(instanceIdentifier, salPort);
- nodeListener.exitEvent(nodeUpdateTask);
- } catch (Exception e) {
- assertTrue(e instanceof Exception);
- }
-
+ public void testGetRequiredEvents() {
+ Set<VtnUpdateType> events = nodeListener.getRequiredEvents();
+ assertEquals(2, events.size());
+ assertEquals(true, events.contains(VtnUpdateType.CREATED));
+ assertEquals(true, events.contains(VtnUpdateType.REMOVED));
}
/**
- * Test method for
- * {@link NodeListener#getRequiredEvents}.
+ * Test case for {@link NodeListener#getLogger()}.
*/
@Test
- public void testGetRequiredEvents() {
+ public void testGetLogger() {
+ Logger logger = nodeListener.getLogger();
+ assertEquals(NodeListener.class.getName(), logger.getName());
+ }
- nodeListener.getRequiredEvents();
+ /**
+ * Test case for {@link NodeListener#close()}.
+ */
+ @Test
+ public void testClose() {
+ verifyZeroInteractions(registration);
+ nodeListener.close();
+ verify(registration).close();
+ }
+ /**
+ * Return a wildcard path to the MD-SAL data model to listen.
+ */
+ private InstanceIdentifier<FlowCapableNode> getPath() {
+ return InstanceIdentifier.builder(Nodes.class).
+ child(Node.class).
+ augmentation(FlowCapableNode.class).
+ build();
}
}
*/
package org.opendaylight.vtn.manager.internal.inventory;
-import org.junit.Test;
import org.slf4j.Logger;
-import org.junit.Assert;
-import org.mockito.Mockito;
+
import org.junit.AfterClass;
+import org.junit.Assert;
import org.junit.BeforeClass;
-import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.junit.Test;
+
+import org.mockito.Mockito;
+
import org.opendaylight.vtn.manager.internal.VTNManagerImpl;
import org.opendaylight.vtn.manager.internal.VTNManagerProvider;
+import org.opendaylight.vtn.manager.internal.util.ChangedData;
+import org.opendaylight.vtn.manager.internal.util.IdentifiedData;
import org.opendaylight.vtn.manager.internal.util.IdentifierTargetComparator;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.types.rev150209.VtnUpdateType;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
-import org.opendaylight.vtn.manager.internal.util.IdentifiedData;
-import org.opendaylight.vtn.manager.internal.util.ChangedData;
+
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.vtn.nodes.VtnNode;
+
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.VtnNodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.inventory.rev150209.vtn.nodes.VtnNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.types.rev150209.VtnUpdateType;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
/**
- * JUnit test for {@link VTNInventoryManager}
+ * JUnit test for {@link VTNInventoryManager}.
*/
public class VTNInventoryManagerTest {
-
/**
- * Static instance of VTNInventoryManager to perform unit testing.
- */
+ * Static instance of VTNInventoryManager to perform unit testing.
+ */
private static VTNInventoryManager vtnInventoryManager;
+
/**
- * Static instance of VTNManagerProvider to perform unit testing.
- */
+ * Static instance of VTNManagerProvider to perform unit testing.
+ */
private static VTNManagerProvider vtnManagerProvider;
+
/**
- * Static instance of VTNInventoryListener to perform unit testing.
- */
+ * Static instance of VTNInventoryListener to perform unit testing.
+ */
private static VTNInventoryListener vtnInventoryListener;
/**
/**
* Test method for
- * {@link VTNInventoryManager#addListener()}.
+ * {@link VTNInventoryManager#addListener(VTNInventoryListener)}.
*/
@Test
public void testAddListener() {
/**
* Test method for
- * {@link VTNInventoryManager#getComparatpr()}.
+ * {@link VTNInventoryManager#getComparator()}.
*/
@Test
public void testGetComparator() {
/**
* Test method for
- * {@link VTNInventoryManager#getOrder()}.
+ * {@link VTNInventoryManager#getOrder(VtnUpdateType)}.
*/
@Test
public void testGetOrder() {
- Assert.assertFalse(vtnInventoryManager.getOrder(VtnUpdateType.CREATED));
- Assert.assertTrue(vtnInventoryManager.getOrder(VtnUpdateType.REMOVED));
+ Assert.assertTrue(vtnInventoryManager.getOrder(VtnUpdateType.CREATED));
+ Assert.assertTrue(vtnInventoryManager.getOrder(VtnUpdateType.CHANGED));
+ Assert.assertFalse(vtnInventoryManager.getOrder(VtnUpdateType.REMOVED));
}
/**
* Test method for
- * {@link VTNInventoryManager#enterEvent()}.
+ * {@link VTNInventoryManager#enterEvent(AsyncDataChangeEvent)}.
*/
@Test
public void testEnterEvent() {
/**
* Test method for
- * {@link VTNInventoryManager#exitEvent()}.
+ * {@link VTNInventoryManager#exitEvent(Void)}.
*/
@Test
public void testExitEvent() {
/**
* Test method for
- * {@link VTNInventoryManager#onCreated()}.
+ * {@link VTNInventoryManager#onCreated(Void,IdentifiedData)}.
*/
@Test
public void testOnCreated() {
/**
* Test method for
- * {@link VTNInventoryManager#onUpdated()}.
+ * {@link VTNInventoryManager#onUpdated(Void,ChangedData)}.
*/
@Test
public void testOnUpdated() {
/**
* Test method for
- * {@link VTNInventoryManager#onRemoved()}.
+ * {@link VTNInventoryManager#onRemoved(Void,IdentifiedData)}.
*/
@Test
public void testOnRemoved() {
-/**
+/*
* Copyright (c) 2015 NEC Corporation
* All rights reserved.
*
* 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.vtn.manager.internal.routing;
import org.opendaylight.vtn.manager.internal.TestBase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.pathpolicy.rev150209.SetPathCostOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.types.rev150209.VtnPortDesc;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.pathpolicy.rev150209.SetPathCostInputBuilder;
+
/**
* SetPathCostTask test class is to test the MD-SAL transaction task that set
* all the specified link cost configurations into the path policy
/**
* Test method for
- * {@link SetPathCostTask#OnStarted()}.
+ * {@link SetPathCostTask#onStarted(TxContext)}.
*/
@Test
public void testOnStarted() {
/**
* Test method for
- * {@link SetPathCostTask#OnSuccess()}.
+ * {@link SetPathCostTask#onSuccess(VTNManagerProvider,List)}.
*/
@Test
public void testOnSuccess() {
* 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.vtn.manager.internal.routing;
import org.junit.Test;
import org.opendaylight.vtn.manager.internal.util.IdentifiedData;
/**
- * JUnit test for {@link VTNRoutingManager}
+ * JUnit test for {@link VTNRoutingManager}.
*/
public class VTNRoutingManagerTest {
/**
/**
* Test method for
- * {@link VTNRoutingManager#addListener()}.
+ * {@link VTNRoutingManager#addListener(VTNRoutingListener)}.
*/
@Test
public void testAddListener() {
/**
* Test method for
- * {@link VTNRoutingManager#getRouteResolver()}.
+ * {@link VTNRoutingManager#getRouteResolver(Integer)}.
*/
@Test
public void testGetRouteResolver() {
/**
* Test method for
- * {@link VTNRoutingManager#enterEvent()}.
+ * {@link VTNRoutingManager#enterEvent(AsyncDataChangeEvent)}.
*/
@Test
public void testEnterEvent() {
/**
* Test method for
- * {@link VTNRoutingManager#exitEvent()}.
+ * {@link VTNRoutingManager#exitEvent(TopologyEventContext)}.
*/
@Test
public void testExitEvent() {
/**
* Test method for
- * {@link VTNRoutingManager#onCreated()}.
+ * {@link VTNRoutingManager#onCreated(TopologyEventContext,IdentifiedData)}.
*/
@Test
public void testOnCreated() {
/**
* Test method for
- * {@link VTNRoutingManager#onUpdated()}.
+ * {@link VTNRoutingManager#onUpdated(TopologyEventContext,ChangedData)}.
*/
@Test
public void testOnUpdated() {
/**
* Test method for
- * {@link VTNRoutingManager#onRemoved()}.
+ * {@link VTNRoutingManager#onRemoved(TopologyEventContext,IdentifiedData)}.
*/
@Test
public void testOnRemoved() {
/**
* Test method for
- * {@link VTNRoutingManager#initConfig()}.
+ * {@link VTNRoutingManager#initConfig(boolean)}.
*/
@Test
public void testInitConfig() {
/**
* Test method for
- * {@link VTNRoutingManager#initRpcService()}.
+ * {@link VTNRoutingManager#initRpcServices(RpcProviderRegistry,CompositeAutoCloseable)}.
*/
@Test
public void testInitRpcServices() {
/**
* Test method for
- * {@link VTNRoutingManager#setPathPolicy()}.
+ * {@link VTNRoutingManager#setPathPolicy(SetPathPolicyInput)}.
*/
@Test
public void testSetPathPolicy() {
/**
* Test method for
- * {@link VTNRoutingManager#removePathPolicy()}.
+ * {@link VTNRoutingManager#removePathPolicy(RemovePathPolicyInput)}.
*/
@Test
public void testRemovePathPolicy() {
/**
* Test method for
- * {@link VTNRoutingManager#setPathCost()}.
+ * {@link VTNRoutingManager#setPathCost(SetPathCostInput)}.
*/
@Test
public void testSetPathCost() {
/**
* Test method for
- * {@link VTNRoutingManager#removePathCost()}.
+ * {@link VTNRoutingManager#removePathCost(RemovePathCostInput)}.
*/
@Test
public void testRemovePathCost() {
/**
* Test method for
- * {@link VTNRoutingManager#clearPathCost()}.
+ * {@link VTNRoutingManager#clearPathPolicy()}.
*/
@Test
public void testClearPathPolicy() {
/**
* Test method for
- * {@link FutureCanceller#Set()}.
+ * {@link FutureCanceller#set(Timer,long,ListenableFuture)} and
+ * {@link FutureCanceller#set(Timer,long,ListenableFuture,boolean)}.
*/
@Test
public void testSet() {
-
listenableFuture = SettableFuture.create();
FutureCanceller.set(timer, 10L, listenableFuture);
FutureCanceller.set(timer, 0, listenableFuture);
-/**
+/*
* Copyright (c) 2015 NEC Corporation
* All rights reserved.
*
/**
* Test method for
- * {@link VTNFutureTask#AddListener()}.
- *
+ * {@link VTNFutureTask#addListener(Runnable,Executor)}.
*/
@Test
public void testAddListener() {
-
- /**
- * creating runnable object used for test.
- *
- */
+ // creating runnable object used for test.
RunnableThread listener = new RunnableThread(1);
- /**
- * creating executor object used for test.
- *
- */
+
+ // creating executor object used for test.
Executor executor = new SequentialExecutor();
int i = 2;
VTNFutureTask vtnFutureTask = new VTNFutureTask(listener, i);
vtnFutureTask.addListener(listener, executor);
-
}
/**
/**
* Test method for
- * {@link VTNFutureTask#CheckedGet(long,timeunit)}.
+ * {@link VTNFutureTask#checkedGet(long,TimeUnit)}.
*/
@Test
public void testcheckedGetinTime() {
* JUnit test for {@link VTNThreadPool}
*/
public class VTNThreadPoolTest extends TestBase {
-
/**
* task Thread class used for test.
* this task wait until a latch is counted down to zero.
}
/**
- * Test method for
- * {@link VTNThreadPool#VTNThreadPool(String, int, long)}.
+ * Test method for {@link VTNThreadPool#VTNThreadPool(String)}.
*
* <p>
- * This also tests
- * {@link VTNThreadPool#execute(Runnable)},
- * {@link VTNThreadPool#executeTask(Runnable)},
- * {@link VTNThreadPool#shutdown()},
- * {@link VTNThreadPool#terminate()}.
+ * This also tests the following methods.
* </p>
+ * <ul>
+ * <li>{@link VTNThreadPool#execute(Runnable)}</li>
+ * <li>{@link VTNThreadPool#executeTask(Runnable)}</li>
+ * <li>{@link VTNThreadPool#shutdown()}</li>
+ * <li>{@link VTNThreadPool#terminate()}</li>
+ * </ul>
*/
@Test
- public void testVTNThreadPool() {
-
+ public void testSingleThreadPool() {
// 1 task on 1 worker.
CountDownLatch latch = new CountDownLatch(1);
- VTNThreadPool pool = new VTNThreadPool("test", 1, 1000L);
+ VTNThreadPool pool = new VTNThreadPool("test");
ThreadTask task1 = new ThreadTask(10000L, latch);
pool.execute(task1);
assertTrue(task1.await(10L, TimeUnit.SECONDS));
assertFalse(task2.await(100L, TimeUnit.MILLISECONDS));
pool.terminate();
+ }
+ /**
+ * Test method for
+ * {@link VTNThreadPool#VTNThreadPool(String, int, long)}.
+ *
+ * <p>
+ * This also tests the following methods.
+ * </p>
+ * <ul>
+ * <li>{@link VTNThreadPool#execute(Runnable)}</li>
+ * <li>{@link VTNThreadPool#executeTask(Runnable)}</li>
+ * <li>{@link VTNThreadPool#shutdown()}</li>
+ * <li>{@link VTNThreadPool#terminate()}</li>
+ * </ul>
+ */
+ @Test
+ public void testVTNThreadPool() {
// 2 tasks on 2 workers.
- pool = new VTNThreadPool("test2", 2, 1000L);
- latch = new CountDownLatch(2);
- task1 = new ThreadTask(1000L, latch);
- task2 = new ThreadTask(1000L, latch);
+ VTNThreadPool pool = new VTNThreadPool("test2", 2, 1000L);
+ CountDownLatch latch = new CountDownLatch(2);
+ ThreadTask task1 = new ThreadTask(1000L, latch);
+ ThreadTask task2 = new ThreadTask(1000L, latch);
assertTrue(pool.executeTask(task1));
assertFalse(task1.await(500L, TimeUnit.MILLISECONDS));
// in case one thread throw Exception.
pool = new VTNThreadPool("test_exception", 1, 10000L);
- latch1 = new CountDownLatch(1);
- latch2 = new CountDownLatch(1);
+ CountDownLatch latch1 = new CountDownLatch(1);
+ CountDownLatch latch2 = new CountDownLatch(1);
ExpThreadTask etask = new ExpThreadTask(1000L, latch1);
task1 = new ThreadTask(10000L, latch2);
assertFalse(pool.join(1L));
-
// in case join is interrupted.
pool = new VTNThreadPool("test3", 2, 10000L);
--- /dev/null
+/*
+ * Copyright (c) 2015 NEC Corporation
+ * 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.vtn.manager.internal.util.flow;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import static org.opendaylight.vtn.manager.internal.util.flow.FlowUtilsTest.VTN_FLOW_COOKIE;
+import static org.opendaylight.vtn.manager.internal.util.flow.FlowUtilsTest.createDuration;
+import static org.opendaylight.vtn.manager.internal.util.flow.FlowUtilsTest.createVtnFlowEntry;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.NavigableMap;
+import java.util.Random;
+import java.util.Set;
+
+import org.junit.Test;
+
+import org.opendaylight.vtn.manager.util.NumberUtils;
+
+import org.opendaylight.vtn.manager.internal.util.inventory.SalNode;
+import org.opendaylight.vtn.manager.internal.util.inventory.SalPort;
+
+import org.opendaylight.vtn.manager.internal.TestBase;
+
+import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.flow.rev150410.VtnFlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.VtnFlows;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.tenant.flow.info.VtnDataFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.tenant.flow.info.VtnDataFlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.FlowStatsHistory;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.FlowStatsHistoryBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.VtnFlowEntry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.flow.stats.history.FlowStatsRecord;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.data.flow.fields.flow.stats.history.FlowStatsRecordBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.flows.VtnFlowTable;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.flow.rev150313.vtn.flows.VtnFlowTableKey;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+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.statistics.rev130819.FlowStatisticsData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetFlowStatisticsFromFlowTableInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.statistics.FlowStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.statistics.FlowStatisticsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
+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.model.statistics.types.rev130925.GenericStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.duration.DurationBuilder;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.Counter32;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.Counter64;
+
+/**
+ * JUnit test for {@link FlowStatsUtils}.
+ */
+public class FlowStatsUtilsTest extends TestBase {
+ /**
+ * Test case for
+ * {@link FlowStatsUtils#getIdentifier(String,VtnDataFlowKey)}.
+ */
+ @Test
+ public void testGetIdentifier1() {
+ String[] tenants = {"vtn1", "vtn_2", "tenant_3"};
+ VtnFlowId[] flowIds = {
+ new VtnFlowId(BigInteger.valueOf(1L)),
+ new VtnFlowId(BigInteger.valueOf(1234567L)),
+ new VtnFlowId(BigInteger.valueOf(0xfffffffffL)),
+ };
+
+ for (String tname: tenants) {
+ VtnFlowTableKey key = new VtnFlowTableKey(tname);
+ for (VtnFlowId id: flowIds) {
+ VtnDataFlowKey vdfKey = new VtnDataFlowKey(id);
+ InstanceIdentifier<FlowStatsHistory> exp = InstanceIdentifier.
+ builder(VtnFlows.class).
+ child(VtnFlowTable.class, key).
+ child(VtnDataFlow.class, vdfKey).
+ child(FlowStatsHistory.class).
+ build();
+ assertEquals(exp, FlowStatsUtils.getIdentifier(tname, vdfKey));
+ }
+ }
+ }
+
+ /**
+ * Test case for
+ * {@link FlowStatsUtils#getIdentifier(NodeId,FlowId)}.
+ */
+ @Test
+ public void testGetIdentifier2() {
+ NodeId[] nodes = {
+ new NodeId("openflow:1"),
+ new NodeId("openflow:7777"),
+ new NodeId("openflow:18446744073709551615"),
+ };
+ FlowId[] flowIds = {
+ new FlowId("flow-1"),
+ new FlowId("flow_2"),
+ new FlowId("flow3"),
+ };
+
+ TableKey tableKey = new TableKey((short)0);
+ for (NodeId node: nodes) {
+ NodeKey nodeKey = new NodeKey(node);
+ for (FlowId id: flowIds) {
+ InstanceIdentifier<FlowStatistics> exp = InstanceIdentifier.
+ builder(Nodes.class).
+ child(Node.class, nodeKey).
+ augmentation(FlowCapableNode.class).
+ child(Table.class, tableKey).
+ child(Flow.class, new FlowKey(id)).
+ augmentation(FlowStatisticsData.class).
+ child(FlowStatistics.class).
+ build();
+ assertEquals(exp, FlowStatsUtils.getIdentifier(node, id));
+ }
+ }
+ }
+
+ /**
+ * Test case for
+ * {@link FlowStatsUtils#createGetFlowStatsInput(VtnFlowEntry)}.
+ *
+ * @throws Exception An error occurred.
+ */
+ @Test
+ public void testCreateGetFlowStatsInput1() throws Exception {
+ long flowId = 9999999L;
+ int order = 0;
+ SalPort ingress = new SalPort(12L, 345L);
+ Short table = 0;
+ VtnFlowEntry vfent = createVtnFlowEntry(flowId, order, ingress);
+ FlowCookie cookieMask = new FlowCookie(NumberUtils.getUnsigned(-1L));
+
+ GetFlowStatisticsFromFlowTableInput input =
+ FlowStatsUtils.createGetFlowStatsInput(vfent);
+ assertEquals(ingress.getNodeRef(), input.getNode());
+ assertEquals(vfent.getPriority(), input.getPriority());
+ assertEquals(table, input.getTableId());
+ assertEquals(vfent.getIdleTimeout(), input.getIdleTimeout());
+ assertEquals(vfent.getHardTimeout(), input.getHardTimeout());
+ assertEquals(vfent.getCookie(), input.getCookie());
+ assertEquals(cookieMask, input.getCookieMask());
+ assertEquals(vfent.getMatch(), input.getMatch());
+ assertEquals(vfent.getFlags(), input.getFlags());
+ assertEquals(vfent.getInstructions(), input.getInstructions());
+ assertEquals(null, input.isStrict());
+ assertEquals(null, input.isBarrier());
+ assertEquals(null, input.getOutPort());
+ assertEquals(null, input.getOutGroup());
+ assertEquals(null, input.getBufferId());
+ assertEquals(null, input.getContainerName());
+ assertEquals(null, input.getFlowName());
+ assertEquals(null, input.isInstallHw());
+ }
+
+ /**
+ * Test case for {@link FlowStatsUtils#createGetFlowStatsInput(SalNode)}.
+ */
+ @Test
+ public void testCreateGetFlowStatsInput2() throws Exception {
+ SalNode[] nodes = {
+ new SalNode(1L),
+ new SalNode(7777L),
+ new SalNode(-1L),
+ };
+
+ FlowCookie cookie =
+ new FlowCookie(NumberUtils.getUnsigned(VTN_FLOW_COOKIE));
+ FlowCookie cookieMask =
+ new FlowCookie(NumberUtils.getUnsigned(0xffff000000000000L));
+ Short table = 0;
+
+ for (SalNode snode: nodes) {
+ GetFlowStatisticsFromFlowTableInput input =
+ FlowStatsUtils.createGetFlowStatsInput(snode);
+ assertEquals(snode.getNodeRef(), input.getNode());
+ assertEquals(null, input.getPriority());
+ assertEquals(table, input.getTableId());
+ assertEquals(null, input.getIdleTimeout());
+ assertEquals(null, input.getHardTimeout());
+ assertEquals(cookie, input.getCookie());
+ assertEquals(cookieMask, input.getCookieMask());
+ assertEquals(null, input.getMatch());
+ assertEquals(null, input.getFlags());
+ assertEquals(null, input.getInstructions());
+ assertEquals(null, input.isStrict());
+ assertEquals(null, input.isBarrier());
+ assertEquals(null, input.getOutPort());
+ assertEquals(null, input.getOutGroup());
+ assertEquals(null, input.getBufferId());
+ assertEquals(null, input.getContainerName());
+ assertEquals(null, input.getFlowName());
+ assertEquals(null, input.isInstallHw());
+ }
+ }
+
+ /**
+ * Test case for {@link FlowStatsUtils#read(ReadTransaction,NodeId,FlowId)}.
+ *
+ * @throws Exception An error occurred.
+ */
+ @Test
+ public void testRead() throws Exception {
+ ReadTransaction rtx = mock(ReadTransaction.class);
+ LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
+ NodeId nodeId = new NodeId("openflow:12345");
+ FlowId flowId = new FlowId("md-flow:55");
+ InstanceIdentifier<FlowStatistics> path = InstanceIdentifier.
+ builder(Nodes.class).
+ child(Node.class, new NodeKey(nodeId)).
+ augmentation(FlowCapableNode.class).
+ child(Table.class, new TableKey((short)0)).
+ child(Flow.class, new FlowKey(flowId)).
+ augmentation(FlowStatisticsData.class).
+ child(FlowStatistics.class).
+ build();
+
+ // In case where the target MD-SAL flow statistics is not present.
+ FlowStatistics fstats = null;
+ when(rtx.read(oper, path)).thenReturn(getReadResult(fstats));
+ assertEquals(null, FlowStatsUtils.read(rtx, nodeId, flowId));
+ verify(rtx).read(oper, path);
+
+ // In case whether the target MD-SAL flow statistics is present.
+ reset(rtx);
+ fstats = new FlowStatisticsBuilder().build();
+ when(rtx.read(oper, path)).thenReturn(getReadResult(fstats));
+ assertEquals(fstats, FlowStatsUtils.read(rtx, nodeId, flowId));
+ verify(rtx).read(oper, path);
+ }
+
+ /**
+ * Test case for {@link FlowStatsUtils#check(GenericStatistics)}.
+ */
+ @Test
+ public void testCheck() {
+ GenericStatistics gstats = null;
+ assertEquals("flow statistics is null.", FlowStatsUtils.check(gstats));
+
+ FlowStatisticsBuilder builder = new FlowStatisticsBuilder();
+ gstats = builder.build();
+ assertEquals("No packet count.", FlowStatsUtils.check(gstats));
+
+ Counter64 packets = new Counter64(NumberUtils.getUnsigned(1234567L));
+ gstats = builder.setPacketCount(packets).build();
+ assertEquals("No byte count.", FlowStatsUtils.check(gstats));
+
+ Counter64 bytes = new Counter64(NumberUtils.getUnsigned(2345678L));
+ gstats = builder.setByteCount(packets).build();
+ assertEquals("No duration.", FlowStatsUtils.check(gstats));
+
+ DurationBuilder db = new DurationBuilder();
+ gstats = builder.setDuration(db.build()).build();
+ assertEquals("No second in duration.", FlowStatsUtils.check(gstats));
+
+ Counter32 sec = new Counter32(99999L);
+ gstats = builder.setDuration(db.setSecond(sec).build()).build();
+ assertEquals("No nanosecond in duration.",
+ FlowStatsUtils.check(gstats));
+
+ Counter32 nsec = new Counter32(888888888L);
+ gstats = builder.setDuration(db.setNanosecond(nsec).build()).build();
+ assertEquals(null, FlowStatsUtils.check(gstats));
+ }
+
+ /**
+ * Test case for {@link FlowStatsUtils#toNavigableMap(FlowStatsHistory)}.
+ */
+ @Test
+ public void testToNavigableMap() {
+ FlowStatsHistory history = null;
+ NavigableMap<Long, FlowStatsRecord> map =
+ FlowStatsUtils.toNavigableMap(history);
+ assertEquals(0, map.size());
+
+ history = new FlowStatsHistoryBuilder().build();
+ map = FlowStatsUtils.toNavigableMap(history);
+ assertEquals(0, map.size());
+
+ Long time1 = 1L;
+ Long time2 = 2L;
+ Long time3 = 3L;
+ FlowStatsRecord fsr1 = new FlowStatsRecordBuilder().
+ setTime(time1).setDuration(createDuration(1L, 100L)).build();
+ FlowStatsRecord fsr2 = new FlowStatsRecordBuilder().
+ setTime(time2).setDuration(createDuration(2L, 200L)).build();
+ FlowStatsRecord fsr3 = new FlowStatsRecordBuilder().
+ setTime(time3).setDuration(createDuration(3L, 300L)).build();
+ List<FlowStatsRecord> records = new ArrayList<>();
+ Collections.addAll(records, fsr2, fsr3, fsr1);
+ history = new FlowStatsHistoryBuilder().
+ setFlowStatsRecord(records).build();
+ map = FlowStatsUtils.toNavigableMap(history);
+ assertEquals(3, map.size());
+ assertEquals(fsr1, map.get(time1));
+ assertEquals(fsr2, map.get(time2));
+ assertEquals(fsr3, map.get(time3));
+ }
+
+ /**
+ * Test case for
+ * {@link FlowStatsUtils#addPeriodic(FlowStatsHistory,Long,GenericStatistics)}.
+ */
+ @Test
+ public void testAddPeriodic() {
+ // In case where statistics history is not present.
+ FlowStatsHistory history = null;
+ GenericStatistics gstats = createStatistics(100L, 199L, 1L, 1234567L);
+ Long time = 9999L;
+ List<FlowStatsRecord> expected = new ArrayList<>();
+ expected.add(new FlowStatsRecordBuilder(gstats).setTime(time).
+ setPeriodic(true).build());
+ FlowStatsHistory result =
+ FlowStatsUtils.addPeriodic(history, time, gstats);
+ assertEquals(expected, result.getFlowStatsRecord());
+
+ history = new FlowStatsHistoryBuilder().build();
+ result = FlowStatsUtils.addPeriodic(history, time, gstats);
+ assertEquals(expected, result.getFlowStatsRecord());
+
+ // Ensure that old entries are removed.
+ time = 100000000L;
+ long expire = time.longValue() - 60000L;
+ Boolean[] booleans = {Boolean.TRUE, Boolean.FALSE};
+ for (Boolean periodic: booleans) {
+ gstats = createStatistics(333L, 4444L, 100L, 55555555L);
+ FlowStatsRecord fsr1 = new FlowStatsRecordBuilder(gstats).
+ setTime(expire).setPeriodic(true).build();
+ gstats = createStatistics(350L, 4555L, 110L, 55556666L);
+ FlowStatsRecord fsr2 = new FlowStatsRecordBuilder(gstats).
+ setTime(expire + 10000L).setPeriodic(true).build();
+
+ long latestSec = 113L;
+ long latestNsec = 55556666L;
+ gstats = createStatistics(1234L, 56788L, latestSec, latestNsec);
+ FlowStatsRecord latest = new FlowStatsRecordBuilder(gstats).
+ setTime(expire + 13000L).setPeriodic(periodic).build();
+
+ gstats = createStatistics(300L, 3000L, 99L, 55556666L);
+ FlowStatsRecord old1 = new FlowStatsRecordBuilder(gstats).
+ setTime(expire - 10000L).setPeriodic(true).build();
+ FlowStatsRecord old2 = new FlowStatsRecordBuilder(gstats).
+ setTime(expire - 1L).build();
+
+ List<FlowStatsRecord> records = new ArrayList<>();
+ Collections.addAll(records, fsr2, old2, latest, old1, fsr1);
+ history = new FlowStatsHistoryBuilder().
+ setFlowStatsRecord(records).build();
+ gstats = createStatistics(1245L, 57890L, 120L, 55556677L);
+ FlowStatsRecord fsr = new FlowStatsRecordBuilder(gstats).
+ setTime(time).setPeriodic(true).build();
+ expected.clear();
+ Collections.addAll(expected, fsr1, fsr2, latest, fsr);
+ result = FlowStatsUtils.addPeriodic(history, time, gstats);
+ assertEquals(expected, result.getFlowStatsRecord());
+
+ if (!periodic.booleanValue()) {
+ // In case where the latest history is newer than the
+ // statistics to be added.
+ fsr = new FlowStatsRecordBuilder(latest).
+ setTime(time).setKey(null).setPeriodic(true).build();
+ expected.clear();
+ Collections.addAll(expected, fsr1, fsr2, fsr);
+ gstats = createStatistics(1234L, 56787L, latestSec,
+ latestNsec - 1);
+ result = FlowStatsUtils.addPeriodic(history, time, gstats);
+ assertEquals(expected, result.getFlowStatsRecord());
+ }
+ }
+ }
+
+ /**
+ * Test case for
+ * {@link FlowStatsUtils#addNonPeriodic(FlowStatsHistory,FlowStatsRecord)}.
+ */
+ @Test
+ public void testAddNonPeriodic() {
+ // In case where statistics history is not present.
+ FlowStatsHistory history = null;
+ GenericStatistics gstats = createStatistics(100L, 199L, 1L, 1234567L);
+ Long time = 10000000L;
+ FlowStatsRecord fsr = new FlowStatsRecordBuilder(gstats).
+ setTime(time).build();
+ List<FlowStatsRecord> expected = new ArrayList<>();
+ expected.add(fsr);
+ FlowStatsHistory result = FlowStatsUtils.addNonPeriodic(history, fsr);
+ assertEquals(expected, result.getFlowStatsRecord());
+
+ history = new FlowStatsHistoryBuilder().build();
+ result = FlowStatsUtils.addNonPeriodic(history, fsr);
+ assertEquals(expected, result.getFlowStatsRecord());
+
+ long latestTime = time - 10000L;
+ long[] shortIntervals = {1L, 2L, 100L, 999L};
+ long[] intervals = {1000L, 1001L, 2000L, 5555L};
+ Boolean[] booleans = {Boolean.TRUE, Boolean.FALSE};
+ for (Boolean periodic: booleans) {
+ gstats = createStatistics(100L, 200L, 4000L, 0L);
+ FlowStatsRecord fsr1 = new FlowStatsRecordBuilder(gstats).
+ setTime(time - 11000L).build();
+ gstats = createStatistics(111L, 222L, 5000L, 10L);
+ FlowStatsRecord latest = new FlowStatsRecordBuilder(gstats).
+ setTime(latestTime).setPeriodic(periodic).build();
+ List<FlowStatsRecord> records = new ArrayList<>();
+ Collections.addAll(records, latest, fsr1);
+ history = new FlowStatsHistoryBuilder().
+ setFlowStatsRecord(records).build();
+
+ // Too old records should be ignored.
+ for (long l = 0; l <= 10L; l++) {
+ fsr = new FlowStatsRecordBuilder(gstats).
+ setTime(latestTime - l).build();
+ assertEquals(null,
+ FlowStatsUtils.addNonPeriodic(history, fsr));
+ }
+
+ gstats = createStatistics(111L, 222L, 5001L, 1111L);
+ for (long l: shortIntervals) {
+ fsr = new FlowStatsRecordBuilder(gstats).
+ setTime(latestTime + l).build();
+ result = FlowStatsUtils.addNonPeriodic(history, fsr);
+ if (periodic.booleanValue()) {
+ // Too frequent record should be ignored.
+ assertEquals(null, result);
+ } else {
+ // Too frequent record should overwrite the latest
+ // non-periodic record.
+ expected.clear();
+ Collections.addAll(expected, fsr1, fsr);
+ assertEquals(expected, result.getFlowStatsRecord());
+ }
+ }
+
+ for (long l: intervals) {
+ fsr = new FlowStatsRecordBuilder(gstats).
+ setTime(latestTime + l).build();
+ expected.clear();
+ Collections.addAll(expected, fsr1, latest, fsr);
+ result = FlowStatsUtils.addNonPeriodic(history, fsr);
+ assertEquals(expected, result.getFlowStatsRecord());
+ }
+ }
+ }
+
+ /**
+ * Test case for
+ * {@link FlowStatsUtils#createTransactionKey(String,BigInteger)}.
+ */
+ @Test
+ public void testCreateTransactionKey() {
+ Random rand = new Random();
+ Set<String> nodeIds = new HashSet<>();
+ nodeIds.add("openflow:1");
+ nodeIds.add("openflow:18446744073709551615");
+ do {
+ long dpid = rand.nextLong();
+ nodeIds.add("openflow:" + NumberUtils.getUnsigned(dpid));
+ } while (nodeIds.size() < 20);
+
+ Set<BigInteger> xids = new HashSet<>();
+ xids.add(BigInteger.ZERO);
+ xids.add(BigInteger.ONE);
+ do {
+ xids.add(NumberUtils.getUnsigned(rand.nextLong()));
+ } while (xids.size() < 20);
+
+ Set<String> keys = new HashSet<>();
+ for (String nid: nodeIds) {
+ for (BigInteger xid: xids) {
+ String xkey = FlowStatsUtils.createTransactionKey(nid, xid);
+ assertEquals(true, keys.add(xkey));
+ }
+ }
+
+ assertEquals(nodeIds.size() * xids.size(), keys.size());
+
+ for (String nid: nodeIds) {
+ for (BigInteger xid: xids) {
+ String xkey = FlowStatsUtils.createTransactionKey(nid, xid);
+ assertEquals(false, keys.add(xkey));
+ assertEquals(true, keys.contains(xkey));
+ }
+ }
+ }
+
+ /**
+ * Create a MD-SAL flow statistics record.
+ *
+ * @param packets The number of transmitted packets.
+ * @param bytes The number of transmitted bytes.
+ * @param sec Duration in seconds.
+ * @param nsec Duration in milliseconds.
+ */
+ private static FlowStatistics createStatistics(long packets, long bytes,
+ long sec, long nsec) {
+ return new FlowStatisticsBuilder().
+ setPacketCount(new Counter64(NumberUtils.getUnsigned(packets))).
+ setByteCount(new Counter64(NumberUtils.getUnsigned(bytes))).
+ setDuration(createDuration(sec, nsec)).
+ build();
+ }
+}
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.types.rev150209.VtnSwitchPort;
import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.Ordered;
+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.AddFlowInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowTableRef;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.Flow;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Instructions;
/**
* Bits in a flow cookie which identifies the VTN flows.
*/
- private static final long VTN_FLOW_COOKIE = 0x7f56000000000000L;
+ public static final long VTN_FLOW_COOKIE = 0x7f56000000000000L;
/**
* An implementation of {@link VtnFlowTimeoutConfig} for test.
long duration = System.currentTimeMillis() - created;
long sec = duration / 1000L;
long nsec = (duration % 1000L) * 1000000L;
- Duration d = new DurationBuilder().
- setSecond(new Counter32(sec)).
- setNanosecond(new Counter32(nsec)).
- build();
+ Duration d = createDuration(sec, nsec);
DataFlowStats dfs = new DataFlowStatsBuilder().
setPacketCount(new Counter64(BigInteger.valueOf(packets))).
setByteCount(new Counter64(BigInteger.valueOf(bytes))).
}
}
+ /**
+ * Test case for {@link FlowUtils#compare(Duration,Duration)}.
+ */
+ @Test
+ public void testCompareDuration() {
+ Duration min = createDuration(0L, 0L);
+ Duration max = createDuration(0xffffffffL, 999999999L);
+ assertEquals(0, FlowUtils.compare(min, min));
+ assertEquals(0, FlowUtils.compare(max, max));
+ assertTrue(FlowUtils.compare(min, max) < 0);
+ assertTrue(FlowUtils.compare(max, min) > 0);
+
+ Duration d1 = createDuration(0L, 1L);
+ Duration d2 = createDuration(1L, 0L);
+ Duration d3 = createDuration(1L, 2L);
+ Duration d4 = createDuration(1L, 999999999L);
+ Duration d5 = createDuration(99L, 499999998L);
+ Duration d6 = createDuration(99L, 499999999L);
+ Duration d7 = createDuration(99L, 500000000L);
+ Duration d8 = createDuration(99L, 500000001L);
+ Duration d9 = createDuration(100L, 500000000L);
+ Duration d10 = createDuration(101L, 500000000L);
+ Duration d11 = createDuration(101L, 500000001L);
+ Duration d12 = createDuration(12345L, 888888888L);
+ Duration d13 = createDuration(12345L, 888888889L);
+ Duration d14 = createDuration(0xffffffffL, 0L);
+ Duration d15 = createDuration(0xffffffffL, 999999998L);
+ List<Duration> list = new ArrayList<>();
+ Collections.addAll(list, d10, max, d7, d1, d4, d9, min, d5, d2, d13,
+ d15, d6, d14, d8, d11, d3, d12);
+ List<Duration> expected = new ArrayList<>();
+ Collections.addAll(expected, min, d1, d2, d3, d4, d5, d6, d7, d8, d9,
+ d10, d11, d12, d13, d14, d15, max);
+
+ Comparator<Duration> comp = new Comparator<Duration>() {
+ @Override
+ public int compare(Duration dr1, Duration dr2) {
+ return FlowUtils.compare(dr1, dr2);
+ }
+ };
+
+ Collections.sort(list, comp);
+ assertEquals(expected, list);
+ }
+
/**
* Test case for {@link FlowUtils#toVNodeRoutes(List, Comparator)}.
*
build();
String value = String.format("%s%x-%s", prefix, c, order);
assertEquals(new Uri(value),
- FlowUtils.createTxUri(vfent, prefix));
+ FlowUtils.createTxUri(vfent, prefix));
}
}
}
assertEquals(Boolean.TRUE, input.isStrict());
assertEquals(Boolean.TRUE, input.isBarrier());
assertEquals(null, input.getOutPort());
+ assertEquals(null, input.getOutGroup());
+ assertEquals(null, input.getBufferId());
+ assertEquals(null, input.getContainerName());
+ assertEquals(null, input.getFlowName());
+ assertEquals(null, input.isInstallHw());
FlowTableRef tref =
new FlowTableRef(ingress.getFlowTableIdentifier(table));
assertEquals(Boolean.FALSE, input.isStrict());
assertEquals(Boolean.TRUE, input.isBarrier());
assertEquals(null, input.getOutPort());
+ assertEquals(null, input.getOutGroup());
+ assertEquals(null, input.getBufferId());
+ assertEquals(null, input.getContainerName());
+ assertEquals(null, input.getFlowName());
+ assertEquals(null, input.isInstallHw());
}
}
assertEquals(Boolean.TRUE, input.isStrict());
assertEquals(Boolean.TRUE, input.isBarrier());
assertEquals(null, input.getOutPort());
+ assertEquals(null, input.getOutGroup());
+ assertEquals(null, input.getBufferId());
+ assertEquals(null, input.getContainerName());
+ assertEquals(null, input.getFlowName());
+ assertEquals(null, input.isInstallHw());
FlowTableRef tref =
new FlowTableRef(ingress.getFlowTableIdentifier(table));
assertEquals(Boolean.TRUE, input.isStrict());
assertEquals(Boolean.TRUE, input.isBarrier());
assertEquals(null, input.getOutPort());
+ assertEquals(null, input.getOutGroup());
+ assertEquals(null, input.getBufferId());
+ assertEquals(null, input.getContainerName());
+ assertEquals(null, input.getFlowName());
+ assertEquals(null, input.isInstallHw());
FlowTableRef tref =
new FlowTableRef(ingress.getFlowTableIdentifier(table));
long c = flowId | VTN_FLOW_COOKIE;
FlowCookie cookie = new FlowCookie(NumberUtils.getUnsigned(c));
FlowCookie cookieMask = new FlowCookie(NumberUtils.getUnsigned(-1L));
- VtnNode vnode = new VtnNodeBuilder().setId(snode.getNodeId()).build();
RemoveFlowInput input =
FlowUtils.createRemoveFlowInput(snode, vfent);
assertEquals(Boolean.TRUE, input.isStrict());
assertEquals(Boolean.TRUE, input.isBarrier());
assertEquals(null, input.getOutPort());
+ assertEquals(null, input.getOutGroup());
+ assertEquals(null, input.getBufferId());
+ assertEquals(null, input.getContainerName());
+ assertEquals(null, input.getFlowName());
+ assertEquals(null, input.isInstallHw());
FlowTableRef tref =
new FlowTableRef(ingress.getFlowTableIdentifier(table));
assertEquals(Boolean.FALSE, input.isStrict());
assertEquals(Boolean.TRUE, input.isBarrier());
assertEquals(null, input.getOutPort());
+ assertEquals(null, input.getOutGroup());
+ assertEquals(null, input.getBufferId());
+ assertEquals(null, input.getContainerName());
+ assertEquals(null, input.getFlowName());
+ assertEquals(null, input.isInstallHw());
FlowTableRef tref =
new FlowTableRef(snode.getFlowTableIdentifier(table));
assertEquals(Boolean.FALSE, input.isStrict());
assertEquals(Boolean.TRUE, input.isBarrier());
assertEquals(tref, input.getFlowTable());
+ assertEquals(null, input.getOutGroup());
+ assertEquals(null, input.getBufferId());
+ assertEquals(null, input.getContainerName());
+ assertEquals(null, input.getFlowName());
+ assertEquals(null, input.isInstallHw());
uri = new Uri(String.format("remove-flow:OUT_PORT=%s", sport));
assertEquals(uri, input.getTransactionUri());
assertEquals(portNum, input.getOutPort());
}
+ /**
+ * Test case for
+ * {@link FlowUtils#createRemoveFlowInput(SalNode,Flow,Uri)}.
+ *
+ * @throws Exception An error occurred.
+ */
+ @Test
+ public void testCreateRemoveFlowInput6() throws Exception {
+ long flowId = 0x7777777777L;
+ int order = 123;
+ SalPort ingress = new SalPort(345L, 88L);
+ SalNode snode = ingress.getSalNode();
+ Short table = 0;
+ VtnFlowEntry vfent = createVtnFlowEntry(flowId, order, ingress);
+ Flow flow = new FlowBuilder(vfent).build();
+ long c = flowId | VTN_FLOW_COOKIE;
+ FlowCookie cookie = new FlowCookie(NumberUtils.getUnsigned(c));
+ FlowCookie cookieMask = new FlowCookie(NumberUtils.getUnsigned(-1L));
+ Uri uri = new Uri("remove-flow-input-5:" + flowId);
+
+ RemoveFlowInput input =
+ FlowUtils.createRemoveFlowInput(snode, flow, uri);
+ assertEquals(ingress.getNodeRef(), input.getNode());
+ assertEquals(vfent.getPriority(), input.getPriority());
+ assertEquals(table, input.getTableId());
+ assertEquals(vfent.getIdleTimeout(), input.getIdleTimeout());
+ assertEquals(vfent.getHardTimeout(), input.getHardTimeout());
+ assertEquals(cookie, input.getCookie());
+ assertEquals(cookieMask, input.getCookieMask());
+ assertEquals(vfent.getMatch(), input.getMatch());
+ assertEquals(vfent.getFlags(), input.getFlags());
+ assertEquals(vfent.getInstructions(), input.getInstructions());
+ assertEquals(Boolean.TRUE, input.isStrict());
+ assertEquals(Boolean.TRUE, input.isBarrier());
+ assertEquals(null, input.getOutPort());
+ assertEquals(null, input.getOutGroup());
+ assertEquals(null, input.getBufferId());
+ assertEquals(null, input.getContainerName());
+ assertEquals(null, input.getFlowName());
+ assertEquals(null, input.isInstallHw());
+ assertEquals(uri, input.getTransactionUri());
+
+ FlowTableRef tref =
+ new FlowTableRef(ingress.getFlowTableIdentifier(table));
+ assertEquals(tref, input.getFlowTable());
+ }
+
/**
* Test case for
* {@link FlowUtils#removeFlowIdSet(ReadWriteTransaction,InstanceIdentifier)}.
* switch port.
* @throws Exception An error occurred.
*/
- private VtnFlowEntry createVtnFlowEntry(long id, int order,
- SalPort ingress) throws Exception {
+ public static VtnFlowEntry createVtnFlowEntry(
+ long id, int order, SalPort ingress) throws Exception {
SalPort egress = new SalPort(ingress.getNodeNumber(), 9999L);
NodeId node = ingress.getNodeId();
Integer pri = 15;
setInstructions(insts).
build();
}
+
+ /**
+ * Create a {@link Duration} instance.
+ *
+ * @param sec The number of seconds.
+ * @param nsec The number of nanoseconds.
+ */
+ public static Duration createDuration(Long sec, Long nsec) {
+ DurationBuilder builder = new DurationBuilder();
+ if (sec != null) {
+ builder.setSecond(new Counter32(sec));
+ }
+ if (nsec != null) {
+ builder.setNanosecond(new Counter32(nsec));
+ }
+
+ return builder.build();
+ }
}
* 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.vtn.manager.internal.util.tx;
-import org.junit.Assert;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
import org.junit.AfterClass;
+import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
-import java.util.List;
-import java.util.ArrayList;
+import org.junit.experimental.categories.Category;
+
+import org.opendaylight.vtn.manager.internal.TxContext;
import org.opendaylight.vtn.manager.internal.TxTask;
import org.opendaylight.vtn.manager.internal.util.concurrent.VTNFuture;
import org.opendaylight.vtn.manager.internal.util.concurrent.VTNFutureTask;
-import java.util.concurrent.Callable;
-import org.opendaylight.vtn.manager.internal.TxContext;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
+
+import org.opendaylight.vtn.manager.internal.SlowTest;
/**
* JUnit test for {@link TxSyncFuture}
*/
+@Category(SlowTest.class)
public class TxSyncFutureTest {
/**
- * Static instance of TxTask to perform unit testing.
- */
+ * Static instance of TxTask to perform unit testing.
+ */
public static TxTask<Integer> txTask;
+
/**
- * Static instance of VTNFuture to perform unit testing.
- */
+ * Static instance of VTNFuture to perform unit testing.
+ */
public static VTNFuture<Integer> vtnFuture;
+
/**
- * Static instance of Callable to perform unit testing.
- */
+ * Static instance of Callable to perform unit testing.
+ */
public static Callable<Integer> callable;
+
/**
- * Static instance of CompositeTxTask to perform unit testing.
- */
+ * Static instance of CompositeTxTask to perform unit testing.
+ */
public static CompositeTxTask compostiteTxTask;
+
/**
- * Static instance of TxSyncFuture to perform unit testing.
- */
+ * Static instance of TxSyncFuture to perform unit testing.
+ */
public static TxSyncFuture<Integer> txSyncFuture;
/**
/**
* Test method for
- * {@link TxSyncFuture#cancel()}.
+ * {@link TxSyncFuture#cancel(boolean)}.
*/
@Test
public void testCancel() {
/**
* Test method for
* {@link TxSyncFuture#get()} and
- * {@link TxSyncFuture#ger(long, TimeUnit}.
+ * {@link TxSyncFuture#get(long, TimeUnit)}.
*/
@Test
public void testGet() {
--- /dev/null
+/*
+ * Copyright (c) 2015 NEC Corporation
+ * 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.vtn.manager.it.ofmock.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@code FlowEntryRemover} is used to remove flow entries that match the
+ * specified condition.
+ */
+class FlowEntryRemover implements FlowTableScanner {
+ /**
+ * A list of flow entries removed from the flow table.
+ */
+ private final List<OfMockFlowEntry> removedFlows = new ArrayList<>();
+
+ /**
+ * Return removed flow entries.
+ *
+ * @return A list of flow entries removed from the flow table.
+ */
+ public List<OfMockFlowEntry> getRemovedFlows() {
+ return removedFlows;
+ }
+
+ // FlowTableScanner
+
+ /**
+ * Invoked when a flow entry to be removed has been found.
+ *
+ * @param ofent A flow entry to be removed.
+ * @return {@code true}.
+ */
+ @Override
+ public boolean flowEntryFound(OfMockFlowEntry ofent) {
+ removedFlows.add(ofent);
+ return true;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 NEC Corporation
+ * 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.vtn.manager.it.ofmock.impl;
+
+/**
+ * {@code FlowTableScanner} is used to scan flow entries in a flow table
+ * in a mock-up of OpenFlow switch.
+ */
+interface FlowTableScanner {
+ /**
+ * Invoked when a flow entry that satisfies the specified condition has
+ * been found.
+ *
+ * @param ofent A flow entry.
+ * @return {@code true} means the given flow entry should be removed from
+ * the flow table. {@code false} means the given flow entry should
+ * be retained.
+ */
+ boolean flowEntryFound(OfMockFlowEntry ofent);
+}
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowsStatisticsUpdateBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAggregateFlowStatisticsFromFlowTableForAllFlowsInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAggregateFlowStatisticsFromFlowTableForAllFlowsOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAggregateFlowStatisticsFromFlowTableForGivenMatchInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAllFlowsStatisticsFromAllFlowTablesOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetFlowStatisticsFromFlowTableInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetFlowStatisticsFromFlowTableOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetFlowStatisticsFromFlowTableOutputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.OpendaylightFlowStatisticsService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.and.statistics.map.list.FlowAndStatisticsMapList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.and.statistics.map.list.FlowAndStatisticsMapListBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.GetFlowTablesStatisticsInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.GetFlowTablesStatisticsOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.OpendaylightFlowTableStatisticsService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.TransactionId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.Flow;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.RemovedReasonFlags;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.GetMeterStatisticsInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.GetMeterStatisticsOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.OpendaylightMeterStatisticsService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.duration.Duration;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.duration.DurationBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.module.config.rev141015.NodeConfigService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.module.config.rev141015.SetConfigInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.module.config.rev141015.SetConfigOutput;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.Counter32;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.Counter64;
/**
* {@code OfNode} describes a mock-up of OpenFlow switch.
private static final long NODE_BUFSIZE = 256L;
/**
- * The number of packets to be notified by a FLOW_REMOVED message.
+ * The number of packets to be set in flow statistics.
*/
- private static final long FRM_PACKET_COUNT = 5555555L;
+ private static final long PACKET_COUNT = 5555555L;
/**
- * The number of bytes to be notified by a FLOW_REMOVED message.
+ * The number of bytes to be set in flow statistics.
*/
- private static final long FRM_BYTE_COUNT = 1234567890L;
+ private static final long BYTE_COUNT = 1234567890L;
/**
- * Duration (in seconds) of a flow entry to be notified by a FLOW_REMOVED
- * message.
+ * Duration (in seconds) of a flow entry to be set in flow statistics.
*/
- private static final long FRM_DURATION_SEC = 12345L;
+ private static final long DURATION_SEC = 12345L;
/**
- * Duration (in nanoseconds) of a flow entry to be notified by a
- * FLOW_REMOVED message.
+ * Duration (in nanoseconds) of a flow entry to be set in flow statistics.
*/
- private static final long FRM_DURATION_NANOSEC = 666777888L;
+ private static final long DURATION_NANOSEC = 666777888L;
/**
* Capabilities of the switch.
CAPABILITIES = Collections.unmodifiableList(cap);
}
+ /**
+ * A thread that notifies flow statistics.
+ */
+ private final class FlowStatsThread extends Thread
+ implements FlowTableScanner {
+ /**
+ * The transaction ID associated with the flow statistics request.
+ */
+ private final TransactionId transactionId;
+
+ /**
+ * The condition to select flow entries to be notified.
+ */
+ private final Flow condition;
+
+ /**
+ * A list of flow statistics.
+ */
+ private final List<FlowAndStatisticsMapList> flowStats =
+ new ArrayList<>();
+
+ /**
+ * Construct a new instance.
+ *
+ * @param xid
+ * @param cond The MD-SAL flow entry which specifies the condition to
+ * select flow entries.
+ */
+ private FlowStatsThread(TransactionId xid, Flow cond) {
+ super("FlowStatsThread: xid=" + xid.getValue());
+ transactionId = xid;
+ condition = cond;
+ }
+
+ /**
+ * Convert the given flow entry into a {@link FlowAndStatisticsMapList}
+ * instance.
+ *
+ * @param ofent The flow entry to be converted.
+ * @return A {@link FlowAndStatisticsMapList} instance.
+ */
+ private FlowAndStatisticsMapList toFlowAndStatisticsMapList(
+ OfMockFlowEntry ofent) {
+ Duration duration = new DurationBuilder().
+ setSecond(new Counter32(DURATION_SEC)).
+ setNanosecond(new Counter32(DURATION_NANOSEC)).
+ build();
+
+ return new FlowAndStatisticsMapListBuilder().
+ setPacketCount(new Counter64(BigInteger.valueOf(PACKET_COUNT))).
+ setByteCount(new Counter64(BigInteger.valueOf(BYTE_COUNT))).
+ setDuration(duration).
+ setPriority(Integer.valueOf(ofent.getPriority())).
+ setTableId(Short.valueOf((short)ofent.getTableId())).
+ setCookie(new FlowCookie(ofent.getCookie())).
+ setMatch(ofent.getMatch()).
+ setInstructions(ofent.getInstructions()).
+ setFlags(ofent.getFlowModFlags()).
+ setIdleTimeout(Integer.valueOf(ofent.getIdleTimeout())).
+ setHardTimeout(Integer.valueOf(ofent.getHardTimeout())).
+ build();
+ }
+
+ // Runnable
+
+ /**
+ * Scan the flow table, and notify flow statistics.
+ */
+ @Override
+ public void run() {
+ try {
+ scanFlowTable(condition, this);
+
+ // Publish a flows-statistics-update notification.
+ FlowsStatisticsUpdateBuilder builder =
+ new FlowsStatisticsUpdateBuilder();
+ builder.setId(new NodeId(nodeIdentifier)).
+ setMoreReplies(false).
+ setTransactionId(transactionId).
+ setFlowAndStatisticsMapList(flowStats);
+
+ ofMockProvider.publish(builder.build());
+ } catch (RuntimeException e) {
+ BigInteger xid = transactionId.getValue();
+ LOG.error("Unexpected exception: xid=" + xid, e);
+ }
+ }
+
+ // FlowTableScanner
+
+ /**
+ * Invoked when a flow entry to be notified has been found.
+ *
+ * @param ofent The flow entry to be notified.
+ * @return {@code false}.
+ */
+ @Override
+ public boolean flowEntryFound(OfMockFlowEntry ofent) {
+ Duration duration = new DurationBuilder().
+ setSecond(new Counter32(DURATION_SEC)).
+ setNanosecond(new Counter32(DURATION_NANOSEC)).
+ build();
+
+ FlowAndStatisticsMapList fs = new FlowAndStatisticsMapListBuilder().
+ setPacketCount(new Counter64(BigInteger.valueOf(PACKET_COUNT))).
+ setByteCount(new Counter64(BigInteger.valueOf(BYTE_COUNT))).
+ setDuration(duration).
+ setPriority(Integer.valueOf(ofent.getPriority())).
+ setTableId(Short.valueOf((short)ofent.getTableId())).
+ setCookie(new FlowCookie(ofent.getCookie())).
+ setMatch(ofent.getMatch()).
+ setInstructions(ofent.getInstructions()).
+ setFlags(ofent.getFlowModFlags()).
+ setIdleTimeout(Integer.valueOf(ofent.getIdleTimeout())).
+ setHardTimeout(Integer.valueOf(ofent.getHardTimeout())).
+ build();
+ flowStats.add(fs);
+
+ return false;
+ }
+ }
+
/**
* Construct a new instance.
*
}
/**
- * Process a bulk flow remove request.
+ * Scan the flow table.
*
- * @param removed A list to store removed flow entries.
- * @param input An input of this RPC.
+ * @param cond The MD-SAL flow entry which specifies the condition to
+ * select flow entries.
+ * @param scanner A {@link FlowTableScanner} instance.
+ * Flow entries that satisfies the condition specified by
+ * {@code flow} will be passed to this instance.
*/
- private synchronized void removeFlows(List<OfMockFlowEntry> removed,
- RemoveFlowInput input) {
- Short table = input.getTableId();
- Match match = input.getMatch();
- BigInteger oport = input.getOutPort();
- String out = (oport == null)
+ private synchronized void scanFlowTable(Flow cond,
+ FlowTableScanner scanner) {
+ Short table = cond.getTableId();
+ Match match = cond.getMatch();
+ BigInteger oport = cond.getOutPort();
+ String outPort = (oport == null)
? null
: OfMockUtils.getPortIdentifier(nodeIdentifier, oport);
- FlowCookie cookie = input.getCookie();
- FlowCookie cookieMask = input.getCookieMask();
+ FlowCookie cookie = cond.getCookie();
+ FlowCookie cookieMask = cond.getCookieMask();
for (Iterator<OfMockFlowEntry> it = flowEntries.iterator();
it.hasNext();) {
continue;
}
- if (out != null &&
- !OfMockUtils.hasOutput(ofent.getInstructions(), out)) {
+ if (outPort != null &&
+ !OfMockUtils.hasOutput(ofent.getInstructions(), outPort)) {
continue;
}
continue;
}
- it.remove();
- removed.add(ofent);
+ if (scanner.flowEntryFound(ofent)) {
+ it.remove();
+ }
}
+ }
+ /**
+ * Process a bulk flow remove request.
+ *
+ * @param input An input of this RPC.
+ * @return A list of flow entries removed from the flow table.
+ */
+ private synchronized List<OfMockFlowEntry> removeFlows(
+ RemoveFlowInput input) {
+ FlowEntryRemover remover = new FlowEntryRemover();
+ scanFlowTable(input, remover);
+
+ List<OfMockFlowEntry> removed = remover.getRemovedFlows();
if (!removed.isEmpty()) {
notifyAll();
}
+
+ return removed;
}
/**
new org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.mod.removed.MatchBuilder(ofent.getMatch()).build();
SwitchFlowRemovedBuilder builder = new SwitchFlowRemovedBuilder().
- setPacketCount(BigInteger.valueOf(FRM_PACKET_COUNT)).
- setByteCount(BigInteger.valueOf(FRM_BYTE_COUNT)).
- setDurationSec(Long.valueOf(FRM_DURATION_SEC)).
- setDurationNsec(Long.valueOf(FRM_DURATION_NANOSEC)).
+ setPacketCount(BigInteger.valueOf(PACKET_COUNT)).
+ setByteCount(BigInteger.valueOf(BYTE_COUNT)).
+ setDurationSec(Long.valueOf(DURATION_SEC)).
+ setDurationNsec(Long.valueOf(DURATION_NANOSEC)).
setPriority(Integer.valueOf(ofent.getPriority())).
setTableId(Short.valueOf((short)ofent.getTableId())).
setIdleTimeout(Integer.valueOf(ofent.getIdleTimeout())).
@Override
public Future<RpcResult<RemoveFlowOutput>> removeFlow(
RemoveFlowInput input) {
- List<OfMockFlowEntry> removed = new ArrayList<>();
+ List<OfMockFlowEntry> removed;
if (Boolean.TRUE.equals(input.isStrict())) {
+ removed = new ArrayList<>();
+ FlowCookie cookie = input.getCookie();
+ FlowCookie cookieMask = input.getCookieMask();
OfMockFlowEntry target;
try {
target = new OfMockFlowEntry(nodeIdentifier, input);
for (Iterator<OfMockFlowEntry> it = flowEntries.iterator();
it.hasNext();) {
OfMockFlowEntry ofent = it.next();
- if (ofent.equals(target)) {
+ if (ofent.equals(target) &&
+ checkCookie(ofent.getCookie(), cookie, cookieMask)) {
it.remove();
removed.add(ofent);
}
}
}
} else {
- removeFlows(removed, input);
+ removed = removeFlows(input);
}
RemovedReasonFlags reason =
@Override
public Future<RpcResult<GetFlowStatisticsFromFlowTableOutput>> getFlowStatisticsFromFlowTable(
GetFlowStatisticsFromFlowTableInput input) {
- return createUnsupported(GetFlowStatisticsFromFlowTableOutput.class);
+ // Start flow statistics read transaction.
+ TransactionId xid = createTransactionId();
+ FlowStatsThread t = new FlowStatsThread(xid, input);
+ t.start();
+
+ // Return the transaction ID only.
+ GetFlowStatisticsFromFlowTableOutputBuilder builder =
+ new GetFlowStatisticsFromFlowTableOutputBuilder();
+ builder.setTransactionId(xid);
+ return createRpcResult(builder.build());
}
// OpendaylightPortStatisticsService