/* * Copyright (c) 2016 Ericsson Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.openflowplugin.applications.bulk.o.matic; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.TransactionChain; import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FlowWriterTxChain implements FlowCounterMBean { private static final Logger LOG = LoggerFactory.getLogger(FlowWriterTxChain.class); private final DataBroker dataBroker; private final ExecutorService flowPusher; private long startTime; private AtomicInteger writeOpStatus = new AtomicInteger(FlowCounter.OperationStatus.INIT.status()); private AtomicInteger countDpnWriteCompletion = new AtomicInteger(); private AtomicLong taskCompletionTime = new AtomicLong(); public FlowWriterTxChain(final DataBroker dataBroker, ExecutorService flowPusher){ this.dataBroker = dataBroker; this.flowPusher = flowPusher; LOG.info("Using Ping Pong Flow Tester Impl"); } public void addFlows(Integer dpnCount, Integer flowsPerDPN, int batchSize, int sleepMillis, int sleepAfter, short startTableId, short endTableId, boolean isCreateParents) { LOG.info("Using Transaction Chain Flow Writer Impl"); countDpnWriteCompletion.set(dpnCount); startTime = System.nanoTime(); for (int i = 1; i <= dpnCount; i++) { FlowHandlerTask task = new FlowHandlerTask(Integer.toString(i), flowsPerDPN, true, batchSize, sleepMillis, sleepAfter, startTableId, endTableId, isCreateParents); flowPusher.execute(task); } } public void deleteFlows(Integer dpnCount, Integer flowsPerDPN, int batchSize, short startTableId, short endTableId) { LOG.info("Using Transaction Chain Flow Writer Impl"); countDpnWriteCompletion.set(dpnCount); for (int i = 1; i <= dpnCount; i++) { FlowHandlerTask task = new FlowHandlerTask(Integer.toString(i), flowsPerDPN, false, batchSize, 0, 1, startTableId, endTableId, false); flowPusher.execute(task); } } @Override public int getWriteOpStatus() { return writeOpStatus.get(); } @Override public long getTaskCompletionTime() { return taskCompletionTime.get(); } private class FlowHandlerTask implements Runnable, TransactionChainListener { private final String dpId; private final boolean add; private final int flowsPerDpn; private final int batchSize; private final int sleepAfter; private final int sleepMillis; private final short startTableId; private final short endTableId; private final boolean isCreateParents; private AtomicInteger remainingTxReturn = new AtomicInteger(0); BindingTransactionChain txChain; public FlowHandlerTask(final String dpId, final int flowsPerDpn, final boolean add, final int batchSize, final int sleepMillis, final int sleepAfter, final short startTableId, final short endTableId, final boolean isCreateParents){ this.dpId = BulkOMaticUtils.DEVICE_TYPE_PREFIX + dpId; this.add = add; this.flowsPerDpn = flowsPerDpn; this.batchSize = batchSize; this.sleepMillis = sleepMillis; this.sleepAfter = sleepAfter; this.startTableId = startTableId; this.endTableId = endTableId; this.isCreateParents = isCreateParents; remainingTxReturn.set(flowsPerDpn/batchSize); } @Override public void run() { writeOpStatus.set(FlowCounter.OperationStatus.IN_PROGRESS.status()); short tableId = startTableId; int numSubmits = flowsPerDpn/batchSize; int sourceIp = 1; int newBatchSize = batchSize; LOG.info("Number of Txn for dpId: {} is: {}", dpId, numSubmits); txChain = dataBroker.createTransactionChain(this); LOG.info("Creating new txChain: {} for dpid: {}", txChain, dpId); for (int i = 1; i <= numSubmits; i++) { WriteTransaction writeTransaction; try { writeTransaction = txChain.newWriteOnlyTransaction(); } catch (Exception e) { LOG.error("Transaction creation failed in txChain: {}, due to: {}", txChain, e); break; } short k = tableId; for (; sourceIp <= newBatchSize; sourceIp++) { String flowId = "Flow-" + dpId + "." + k + "." + sourceIp; Flow flow = null; if (add) { Match match = BulkOMaticUtils.getMatch(sourceIp); flow = BulkOMaticUtils.buildFlow(k, flowId, match); } writeTxToDs(writeTransaction, flowId, BulkOMaticUtils.getFlowInstanceIdentifier(k, flowId, dpId), flow, sourceIp, k); if (sourceIp < newBatchSize) { short a = 1; short b = (short) (endTableId - startTableId + 1); k = (short) (((k + a) % b) + startTableId); } } LOG.debug("Submitting Txn for dpId: {}, begin tableId: {}, end tableId: {}, sourceIp: {}", dpId, tableId, k, sourceIp - 1); Futures.addCallback(writeTransaction.submit(), new DsCallBack(dpId, tableId, k, sourceIp)); // Wrap around tableId = (short) (((k + 1) % ((short) (endTableId - startTableId + 1))) + startTableId); newBatchSize += batchSize; if (((i % sleepAfter) == 0) && (sleepMillis > 0)) { try { Thread.sleep(sleepMillis); } catch (InterruptedException e) { LOG.error("Writer Thread Interrupted: {}", e.getMessage()); } } } LOG.info("Completed FlowHandlerTask thread for dpid: {}", dpId); } @Override public void onTransactionChainFailed(TransactionChain<?, ?> transactionChain, AsyncTransaction<?, ?> asyncTransaction, Throwable throwable) { LOG.error("Transaction chain: {} FAILED at asyncTransaction: {} due to: {}", transactionChain, asyncTransaction.getIdentifier(), throwable); transactionChain.close(); } @Override public void onTransactionChainSuccessful(TransactionChain<?, ?> transactionChain) { LOG.info("Transaction chain: {} closed successfully.", transactionChain); } private void writeTxToDs(WriteTransaction writeTransaction, String flowId, InstanceIdentifier<Flow> flowIid, Flow flow, Integer sourceIp, Short tableId){ if (add) { LOG.trace("Adding flow for flowId: {}, flowIid: {}", flowId, flowIid); writeTransaction.put(LogicalDatastoreType.CONFIGURATION, flowIid, flow, isCreateParents); } else { LOG.trace("Deleting flow for flowId: {}, flowIid: {}", flowId, flowIid); writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, flowIid); } } private class DsCallBack implements FutureCallback { private String dpId; private int sourceIp; private short endTableId; private short beginTableId; public DsCallBack(String dpId, Short beginTableId, Short endTableId, Integer sourceIp) { this.dpId = dpId; this.sourceIp = sourceIp; this.endTableId = endTableId; this.beginTableId = beginTableId; } @Override public void onSuccess(Object o) { if (remainingTxReturn.decrementAndGet() <= 0) { long dur = System.nanoTime() - startTime; LOG.info("Completed all flows installation for: dpid: {} in {}ns", dpId, dur); if(0 == countDpnWriteCompletion.decrementAndGet() && writeOpStatus.get() != FlowCounter.OperationStatus.FAILURE.status()) { writeOpStatus.set(FlowCounter.OperationStatus.SUCCESS.status()); taskCompletionTime.set(dur); } txChain.close(); } } public void onFailure(Throwable error) { if (remainingTxReturn.decrementAndGet() <= 0) { long dur = System.nanoTime() - startTime; LOG.info("Completed all flows installation for: dpid: {} in {}ns", dpId, dur); } LOG.error("Error: {} in Datastore write operation: dpid: {}, begin tableId: {}, " + "end tableId: {}, sourceIp: {} ", error, dpId, beginTableId, endTableId, sourceIp); writeOpStatus.set(FlowCounter.OperationStatus.FAILURE.status()); } } } }