org.onosproject.demo.DemoInstaller.java Source code

Java tutorial

Introduction

Here is the source code for org.onosproject.demo.DemoInstaller.java

Source

/*
 * Copyright 2014-2015 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.demo;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.apache.commons.lang.math.RandomUtils;
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.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onlab.packet.MacAddress;
import org.onosproject.cluster.ClusterService;
import org.onosproject.cluster.ControllerNode;
import org.onosproject.cluster.NodeId;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.Device;
import org.onosproject.net.Host;
import org.onosproject.net.HostId;
import org.onosproject.net.MastershipRole;
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.FlowRuleOperations;
import org.onosproject.net.flow.FlowRuleOperationsContext;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.host.HostService;
import org.onosproject.net.intent.Constraint;
import org.onosproject.net.intent.HostToHostIntent;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentService;
import org.slf4j.Logger;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Predicate;
import com.google.common.base.Stopwatch;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.ThreadFactoryBuilder;

import static org.slf4j.LoggerFactory.getLogger;

/**
 * Application to set up demos.
 */
@Component(immediate = true)
@Service
public class DemoInstaller implements DemoAPI {

    private final Logger log = getLogger(getClass());

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

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected IntentService intentService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected HostService hostService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected MastershipService mastershipService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected ClusterService clusterService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected DeviceService deviceService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected FlowRuleService flowService;

    private ExecutorService worker;

    private ExecutorService installWorker;

    private ApplicationId appId;

    private final Set<Intent> existingIntents = new HashSet<>();
    private RandomInstaller randomInstaller;

    private ObjectMapper mapper = new ObjectMapper();

    @Activate
    public void activate() {
        String nodeId = clusterService.getLocalNode().ip().toString();
        appId = coreService.registerApplication("org.onosproject.demo.installer." + nodeId);
        worker = Executors.newFixedThreadPool(1,
                new ThreadFactoryBuilder().setNameFormat("demo-app-worker").build());
        log.info("Started with Application ID {}", appId.id());
    }

    @Deactivate
    public void deactivate() {
        shutdownAndAwaitTermination(worker);
        if (installWorker != null && !installWorker.isShutdown()) {
            shutdownAndAwaitTermination(installWorker);
        }
        log.info("Stopped");
    }

    @Override
    public JsonNode flowTest(Optional<JsonNode> params) {
        int flowsPerDevice = 1000;
        int neighbours = 0;
        boolean remove = true;
        if (params.isPresent()) {
            flowsPerDevice = params.get().get("flowsPerDevice").asInt();
            neighbours = params.get().get("neighbours").asInt();
            remove = params.get().get("remove").asBoolean();
        }

        Future<JsonNode> future = worker.submit(new FlowTest(flowsPerDevice, neighbours, remove));

        try {
            return future.get(10, TimeUnit.SECONDS);
        } catch (InterruptedException | ExecutionException | TimeoutException e) {
            ObjectNode node = mapper.createObjectNode();
            node.put("Error", e.getMessage());
            return node;
        }
    }

    @Override
    public void setup(InstallType type, Optional<JsonNode> runParams) {
        switch (type) {
        case MESH:
            log.debug("Installing mesh intents");
            worker.execute(new MeshInstaller());
            break;
        case RANDOM:
            //check that we do not have a random installer running
            if (installWorker == null || installWorker.isShutdown()) {
                installWorker = Executors.newFixedThreadPool(1,
                        new ThreadFactoryBuilder().setNameFormat("random-worker").build());
                log.debug("Installing random sequence of intents");
                randomInstaller = new RandomInstaller(runParams);
                installWorker.execute(randomInstaller);
            } else {
                log.warn("Random installer is already running");
            }
            break;
        default:
            throw new IllegalArgumentException("What is it you want exactly?");
        }
    }

    @Override
    public void tearDown() {
        worker.submit(new UnInstaller());
    }

    /**
     * Simply installs a mesh of intents from all the hosts existing in the network.
     */
    private class MeshInstaller implements Runnable {

        @Override
        public void run() {
            TrafficSelector selector = DefaultTrafficSelector.emptySelector();
            TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
            List<Constraint> constraint = Lists.newArrayList();
            List<Host> hosts = Lists.newArrayList(hostService.getHosts());
            while (!hosts.isEmpty()) {
                Host src = hosts.remove(0);
                for (Host dst : hosts) {
                    HostToHostIntent intent = HostToHostIntent.builder().appId(appId).one(src.id()).two(dst.id())
                            .selector(selector).treatment(treatment).constraints(constraint).build();
                    existingIntents.add(intent);
                    intentService.submit(intent);
                }
            }
        }
    }

    /**
     * Randomly installs and withdraws intents.
     */
    private class RandomInstaller implements Runnable {

        private final boolean isLocal;
        private final Set<Host> hosts;

        private final Random random = new Random(System.currentTimeMillis());

        private Set<HostPair> uninstalledOrWithdrawn;
        private Set<HostPair> installed;

        private CountDownLatch latch;

        //used to wait on a batch to be processed.
        private static final int ITERATIONMAX = 50000000;

        public RandomInstaller(Optional<JsonNode> runParams) {
            /*
            Check if we have params and honour them. Otherwise
                set defaults to processing only local stuff and
                all local hosts.
             */
            if (runParams.isPresent()) {
                JsonNode node = runParams.get();
                isLocal = node.get("local").asBoolean();
                hosts = node.get("hosts") == null ? Sets.newHashSet(hostService.getHosts())
                        : constructHostIds(node.get("hosts").elements());
            } else {
                isLocal = true;
                hosts = Sets.newHashSet(hostService.getHosts());
            }

            //construct list of intents.
            installed = Sets.newHashSet();
            if (isLocal) {
                uninstalledOrWithdrawn = buildPairs(pruneHostsByMasterShip());
            } else {
                uninstalledOrWithdrawn = buildPairs(hosts);
            }

        }

        private Set<Host> constructHostIds(Iterator<JsonNode> elements) {
            Set<Host> hostIds = Sets.newHashSet();
            JsonNode n;
            while (elements.hasNext()) {
                n = elements.next();
                hostIds.add(hostService.getHost(HostId.hostId(n.textValue())));
            }
            return hostIds;
        }

        @Override
        public void run() {
            if (!installWorker.isShutdown()) {
                randomize();
                latch = new CountDownLatch(1);
                try {
                    trackIntents();
                } catch (InterruptedException e) {
                    shutdown();
                }
            }

        }

        /**
         *   Check whether the previously submitted batch is in progress
         *   and if yes submit the next one. If things hang, wait for at
         *   most 5 seconds and bail.
         * @throws InterruptedException if the thread go interupted
         */
        private void trackIntents() throws InterruptedException {
            //FIXME
            // TODO generate keys for each set of intents to allow manager to throttle
            // TODO may also look into the store to see how many operations are pending

            //if everything is good proceed.
            if (!installWorker.isShutdown()) {
                installWorker.execute(this);
            }

        }

        public void shutdown() {
            log.warn("Shutting down random installer!");
            cleanUp();
        }

        /**
         *   Shuffle the uninstalled and installed list (separately) and select
         *   a random number of them and install or uninstall them respectively.
         */
        private void randomize() {
            List<HostPair> hostList = new LinkedList<>(uninstalledOrWithdrawn);
            Collections.shuffle(hostList);
            List<HostPair> toInstall = hostList.subList(0, random.nextInt(hostList.size() - 1));
            List<HostPair> toRemove;
            if (!installed.isEmpty()) {
                hostList = new LinkedList<>(installed);
                Collections.shuffle(hostList);
                toRemove = hostList.subList(0, random.nextInt(hostList.size() - 1));
                uninstallIntents(toRemove);
            }
            installIntents(toInstall);

        }

        private void installIntents(List<HostPair> toInstall) {
            for (HostPair pair : toInstall) {
                installed.add(pair);
                uninstalledOrWithdrawn.remove(pair);
                intentService.submit(pair.h2hIntent());
            }
        }

        private void uninstallIntents(Collection<HostPair> toRemove) {
            for (HostPair pair : toRemove) {
                installed.remove(pair);
                uninstalledOrWithdrawn.add(pair);
                intentService.withdraw(pair.h2hIntent());
            }
        }

        /**
         *  Take everything and remove it all.
         */
        private void cleanUp() {
            List<HostPair> allPairs = Lists.newArrayList(installed);
            allPairs.addAll(uninstalledOrWithdrawn);
            for (HostPair pair : allPairs) {
                intentService.withdraw(pair.h2hIntent());
            }
        }

        private Set<HostPair> buildPairs(Set<Host> hosts) {
            Set<HostPair> pairs = Sets.newHashSet();
            Iterator<Host> it = Sets.newHashSet(hosts).iterator();
            while (it.hasNext()) {
                Host src = it.next();
                it.remove();
                for (Host dst : hosts) {
                    pairs.add(new HostPair(src, dst));
                }
            }
            return pairs;
        }

        private Set<Host> pruneHostsByMasterShip() {
            return FluentIterable.from(hosts).filter(hasLocalMaster()).toSet();

        }

        private Predicate<? super Host> hasLocalMaster() {
            return host -> mastershipService.getLocalRole(host.location().deviceId()).equals(MastershipRole.MASTER);
        }

        /**
         * Simple class representing a pair of hosts and precomputes the associated
         * h2h intent.
         */
        private class HostPair {

            private final Host src;
            private final Host dst;

            private final TrafficSelector selector = DefaultTrafficSelector.emptySelector();
            private final TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
            private final List<Constraint> constraint = Lists.newArrayList();
            private final HostToHostIntent intent;

            public HostPair(Host src, Host dst) {
                this.src = src;
                this.dst = dst;
                this.intent = HostToHostIntent.builder().appId(appId).one(src.id()).two(dst.id()).selector(selector)
                        .treatment(treatment).constraints(constraint).build();
            }

            public HostToHostIntent h2hIntent() {
                return intent;
            }

            @Override
            public boolean equals(Object o) {
                if (this == o) {
                    return true;
                }
                if (o == null || getClass() != o.getClass()) {
                    return false;
                }

                HostPair hostPair = (HostPair) o;

                return Objects.equals(src, hostPair.src) && Objects.equals(dst, hostPair.dst);

            }

            @Override
            public int hashCode() {
                return Objects.hash(src, dst);
            }

        }

    }

    /**
     * Remove anything that is running and clear it all out.
     */
    private class UnInstaller implements Runnable {
        @Override
        public void run() {
            if (!existingIntents.isEmpty()) {
                clearExistingIntents();
            }

            if (installWorker != null && !installWorker.isShutdown()) {
                shutdownAndAwaitTermination(installWorker);
                randomInstaller.shutdown();
            }
        }

        private void clearExistingIntents() {
            for (Intent i : existingIntents) {
                intentService.withdraw(i);
            }
            existingIntents.clear();
        }
    }

    /**
     * Shutdown a pool cleanly if possible.
     *
     * @param pool an executorService
     */
    private void shutdownAndAwaitTermination(ExecutorService pool) {
        pool.shutdown(); // Disable new tasks from being submitted
        try {
            // Wait a while for existing tasks to terminate
            if (!pool.awaitTermination(10, TimeUnit.SECONDS)) {
                pool.shutdownNow(); // Cancel currently executing tasks
                // Wait a while for tasks to respond to being cancelled
                if (!pool.awaitTermination(10, TimeUnit.SECONDS)) {
                    log.error("Pool did not terminate");
                }
            }
        } catch (Exception ie) {
            // (Re-)Cancel if current thread also interrupted
            pool.shutdownNow();
            // Preserve interrupt status
            Thread.currentThread().interrupt();
        }
    }

    private class FlowTest implements Callable<JsonNode> {
        private final int flowPerDevice;
        private final int neighbours;
        private final boolean remove;
        private FlowRuleOperations.Builder adds;
        private FlowRuleOperations.Builder removes;

        public FlowTest(int flowsPerDevice, int neighbours, boolean remove) {
            this.flowPerDevice = flowsPerDevice;
            this.neighbours = neighbours;
            this.remove = remove;
            prepareInstallation();
        }

        private void prepareInstallation() {
            Set<ControllerNode> instances = Sets.newHashSet(clusterService.getNodes());
            instances.remove(clusterService.getLocalNode());
            Set<NodeId> acceptableNodes = Sets.newHashSet();
            if (neighbours >= instances.size()) {
                instances.forEach(instance -> acceptableNodes.add(instance.id()));
            } else {
                Iterator<ControllerNode> nodes = instances.iterator();
                for (int i = neighbours; i > 0; i--) {
                    acceptableNodes.add(nodes.next().id());
                }
            }
            acceptableNodes.add(clusterService.getLocalNode().id());

            Set<Device> devices = Sets.newHashSet();
            for (Device dev : deviceService.getDevices()) {
                if (acceptableNodes.contains(mastershipService.getMasterFor(dev.id()))) {
                    devices.add(dev);
                }
            }

            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
                    .setOutput(PortNumber.portNumber(RandomUtils.nextInt())).build();
            TrafficSelector.Builder sbuilder;
            FlowRuleOperations.Builder rules = FlowRuleOperations.builder();
            FlowRuleOperations.Builder remove = FlowRuleOperations.builder();

            for (Device d : devices) {
                for (int i = 0; i < this.flowPerDevice; i++) {
                    sbuilder = DefaultTrafficSelector.builder();

                    sbuilder.matchEthSrc(MacAddress.valueOf(RandomUtils.nextInt() * i))
                            .matchEthDst(MacAddress.valueOf((Integer.MAX_VALUE - i) * RandomUtils.nextInt()));

                    int randomPriority = RandomUtils.nextInt();
                    FlowRule f = DefaultFlowRule.builder().forDevice(d.id()).withSelector(sbuilder.build())
                            .withTreatment(treatment).withPriority(randomPriority).fromApp(appId).makeTemporary(10)
                            .build();
                    rules.add(f);
                    remove.remove(f);

                }
            }

            this.adds = rules;
            this.removes = remove;
        }

        @Override
        public JsonNode call() throws Exception {
            ObjectNode node = mapper.createObjectNode();
            CountDownLatch latch = new CountDownLatch(1);
            flowService.apply(adds.build(new FlowRuleOperationsContext() {

                private final Stopwatch timer = Stopwatch.createStarted();

                @Override
                public void onSuccess(FlowRuleOperations ops) {

                    long elapsed = timer.elapsed(TimeUnit.MILLISECONDS);
                    node.put("elapsed", elapsed);

                    latch.countDown();
                }
            }));

            latch.await(10, TimeUnit.SECONDS);
            if (this.remove) {
                flowService.apply(removes.build());
            }
            return node;
        }
    }
}