org.onosproject.flowperf.FlowPerfApp.java Source code

Java tutorial

Introduction

Here is the source code for org.onosproject.flowperf.FlowPerfApp.java

Source

/*
 * Copyright 2016-present Open Networking Laboratory
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.onosproject.flowperf;

import static com.google.common.base.Strings.isNullOrEmpty;
import static org.apache.felix.scr.annotations.ReferenceCardinality.MANDATORY_UNARY;
import static org.onlab.util.Tools.get;
import static org.slf4j.LoggerFactory.getLogger;

import java.util.Dictionary;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Modified;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onlab.packet.MacAddress;
import org.onlab.util.Tools;
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.Device;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flow.DefaultFlowRule;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.FlowRuleEvent;
import org.onosproject.net.flow.FlowRuleListener;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.instructions.Instructions;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;

import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

/**
 * Application for measuring flow installation performance.
 * <p>
 * This application installs a bunch of flows, validates that all those flows have
 * been successfully added and immediately proceeds to remove all the added flows.
 */
@Component(immediate = true, enabled = true)
@Service(value = FlowPerfApp.class)
public class FlowPerfApp {
    private final Logger log = getLogger(getClass());

    @Reference(cardinality = MANDATORY_UNARY)
    protected DeviceService deviceService;

    @Reference(cardinality = MANDATORY_UNARY)
    protected FlowRuleService flowRuleService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected CoreService coreService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected ComponentConfigService configService;

    protected ApplicationId appId;

    private static final int DEFAULT_BATCH_SIZE = 200;
    private static final int DEFAULT_TOTAL_THREADS = 1;
    private static final int DEFAULT_TOTAL_FLOWS = 100000;
    private AtomicInteger pendingBatchCount;
    private CountDownLatch installationLatch;
    private CountDownLatch uninstallationLatch;
    private Iterator<Device> devices;
    private AtomicLong macIndex;

    List<FlowRule> addedRules = Lists.newArrayList();

    @Property(name = "totalFlows", intValue = DEFAULT_TOTAL_FLOWS, label = "Total number of flows")
    protected int totalFlows = DEFAULT_TOTAL_FLOWS;

    @Property(name = "batchSize", intValue = DEFAULT_BATCH_SIZE, label = "Number of flows per batch")
    protected int batchSize = DEFAULT_BATCH_SIZE;

    @Property(name = "totalThreads", intValue = DEFAULT_TOTAL_THREADS, label = "Number of installer threads")
    protected int totalThreads = DEFAULT_TOTAL_THREADS;

    private ExecutorService installer;
    private ExecutorService testRunner = Executors
            .newSingleThreadExecutor(Tools.groupedThreads("app/flow-perf-test-runner", ""));

    @Activate
    public void activate(ComponentContext context) {
        appId = coreService.registerApplication("org.onosproject.flowperf");
        configService.registerProperties(getClass());
        installer = Executors.newFixedThreadPool(totalThreads, Tools.groupedThreads("app/flow-perf-worker", "%d"));
        testRunner.submit(this::runTest);
        log.info("Started");
    }

    @Deactivate
    public void deactivate(ComponentContext context) {
        installer.shutdown();
        testRunner.shutdown();
        configService.unregisterProperties(getClass(), false);
        log.info("Stopped.");
    }

    private void runTest() {
        pendingBatchCount = new AtomicInteger(totalFlows / batchSize);
        installationLatch = new CountDownLatch(totalFlows);
        List<Device> deviceList = Lists.newArrayList();
        deviceService.getAvailableDevices().forEach(deviceList::add);
        devices = Iterables.cycle(deviceList).iterator();
        log.info("Starting installation. Total flows: {}, Total threads: {}, " + "Batch Size: {}", totalFlows,
                totalThreads, batchSize);

        macIndex = new AtomicLong(0);
        FlowRuleListener addMonitor = event -> {
            if (event.type() == FlowRuleEvent.Type.RULE_ADDED) {
                installationLatch.countDown();
            }
        };

        flowRuleService.addListener(addMonitor);
        long addStartTime = System.currentTimeMillis();
        for (int i = 0; i < totalThreads; ++i) {
            installer.submit(() -> {
                while (pendingBatchCount.getAndDecrement() > 0) {
                    List<FlowRule> batch = nextBatch(batchSize);
                    addedRules.addAll(batch);
                    flowRuleService.applyFlowRules(batch.toArray(new FlowRule[] {}));
                }
            });
        }

        // Wait till all the flows are in ADDED state.
        try {
            installationLatch.await();
        } catch (InterruptedException e) {
            Thread.interrupted();
        }
        log.info("Time to install {} flows: {} ms", totalFlows, System.currentTimeMillis() - addStartTime);
        flowRuleService.removeListener(addMonitor);

        uninstallationLatch = new CountDownLatch(totalFlows);
        FlowRuleListener removeListener = event -> {
            if (event.type() == FlowRuleEvent.Type.RULE_REMOVED) {
                uninstallationLatch.countDown();
            }
        };
        AtomicInteger currentIndex = new AtomicInteger(0);
        long removeStartTime = System.currentTimeMillis();
        flowRuleService.addListener(removeListener);
        // Uninstallation runs on a single thread.
        installer.submit(() -> {
            while (currentIndex.get() < addedRules.size()) {
                List<FlowRule> removeBatch = addedRules.subList(currentIndex.get(),
                        Math.min(currentIndex.get() + batchSize, addedRules.size()));
                currentIndex.addAndGet(removeBatch.size());
                flowRuleService.removeFlowRules(removeBatch.toArray(new FlowRule[] {}));
            }
        });
        try {
            uninstallationLatch.await();
        } catch (InterruptedException e) {
            Thread.interrupted();
        }
        log.info("Time to uninstall {} flows: {} ms", totalFlows, System.currentTimeMillis() - removeStartTime);
        flowRuleService.removeListener(removeListener);
    }

    private List<FlowRule> nextBatch(int size) {
        List<FlowRule> rules = Lists.newArrayList();
        for (int i = 0; i < size; ++i) {
            Device device = devices.next();
            long srcMac = macIndex.incrementAndGet();
            long dstMac = srcMac + 1;
            TrafficSelector selector = DefaultTrafficSelector.builder().matchEthSrc(MacAddress.valueOf(srcMac))
                    .matchEthDst(MacAddress.valueOf(dstMac)).matchInPort(PortNumber.portNumber(2)).build();
            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
                    .add(Instructions.createOutput(PortNumber.portNumber(3))).build();
            FlowRule rule = new DefaultFlowRule(device.id(), selector, treatment, 100, appId, 50000, true, null);
            rules.add(rule);
        }
        return rules;
    }

    @Modified
    public void modified(ComponentContext context) {
        if (context == null) {
            totalFlows = DEFAULT_TOTAL_FLOWS;
            batchSize = DEFAULT_BATCH_SIZE;
            totalThreads = DEFAULT_TOTAL_THREADS;
            return;
        }

        Dictionary properties = context.getProperties();

        int newTotalFlows = totalFlows;
        int newBatchSize = batchSize;
        int newTotalThreads = totalThreads;
        try {
            String s = get(properties, "batchSize");
            newTotalFlows = isNullOrEmpty(s) ? totalFlows : Integer.parseInt(s.trim());

            s = get(properties, "batchSize");
            newBatchSize = isNullOrEmpty(s) ? batchSize : Integer.parseInt(s.trim());

            s = get(properties, "totalThreads");
            newTotalThreads = isNullOrEmpty(s) ? totalThreads : Integer.parseInt(s.trim());

        } catch (NumberFormatException | ClassCastException e) {
            return;
        }

        boolean modified = newTotalFlows != totalFlows || newTotalThreads != totalThreads
                || newBatchSize != batchSize;

        // If nothing has changed, simply return.
        if (!modified) {
            return;
        }

        totalFlows = newTotalFlows;
        batchSize = newBatchSize;
        if (totalThreads != newTotalThreads) {
            totalThreads = newTotalThreads;
            installer.shutdown();
            installer = Executors.newFixedThreadPool(totalThreads, Tools.groupedThreads("flow-perf-worker", "%d"));
        }
    }
}