org.opennms.netmgt.events.commands.StressCommand.java Source code

Java tutorial

Introduction

Here is the source code for org.opennms.netmgt.events.commands.StressCommand.java

Source

/*******************************************************************************
 * This file is part of OpenNMS(R).
 *
 * Copyright (C) 2016-2016 The OpenNMS Group, Inc.
 * OpenNMS(R) is Copyright (C) 1999-2016 The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * OpenNMS(R) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with OpenNMS(R).  If not, see:
 *      http://www.gnu.org/licenses/
 *
 * For more information contact:
 *     OpenNMS(R) Licensing <license@opennms.org>
 *     http://www.opennms.org/
 *     http://www.opennms.com/
 *******************************************************************************/

package org.opennms.netmgt.events.commands;

import static java.util.concurrent.TimeUnit.SECONDS;

import java.math.BigInteger;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

import org.apache.commons.jexl2.Expression;
import org.apache.commons.jexl2.JexlContext;
import org.apache.commons.jexl2.JexlEngine;
import org.apache.commons.jexl2.MapContext;
import org.apache.felix.gogo.commands.Command;
import org.apache.felix.gogo.commands.Option;
import org.apache.karaf.shell.console.OsgiCommandSupport;
import org.opennms.core.utils.InetAddressUtils;
import org.opennms.netmgt.events.api.EventForwarder;
import org.opennms.netmgt.model.events.EventBuilder;
import org.opennms.netmgt.xml.event.Event;
import org.opennms.netmgt.xml.event.Events;
import org.opennms.netmgt.xml.event.Log;

import com.codahale.metrics.ConsoleReporter;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.RateLimiter;
import com.google.common.util.concurrent.ThreadFactoryBuilder;

/**
 * Used to stress the event bus.
 *
 * @author jwhite
 */
@Command(scope = "events", name = "stress", description = "Stress the event bus with generated events.", detailedDescription = "Generate newSuspect events with increasing IP addresses:\n"
        + "\tevents:stress -u uei.opennms.org/internal/discovery/newSuspect -e 10 -s 1 -j \"i=i+1\" -j \"eb.setInterface(iputils:int2ip(167837696 + i))\"")
public class StressCommand extends OsgiCommandSupport {

    private static final String EVENT_SOURCE = "stress";

    private EventForwarder eventForwarder;

    @Option(name = "-e", aliases = "--eps", description = "events per seconds to generate per thread, defaults to 300", required = false, multiValued = false)
    int eventsPerSecondPerThread = 300;

    @Option(name = "-t", aliases = "--threads", description = "number of threads used to generated events, defaults to 1", required = false, multiValued = false)
    int numberOfThreads = 1;

    @Option(name = "-u", aliases = "--uei", description = "events uei", required = false, multiValued = false)
    String eventUei = "uei.opennms.org/alarms/trigger";

    @Option(name = "-s", aliases = "--seconds", description = "number of seconds to run, defaults to 60", required = false, multiValued = false)
    int numSeconds = 60;

    @Option(name = "-r", aliases = "--report", description = "number of seconds after which the report should be generated, defaults to 15", required = false, multiValued = false)
    int reportIntervalInSeconds = 15;

    @Option(name = "-j", aliases = "--jexl", description = "JEXL expressions", required = false, multiValued = true)
    List<String> jexlExpressions = null;

    @Option(name = "-b", aliases = "--batch-size", description = "The size of the log (batch size)", required = false, multiValued = false)
    int batchSize = 1;

    @Option(name = "-n", aliases = "--node-id", description = "The node id to associate with the generated event")
    Integer eventNodeId = null;

    @Option(name = "-i", aliases = "--interface", description = "The ip interface to associate with the generated event")
    String eventIpInterface = null;
    @Option(name = "-x", aliases = "--sync", description = "Use synchronous instead of asynchronous calls", required = false, multiValued = false)
    boolean isSynchronous = false;

    private final MetricRegistry metrics = new MetricRegistry();

    private final Meter eventsGenerated = metrics.meter("events-generated");

    private class JexlEventGenerator extends EventGenerator {
        private final JexlContext context = new MapContext();
        private final List<Expression> expressions = new ArrayList<>();

        public JexlEventGenerator(List<String> jexlExpressions) {
            JexlEngine engine = new JexlEngine();

            Map<String, Object> functions = Maps.newHashMap();
            functions.put("iputils", IpUtils.class);
            engine.setFunctions(functions);

            for (String jexlExpression : jexlExpressions) {
                expressions.add(engine.createExpression(jexlExpression));
            }
        }

        @Override
        public Event getNextEvent() {
            final EventBuilder eb = new EventBuilder(eventUei, EVENT_SOURCE);
            context.set("eb", eb);
            for (Expression expression : expressions) {
                expression.evaluate(context);
            }
            return eb.getEvent();
        }
    }

    private class EventGenerator implements Runnable {
        @Override
        public void run() {
            final RateLimiter rateLimiter = RateLimiter.create(eventsPerSecondPerThread);
            while (true) {
                Log log = new Log();
                log.setEvents(new Events());
                for (int i = 0; i < batchSize; i++) {
                    log.getEvents().getEventCollection().add(getNextEvent());
                }

                rateLimiter.acquire(batchSize);
                if (isSynchronous) {
                    eventForwarder.sendNowSync(log);
                } else {
                    eventForwarder.sendNow(log);
                }
                eventsGenerated.mark(batchSize);
                if (Thread.interrupted()) {
                    break;
                }
            }
        }

        public Event getNextEvent() {
            final EventBuilder eb = new EventBuilder(eventUei, EVENT_SOURCE);
            if (eventNodeId != null) {
                eb.setNodeid(eventNodeId.intValue());
            }
            if (eventIpInterface != null) {
                eb.setInterface(InetAddressUtils.addr(eventIpInterface));
            }
            return eb.getEvent();
        }
    }

    @Override
    protected Object doExecute() {
        // Apply sane lower bounds to all of the configurable options
        eventsPerSecondPerThread = Math.max(1, eventsPerSecondPerThread);
        numberOfThreads = Math.max(1, numberOfThreads);
        numSeconds = Math.max(1, numSeconds);
        reportIntervalInSeconds = Math.max(1, reportIntervalInSeconds);
        batchSize = Math.max(1, batchSize);
        boolean useJexl = jexlExpressions != null && jexlExpressions.size() > 0;

        // Display the effective settings and rates
        double eventsPerSecond = eventsPerSecondPerThread * numberOfThreads;
        System.out.printf("Generating %d events per second accross %d threads for %d seconds\n",
                eventsPerSecondPerThread, numberOfThreads, numSeconds);
        System.out.printf("\t with UEI: %s\n", eventUei);
        System.out.printf("\t with batch size: %d\n", batchSize);
        System.out.printf("\t with synchronous calls: %s\n", isSynchronous);
        System.out.printf("Which will yield an effective\n");
        System.out.printf("\t %.2f events per second\n", eventsPerSecond);
        System.out.printf("\t %.2f total events\n", eventsPerSecond * numSeconds);
        if (useJexl) {
            System.out.printf("Using JEXL expressions:\n");
            for (String jexlExpression : jexlExpressions) {
                System.out.printf("\t%s\n", jexlExpression);
            }
        }

        // Setup the reporter
        ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics).convertRatesTo(TimeUnit.SECONDS)
                .convertDurationsTo(TimeUnit.MILLISECONDS).build();

        // Setup the executor
        final ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("Event Generator #%d").build();
        final ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads, threadFactory);

        System.out.println("Starting.");
        try {
            reporter.start(reportIntervalInSeconds, TimeUnit.SECONDS);
            for (int i = 0; i < numberOfThreads; i++) {
                final EventGenerator eventGenerator = useJexl ? new JexlEventGenerator(jexlExpressions)
                        : new EventGenerator();
                executor.execute(eventGenerator);
            }
            System.out.println("Started.");

            // Wait until we timeout or get interrupted
            try {
                Thread.sleep(SECONDS.toMillis(numSeconds));
            } catch (InterruptedException e) {
            }

            // Stop!
            try {
                System.out.println("Stopping.");
                executor.shutdownNow();
                if (!executor.awaitTermination(2, TimeUnit.MINUTES)) {
                    System.err.println("The threads did not stop in time.");
                } else {
                    System.out.println("Stopped.");
                }
            } catch (InterruptedException e) {
            }
        } finally {
            // Make sure we always stop the reporter
            reporter.stop();
        }

        // And display one last report...
        reporter.report();
        return null;
    }

    public void setEventForwarder(EventForwarder eventForwarder) {
        this.eventForwarder = eventForwarder;
    }

    /**
     * Convenience methods added to the JEXL context
     */
    public static class IpUtils {
        public static InetAddress int2ip(int k) {
            final byte[] bytes = BigInteger.valueOf(k).toByteArray();
            try {
                return InetAddress.getByAddress(bytes);
            } catch (UnknownHostException e) {
                System.err.printf("Failed to convert %d to an InetAddress.", k);
                return null;
            }
        }
    }
}