diff options
Diffstat (limited to 'testsuites/performance/performance-benchmark-test/src/main/java')
9 files changed, 1458 insertions, 0 deletions
diff --git a/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/EventBatch.java b/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/EventBatch.java new file mode 100644 index 000000000..abf0c993f --- /dev/null +++ b/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/EventBatch.java @@ -0,0 +1,151 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2018 Ericsson. All rights reserved. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.apex.testsuites.performance.benchmark.eventgenerator; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +import org.onap.policy.apex.testsuites.performance.benchmark.eventgenerator.events.InputEvent; +import org.onap.policy.apex.testsuites.performance.benchmark.eventgenerator.events.OutputEvent; + +/** + * This class keeps track of a batch of events sent to an Apex instance. + */ +public class EventBatch { + private static AtomicInteger nextBatchNumber = new AtomicInteger(); + + private final int batchNumber = nextBatchNumber.getAndIncrement(); + private final Map<Integer, InputEvent> inputEventMap = new ConcurrentHashMap<>(); + private final Map<Integer, OutputEvent> outputEventMap = new ConcurrentHashMap<>(); + + private final int batchSize; + private final String apexClient; + + /** + * Create an event batch. + * + * @param batchSize the size of the batch + * @param apexClient the apex client to which the event batch will be sent + */ + public EventBatch(final int batchSize, final String apexClient) { + this.batchSize = batchSize; + this.apexClient = apexClient; + + // Create the events for the batch of events + for (int eventNumber = 0; eventNumber < batchSize; eventNumber++) { + InputEvent inputEvent = new InputEvent(); + inputEvent.setTestSlogan(getEventSlogan(eventNumber)); + inputEventMap.put(eventNumber, inputEvent); + } + } + + /** + * Get the batch of events as a JSON string. + * + * @return the JSON string representation of the batch of events. + */ + public String getBatchAsJsonString() { + if (batchSize == 1) { + return inputEventMap.get(0).asJson(); + } + + StringBuilder jsonBuilder = new StringBuilder(); + jsonBuilder.append("[\n"); + boolean first = true; + for (InputEvent inputEvent : inputEventMap.values()) { + if (first) { + first = false; + } + else { + jsonBuilder.append(",\n"); + } + jsonBuilder.append(inputEvent.asJson()); + } + jsonBuilder.append("\n]\n"); + + return jsonBuilder.toString(); + } + + public int getBatchNumber() { + return batchNumber; + } + + public int getBatchSize() { + return batchSize; + } + + public String getApexClient() { + return apexClient; + } + + /** + * Get the event slogan. + * + * @param eventNumber the number of this event + * @return the event slogan + */ + private String getEventSlogan(final int eventNumber) { + StringBuilder testSloganBuilder = new StringBuilder(); + testSloganBuilder.append(batchNumber); + testSloganBuilder.append('-'); + testSloganBuilder.append(eventNumber); + testSloganBuilder.append(": "); + testSloganBuilder.append(apexClient); + + return testSloganBuilder.toString(); + } + + /** + * Handle a response event. + * + * @param responseEvent the response event + */ + public void handleResponse(OutputEvent responseEvent) { + outputEventMap.put(responseEvent.findEventNumber(), responseEvent); + } + + /** + * Get the statistics on this event batch. + * @return the event batch statistics + */ + public EventBatchStats getStats() { + return new EventBatchStats(this); + } + + /** + * Get an input event for an event number. + * @param eventNo the event number + * @return the event + */ + public InputEvent getInputEvent(int eventNo) { + return inputEventMap.get(eventNo); + } + + /** + * Get an output event for an event number. + * @param eventNo the event number + * @return the event + */ + public OutputEvent getOutputEvent(int eventNo) { + return outputEventMap.get(eventNo); + } +} diff --git a/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/EventBatchStats.java b/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/EventBatchStats.java new file mode 100644 index 000000000..321cd28e2 --- /dev/null +++ b/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/EventBatchStats.java @@ -0,0 +1,239 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2018 Ericsson. All rights reserved. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.apex.testsuites.performance.benchmark.eventgenerator; + +import java.util.List; + +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.onap.policy.apex.testsuites.performance.benchmark.eventgenerator.events.OutputEvent; + +/** + * This POJO class returns statistics on a event batch execution in Apex. + */ +public class EventBatchStats { + private final int batchNumber; + private final int batchSize; + private final String apexClient; + + // @formatter:off + private long eventsNotSent = 0; + private long eventsSent = 0; + private long eventsNotReceived = 0; + private long eventsReceived = 0; + private long averageRoundTripNano = 0; + private long shortestRoundTripNano = Long.MAX_VALUE; + private long longestRoundTripNano = 0; + private long averageApexExecutionNano = 0; + private long shortestApexExecutionNano = Long.MAX_VALUE; + private long longestApexExecutionNano = 0; + // @formatter:on + + /** + * Create a statistics object for an event batch. + * + * @param eventBatch the event batch for these statistics + */ + public EventBatchStats(final EventBatch eventBatch) { + this.batchNumber = eventBatch.getBatchNumber(); + this.batchSize = eventBatch.getBatchSize(); + this.apexClient = eventBatch.getApexClient(); + + calcutateStats(eventBatch); + } + + /** + * Create a total statistics object for a list of event batches. + * + * @param eventBatchStatsList the event batch for these statistics + */ + public EventBatchStats(final List<EventBatchStats> eventBatchStatsList) { + this.batchNumber = -1; + this.apexClient = "TOTAL"; + + calcutateStats(eventBatchStatsList); + + this.batchSize = (int)(eventsNotSent + eventsSent); + } + + /** + * Compile the statistics. + * @param eventBatch the event batch for which statisticss should be calculated + */ + private void calcutateStats(final EventBatch eventBatch) { + long accumulatedRoundTripTime = 0; + long accumulatedApexExecutionTime = 0; + + for (int eventNo = 0; eventNo < batchSize; eventNo++) { + Pair<Long, Long> eventTimings = calculateEventTimings(eventBatch, eventNo); + if (eventTimings == null) { + // The event has not been sent yet or the response has not been received yet + continue; + } + + accumulatedRoundTripTime += eventTimings.getLeft(); + accumulatedApexExecutionTime += eventTimings.getRight(); + } + + if (eventsReceived != 0) { + averageRoundTripNano = accumulatedRoundTripTime / eventsReceived; + averageApexExecutionNano = accumulatedApexExecutionTime / eventsReceived; + } + } + + /** + * Compile the statistics. + * @param eventBatchStatsList the event batch list for which statistics should be calculated + */ + private void calcutateStats(final List<EventBatchStats> eventBatchStatsList) { + long accumulatedRoundTripTime = 0; + long accumulatedApexExecutionTime = 0; + + for (EventBatchStats eventBatchStats: eventBatchStatsList) { + // @formatter:off + eventsNotSent += eventBatchStats.getEventsNotSent(); + eventsSent += eventBatchStats.getEventsSent(); + eventsNotReceived += eventBatchStats.getEventsNotReceived(); + eventsReceived += eventBatchStats.getEventsReceived(); + // @formatter:on + + if (shortestRoundTripNano > eventBatchStats.getShortestRoundTripNano()) { + shortestRoundTripNano = eventBatchStats.getShortestRoundTripNano(); + } + + if (shortestApexExecutionNano > eventBatchStats.getShortestApexExecutionNano()) { + shortestApexExecutionNano = eventBatchStats.getShortestApexExecutionNano(); + } + + if (longestRoundTripNano < eventBatchStats.getLongestRoundTripNano()) { + longestRoundTripNano = eventBatchStats.getLongestRoundTripNano(); + } + + if (longestApexExecutionNano < eventBatchStats.getLongestApexExecutionNano()) { + longestApexExecutionNano = eventBatchStats.getLongestApexExecutionNano(); + } + + accumulatedRoundTripTime += eventBatchStats.getAverageRoundTripNano(); + accumulatedApexExecutionTime += eventBatchStats.getAverageApexExecutionNano(); + } + + if (!eventBatchStatsList.isEmpty()) { + averageRoundTripNano = accumulatedRoundTripTime / eventBatchStatsList.size(); + averageApexExecutionNano = accumulatedApexExecutionTime / eventBatchStatsList.size(); + } + } + + /** + * Calculate statistics for a single event. + * @param eventBatch the event batch for the event + * @param eventNo the event number of the event + * @return + */ + private Pair<Long, Long> calculateEventTimings(EventBatch eventBatch, int eventNo) { + // If an event is in a batch, it has been sent + eventsSent++; + + OutputEvent outputEvent = eventBatch.getOutputEvent(eventNo); + + if (outputEvent == null) { + eventsNotReceived++; + return null; + + } + else { + eventsReceived++; + } + + long roundTrip = outputEvent.getTestReceviedTimestamp() - outputEvent.getTestTimestamp(); + long apexExecution = outputEvent.getTestActStateTime() - outputEvent.getTestMatchStateTime(); + + + if (shortestRoundTripNano > roundTrip) { + shortestRoundTripNano = roundTrip; + } + + if (shortestApexExecutionNano > apexExecution) { + shortestApexExecutionNano = apexExecution; + } + + if (longestRoundTripNano < roundTrip) { + longestRoundTripNano = roundTrip; + } + + if (longestApexExecutionNano < apexExecution) { + longestApexExecutionNano = apexExecution; + } + + return new ImmutablePair<>(roundTrip, apexExecution); + } + + public int getBatchNumber() { + return batchNumber; + } + + public int getBatchSize() { + return batchSize; + } + + public String getApexClient() { + return apexClient; + } + + public long getEventsNotSent() { + return eventsNotSent; + } + + public long getEventsSent() { + return eventsSent; + } + + public long getEventsNotReceived() { + return eventsNotReceived; + } + + public long getEventsReceived() { + return eventsReceived; + } + + public long getAverageRoundTripNano() { + return averageRoundTripNano; + } + + public long getShortestRoundTripNano() { + return shortestRoundTripNano; + } + + public long getLongestRoundTripNano() { + return longestRoundTripNano; + } + + public long getAverageApexExecutionNano() { + return averageApexExecutionNano; + } + + public long getShortestApexExecutionNano() { + return shortestApexExecutionNano; + } + + public long getLongestApexExecutionNano() { + return longestApexExecutionNano; + } +} diff --git a/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/EventGenerator.java b/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/EventGenerator.java new file mode 100644 index 000000000..a5ed37803 --- /dev/null +++ b/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/EventGenerator.java @@ -0,0 +1,185 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2018 Ericsson. All rights reserved. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.apex.testsuites.performance.benchmark.eventgenerator; + +import java.io.IOException; +import java.net.URI; +import java.util.Arrays; + +import org.apache.commons.cli.ParseException; +import org.glassfish.grizzly.http.server.HttpServer; +import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory; +import org.glassfish.jersey.server.ResourceConfig; +import org.onap.policy.apex.core.infrastructure.threading.ThreadUtilities; +import org.onap.policy.apex.model.utilities.TextFileUtils; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +/** + * This class is the main class of a REST server that generates sample events. + */ +public class EventGenerator { + // Get a reference to the logger + private static final XLogger LOGGER = XLoggerFactory.getXLogger(EventGenerator.class); + + // Parameters for event generation + private final EventGeneratorParameters parameters; + + // The HTTP server we are running + private final HttpServer eventGeneratorServer; + + /** + * Instantiates a new event generator with the given parameters. + * + * @param parameters the parameters for the event generator + */ + public EventGenerator(final EventGeneratorParameters parameters) { + this.parameters = parameters; + + // Set the parameters in the event generator endpoint + EventGeneratorEndpoint.clearEventGenerationStats(); + EventGeneratorEndpoint.setParameters(parameters); + + // Add a shutdown hook to shut down the rest services when the process is exiting + Runtime.getRuntime().addShutdownHook(new Thread(new EventGeneratorShutdownHook())); + + LOGGER.info("Event generator REST server starting"); + + final ResourceConfig rc = new ResourceConfig(EventGeneratorEndpoint.class); + eventGeneratorServer = GrizzlyHttpServerFactory.createHttpServer(getBaseUri(), rc); + + // Wait for the HTTP server to come up + while (!eventGeneratorServer.isStarted()) { + ThreadUtilities.sleep(50); + } + + LOGGER.info("Event generator REST server started"); + } + + /** + * Get the current event generation statistics. + * + * @return the statistics as a JSON string + */ + public String getEventGenerationStats() { + return EventGeneratorEndpoint.getEventGenerationStats(); + } + + /** + * Check if event generation is finished. + * + * @return true if event generation is finished + */ + public boolean isFinished() { + return EventGeneratorEndpoint.isFinished(); + } + + /** + * Tear down the event generator. + */ + public void tearDown() { + LOGGER.info("Event generator shutting down"); + + eventGeneratorServer.shutdown(); + + if (parameters.getOutFile() != null) { + try { + TextFileUtils.putStringAsTextFile(getEventGenerationStats(), parameters.getOutFile()); + } + catch (IOException ioe) { + LOGGER.warn("could not output statistics to file \"" + parameters.getOutFile() + "\"", ioe); + } + } + + LOGGER.info("Event generator shut down"); + } + + /** + * Get the base URI for the server. + * + * @return the base URI + */ + private URI getBaseUri() { + String baseUri = "http://" + parameters.getHost() + ':' + parameters.getPort() + '/' + "/EventGenerator"; + return URI.create(baseUri); + } + + /** + * This class is a shutdown hook for the Apex editor command. + */ + private class EventGeneratorShutdownHook implements Runnable { + /* + * (non-Javadoc) + * + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + tearDown(); + } + } + + /** + * The main method. + * + * @param args the arguments + * @throws Exception the exception + */ + public static void main(final String[] args) { + LOGGER.info("Starting event generator with arguments: " + Arrays.toString(args)); + + EventGeneratorParameterHandler parameterHandler = new EventGeneratorParameterHandler(); + + EventGeneratorParameters parameters = null; + + try { + parameters = parameterHandler.parse(args); + } + catch (ParseException pe) { + LOGGER.trace("Event generator start exception", pe); + LOGGER.info("Start of event generator failed: {}", pe.getMessage()); + return; + } + + // Null parameters means we print help + if (parameters == null) { + LOGGER.info(parameterHandler.getHelp(EventGenerator.class.getName())); + return; + } + + // Start the event generator + EventGenerator eventGenerator = new EventGenerator(parameters); + LOGGER.info("Event generator started"); + + // Wait for event generation to finish + while (!eventGenerator.isFinished()) { + ThreadUtilities.sleep(200); + } + + + // Shut down the server + eventGenerator.tearDown(); + + LOGGER.info("Event generator statistics\n" + eventGenerator.getEventGenerationStats()); + + LOGGER.info("Event generator finished"); + } +} diff --git a/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/EventGeneratorEndpoint.java b/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/EventGeneratorEndpoint.java new file mode 100644 index 000000000..ed624fb83 --- /dev/null +++ b/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/EventGeneratorEndpoint.java @@ -0,0 +1,177 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2018 Ericsson. All rights reserved. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.apex.testsuites.performance.benchmark.eventgenerator; + +import com.google.gson.Gson; + +import java.util.concurrent.ConcurrentHashMap; + +import javax.inject.Inject; +import javax.inject.Provider; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.core.Response; + +import org.glassfish.grizzly.http.server.Request; +import org.onap.policy.apex.core.infrastructure.threading.ThreadUtilities; +import org.onap.policy.apex.testsuites.performance.benchmark.eventgenerator.events.OutputEvent; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +/** + * This class is the REST end point for event simulator REST calls. + */ +@Path("/") +public class EventGeneratorEndpoint { + // Get a reference to the logger + private static final XLogger LOGGER = XLoggerFactory.getXLogger(EventGeneratorEndpoint.class); + + // Parameters for event generation + private static EventGeneratorParameters parameters = new EventGeneratorParameters(); + + // The map of event batches sent in the test + private static ConcurrentHashMap<Integer, EventBatch> batchMap = new ConcurrentHashMap<>(); + + // Flag indicating that event processing has finished + private static boolean finished = false; + + // The current HTTP request + private final Provider<Request> httpRequest; + + /** + * Inject the HTTP request with a constructor. + * @param httpRequest the current request + */ + @Inject + public EventGeneratorEndpoint(final Provider<Request> httpRequest) { + this.httpRequest = httpRequest; + } + + /** + * Set the parameters for the end point. + * + * @param incomingParameters the new parameters + */ + public static void setParameters(EventGeneratorParameters incomingParameters) { + synchronized (parameters) { + parameters = incomingParameters; + } + } + + /** + * Get event generator statistics. + * + * @return the response + */ + @Path("/Stats") + @GET + public Response serviceGetStats() { + return Response.status(200).entity(new EventGeneratorStats(batchMap).getStatsAsJsonString()).build(); + } + + /** + * Generate a single event. + * + * @return the event + */ + @Path("/GetEvents") + @GET + public Response getEvents() { + ThreadUtilities.sleep(parameters.getDelayBetweenBatches()); + + // Check if event generation is finished + if (isFinished()) { + return Response.status(204).build(); + } + + // A batch count of 0 means to continue to handle events for ever + if (parameters.getBatchCount() > 0 && batchMap.size() >= parameters.getBatchCount()) { + setFinished(true); + return Response.status(204).build(); + } + + EventBatch batch = new EventBatch(parameters.getBatchSize(), getApexClient()); + batchMap.put(batch.getBatchNumber(), batch); + + return Response.status(200).entity(batch.getBatchAsJsonString()).build(); + } + + /** + * Get a single response to an event. + * + * @param jsonString the json string + * @return the response + */ + @Path("/PostEvent") + @POST + public Response postEventResponse(final String jsonString) { + final OutputEvent outputEvent = new Gson().fromJson(jsonString, OutputEvent.class); + + EventBatch batch = batchMap.get(outputEvent.findBatchNumber()); + + if (batch == null) { + String errorMessage = "no input event found for received output event " + outputEvent; + LOGGER.warn(errorMessage); + return Response.status(409).build(); + } + + batch.handleResponse(outputEvent); + return Response.status(200).build(); + } + + /** + * Get the name, address, and port of the Apex client getting the events. + * + * @return the Apex client + */ + private String getApexClient() { + return httpRequest.get().getRemoteHost() + '(' + httpRequest.get().getRemoteAddr() + "):" + + httpRequest.get().getRemotePort(); + } + + /** + * Get event generation statistics. + * @return the statistics on event generation + */ + protected static String getEventGenerationStats() { + return new EventGeneratorStats(batchMap).getStatsAsJsonString(); + } + + /** + * Clear event generation statistics. + */ + protected static void clearEventGenerationStats() { + batchMap.clear(); + } + + /** + * Check if event generation has finished. + * @return true if event generation has finished + */ + protected static boolean isFinished() { + return finished; + } + + protected static void setFinished(boolean finished) { + EventGeneratorEndpoint.finished = finished; + } +} diff --git a/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/EventGeneratorParameterHandler.java b/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/EventGeneratorParameterHandler.java new file mode 100644 index 000000000..4cc94712f --- /dev/null +++ b/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/EventGeneratorParameterHandler.java @@ -0,0 +1,214 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2018 Ericsson. All rights reserved. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.apex.testsuites.performance.benchmark.eventgenerator; + +import com.google.gson.Gson; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Arrays; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.onap.policy.apex.model.utilities.TextFileUtils; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +/** + * This class reads and handles command line parameters to the event generator. + */ +public class EventGeneratorParameterHandler { + // Get a reference to the logger + private static final XLogger LOGGER = XLoggerFactory.getXLogger(EventGeneratorParameterHandler.class); + + private static final String CONFIGURATION_FILE = "configuration-file"; + private static final String PORT = "port"; + private static final String HOST = "host"; + private static final String HELP = "help"; + private static final String BATCH_SIZE = "batch-size"; + private static final String BATCH_COUNT = "batch-count"; + private static final String BATCH_DELAY = "delay-between-batches"; + private static final String OUTPUT_FILE = "output-file"; + + private static final int MAX_HELP_LINE_LENGTH = 120; + + // Apache Commons CLI options + private final Options options; + + /** + * Construct the options for the CLI editor. + */ + public EventGeneratorParameterHandler() { + options = new Options(); + options.addOption(Option.builder("h").longOpt(HELP).desc("outputs the usage of this command").required(false) + .type(Boolean.class).build()); + options.addOption(Option.builder("H").longOpt(HOST) + .desc("the host name on which to start the event generation server, defaults to \"localhost\"") + .hasArg().argName(HOST).required(false).type(String.class).build()); + options.addOption(Option.builder("p").longOpt(PORT) + .desc("the port on which to start the event generation server, defaults to 42339").hasArg() + .argName(PORT).required(false).type(Number.class).build()); + options.addOption(Option.builder("c").longOpt(CONFIGURATION_FILE) + .desc("name of a file containing the parameters for the event generations server, " + + "this option must appear on its own") + .hasArg().argName(CONFIGURATION_FILE).required(false).type(String.class).build()); + options.addOption(Option.builder("o").longOpt(OUTPUT_FILE) + .desc("path and name of a file to which output will be written," + + " if not specified no output is written") + .hasArg().argName(OUTPUT_FILE).required(false).type(String.class).build()); + options.addOption(Option.builder("bc").longOpt(BATCH_COUNT) + .desc("the number of batches of events to send, must be 0 or more, " + + "0 means send event batches forever, defaults to 1") + .hasArg().argName(BATCH_COUNT).required(false).type(Number.class).build()); + options.addOption(Option.builder("bs").longOpt(BATCH_SIZE) + .desc("the number of events to send in an event batch, must be 1 or more, defaults to 1") + .hasArg().argName(BATCH_SIZE).required(false).type(Number.class).build()); + options.addOption(Option.builder("bd").longOpt(BATCH_DELAY) + .desc("the delay in milliseconds between event batches, must be zero or more, " + + "defaults to 10,000 (10 seconds)") + .hasArg().argName(BATCH_DELAY).required(false).type(Number.class).build()); + } + + /** + * Parse the command line options. + * + * @param args The arguments + * @return the CLI parameters + * @throws ParseException on parse errors + */ + public EventGeneratorParameters parse(final String[] args) throws ParseException { + CommandLine commandLine = new DefaultParser().parse(options, args); + final String[] remainingArgs = commandLine.getArgs(); + + if (remainingArgs.length > 0) { + throw new ParseException("too many command line arguments specified : " + Arrays.toString(remainingArgs)); + } + + if (commandLine.hasOption('h')) { + return null; + } + + EventGeneratorParameters parameters = new EventGeneratorParameters(); + + if (commandLine.hasOption('c')) { + parameters = getParametersFromJsonFile(commandLine.getOptionValue(CONFIGURATION_FILE)); + } + + parseFlags(commandLine, parameters); + + if (commandLine.hasOption('o')) { + parameters.setOutFile(commandLine.getOptionValue(OUTPUT_FILE)); + } + + if (!parameters.isValid()) { + throw new ParseException("specified parameters are not valid: " + parameters.validate().getResult()); + } + + return parameters; + } + + /** + * Parse the command flags. + * + * @param commandLine the command line to parse + * @param parameters the parameters we are parsing into + * @throws ParseException on parse errors + */ + private void parseFlags(CommandLine commandLine, EventGeneratorParameters parameters) throws ParseException { + if (commandLine.hasOption('H')) { + parameters.setHost(commandLine.getOptionValue(HOST)); + } + + if (commandLine.hasOption('p')) { + parameters.setPort(((Number) commandLine.getParsedOptionValue(PORT)).intValue()); + } + + if (commandLine.hasOption("bc")) { + parameters.setBatchCount(((Number) commandLine.getParsedOptionValue(BATCH_COUNT)).intValue()); + } + + if (commandLine.hasOption("bs")) { + parameters.setBatchSize(((Number) commandLine.getParsedOptionValue(BATCH_SIZE)).intValue()); + } + + if (commandLine.hasOption("bd")) { + parameters.setDelayBetweenBatches(((Number) commandLine.getParsedOptionValue(BATCH_DELAY)).longValue()); + } + } + + /** + * Get the parameters from a JSON file. + * + * @param configurationFile the location of the configuration file + * @return the parameters read from the JSON file + * @throws ParseException on errors reading the parameters + */ + private EventGeneratorParameters getParametersFromJsonFile(String configurationFile) throws ParseException { + String parameterJsonString = null; + + try { + parameterJsonString = TextFileUtils.getTextFileAsString(configurationFile); + } catch (IOException ioe) { + String errorMessage = "Could not read parameters from configuration file \"" + configurationFile + "\": " + + ioe.getMessage(); + LOGGER.warn(errorMessage, ioe); + throw new ParseException(errorMessage); + } + + if (parameterJsonString == null || parameterJsonString.trim().length() == 0) { + String errorMessage = "No parameters found in configuration file \"" + configurationFile + "\""; + LOGGER.warn(errorMessage); + throw new ParseException(errorMessage); + } + + try { + return new Gson().fromJson(parameterJsonString, EventGeneratorParameters.class); + } catch (Exception ge) { + String errorMessage = "Error parsing JSON parameters from configuration file \"" + configurationFile + + "\": " + ge.getMessage(); + LOGGER.warn(errorMessage, ge); + throw new ParseException(errorMessage); + } + } + + /** + * Get help information. + * + * @param mainClassName the main class name for the help output + * @return help string + */ + public String getHelp(final String mainClassName) { + final StringWriter stringWriter = new StringWriter(); + final PrintWriter stringPrintWriter = new PrintWriter(stringWriter); + + final HelpFormatter helpFormatter = new HelpFormatter(); + helpFormatter.printHelp(stringPrintWriter, MAX_HELP_LINE_LENGTH, mainClassName + " [options...] ", "", options, + 0, 0, ""); + + return stringWriter.toString(); + } + +} diff --git a/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/EventGeneratorParameters.java b/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/EventGeneratorParameters.java new file mode 100644 index 000000000..8eb41b4e3 --- /dev/null +++ b/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/EventGeneratorParameters.java @@ -0,0 +1,156 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2018 Ericsson. All rights reserved. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.apex.testsuites.performance.benchmark.eventgenerator; + +import org.onap.policy.common.parameters.GroupValidationResult; +import org.onap.policy.common.parameters.ParameterGroup; +import org.onap.policy.common.parameters.ValidationStatus; + +/** + * This class defines the parameters for event generation. + */ +public class EventGeneratorParameters implements ParameterGroup { + // @formatter:off + private static final String DEFAULT_NAME = EventGeneratorParameters.class.getSimpleName(); + private static final String DEFAULT_HOST = "localhost"; + private static final int DEFAULT_PORT = 32801; + private static final int DEFAULT_BATCH_COUNT = 1; + private static final int DEFAULT_BATCH_SIZE = 1; + private static final long DEFAULT_DELAY_BETWEEN_BATCHES = 2000; + + private String name = DEFAULT_NAME; + private String host = DEFAULT_HOST; + private int port = DEFAULT_PORT; + private int batchCount = DEFAULT_BATCH_COUNT; + private int batchSize = DEFAULT_BATCH_SIZE; + private long delayBetweenBatches = DEFAULT_DELAY_BETWEEN_BATCHES; + private String outFile = null; + // @formatter:on + + /** + * Create default parameters. + */ + public EventGeneratorParameters() { + // Default parameters are generated + } + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public int getBatchCount() { + return batchCount; + } + + public void setBatchCount(int batchCount) { + this.batchCount = batchCount; + } + + public int getBatchSize() { + return batchSize; + } + + public void setBatchSize(int batchSize) { + this.batchSize = batchSize; + } + + public long getDelayBetweenBatches() { + return delayBetweenBatches; + } + + public void setDelayBetweenBatches(long delayBetweenBatches) { + this.delayBetweenBatches = delayBetweenBatches; + } + + public String getOutFile() { + return outFile; + } + + public void setOutFile(String outFile) { + this.outFile = outFile; + } + + /** + * {@inheritDoc} + */ + @Override + public GroupValidationResult validate() { + GroupValidationResult validationResult = new GroupValidationResult(this); + + if (isNullOrBlank(name)) { + validationResult.setResult("name", ValidationStatus.INVALID, "name must be a non-blank string"); + } + + if (isNullOrBlank(host)) { + validationResult.setResult("host", ValidationStatus.INVALID, "host must be a non-blank string"); + } + + if (port < 1024 || port > 65535) { + validationResult.setResult("port", ValidationStatus.INVALID, + "port must be an integer between 1024 and 65535 inclusive"); + } + + if (batchCount < 0) { + validationResult.setResult("batchCount", ValidationStatus.INVALID, + "batchCount must be an integer with a value of zero or more, " + + "zero means generate batches forever"); + } + + if (batchSize < 1) { + validationResult.setResult("batchSize", ValidationStatus.INVALID, + "batchSize must be an integer greater than zero"); + } + + if (delayBetweenBatches < 0) { + validationResult.setResult("batchSize", ValidationStatus.INVALID, + "batchSize must be an integer with a value of zero or more"); + } + + return validationResult; + } + + private boolean isNullOrBlank(final String stringValue) { + return stringValue == null || stringValue.trim().length() == 0; + } + +} diff --git a/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/EventGeneratorStats.java b/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/EventGeneratorStats.java new file mode 100644 index 000000000..f3a5372bf --- /dev/null +++ b/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/EventGeneratorStats.java @@ -0,0 +1,59 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2018 Ericsson. All rights reserved. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.apex.testsuites.performance.benchmark.eventgenerator; + +import com.google.gson.GsonBuilder; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * This class creates statistics for the event generator's current status. + */ +public class EventGeneratorStats { + private final EventBatchStats totalStats; + private final List<EventBatchStats> batchStatsList = new ArrayList<>(); + + /** + * Create the statistics using the current batch map. + * @param batchMap the batch map to use + */ + public EventGeneratorStats(final Map<Integer, EventBatch> batchMap) { + for (EventBatch eventBatch: batchMap.values()) { + batchStatsList.add(new EventBatchStats(eventBatch)); + } + + totalStats = new EventBatchStats(batchStatsList); + } + + /** + * Get the batch statistics as a JSON string. + * @return the statistics as a JSON string + */ + public String getStatsAsJsonString() { + return new GsonBuilder().setPrettyPrinting().create().toJson(this); + } + + public EventBatchStats getTotalStats() { + return totalStats; + } +} diff --git a/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/events/InputEvent.java b/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/events/InputEvent.java new file mode 100644 index 000000000..cf70ea2f1 --- /dev/null +++ b/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/events/InputEvent.java @@ -0,0 +1,142 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2018 Ericsson. All rights reserved. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.apex.testsuites.performance.benchmark.eventgenerator.events; + +import com.google.gson.GsonBuilder; +import com.google.gson.annotations.SerializedName; + +import java.util.Random; + +/** + * This class is a POJO representing an input event for load testing. + */ +public class InputEvent { + private String nameSpace = "org.onap.policy.apex.sample.events"; + private String name; + private String version = "0.0.1"; + private String source = "EventGenerator"; + private String target = "Apex"; + + @SerializedName(value = "TestSlogan") + private String testSlogan; + + @SerializedName(value = "TestMatchCase") + private int testMatchCase; + + @SerializedName(value = "TestTimestamp") + private long testTimestamp = System.nanoTime(); + + @SerializedName(value = "TestTemperature") + private double testTemperature; + + /** + * Constructor, assign default values to fields. + */ + public InputEvent() { + final Random rand = new Random(); + testMatchCase = rand.nextInt(4); + name = "Event0" + rand.nextInt(2) + "00"; + testTemperature = rand.nextDouble() * 1000; + } + + public String getNameSpace() { + return nameSpace; + + } + + public void setNameSpace(String nameSpace) { + this.nameSpace = nameSpace; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } + + public String getTarget() { + return target; + } + + + public void setTarget(String target) { + this.target = target; + } + + public String getTestSlogan() { + return testSlogan; + } + + public void setTestSlogan(String testSlogan) { + this.testSlogan = testSlogan; + } + + public int getTestMatchCase() { + return testMatchCase; + } + + public void setTestMatchCase(int testMatchCase) { + this.testMatchCase = testMatchCase; + } + + public long getTestTimestamp() { + return testTimestamp; + } + + public void setTestTimestamp(long testTimestamp) { + this.testTimestamp = testTimestamp; + } + + public double getTestTemperature() { + return testTemperature; + } + + public void setTestTemperature(double testTemperature) { + this.testTemperature = testTemperature; + } + + /** + * Get a JSON representation of the input event. + * + * @return the event in JSON format + */ + public String asJson() { + return new GsonBuilder().setPrettyPrinting().create().toJson(this); + } +} diff --git a/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/events/OutputEvent.java b/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/events/OutputEvent.java new file mode 100644 index 000000000..0350b3f2e --- /dev/null +++ b/testsuites/performance/performance-benchmark-test/src/main/java/org/onap/policy/apex/testsuites/performance/benchmark/eventgenerator/events/OutputEvent.java @@ -0,0 +1,135 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2018 Ericsson. All rights reserved. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.apex.testsuites.performance.benchmark.eventgenerator.events; + +import com.google.gson.annotations.SerializedName; + +/** + * This class is a POJO representing an output event for load testing. + */ +public class OutputEvent extends InputEvent { + @SerializedName(value = "TestMatchCaseSelected") + private int testMatchCaseSelected; + + @SerializedName(value = "TestMatchStateTime") + private long testMatchStateTime; + + @SerializedName(value = "TestEstablishCaseSelected") + private int testEstablishCaseSelected; + + @SerializedName(value = "TestEstablishStateTime") + private long testEstablishStateTime; + + @SerializedName(value = "TestDecideCaseSelected") + private int testDecideCaseSelected; + + @SerializedName(value = "TestDecideStateTime") + private long testDecideStateTime; + + @SerializedName(value = "TestActCaseSelected") + private int testActCaseSelected; + + @SerializedName(value = "TestActStateTime") + private long testActStateTime; + + private long testReceviedTimestamp = System.nanoTime(); + + public int getTestMatchCaseSelected() { + return testMatchCaseSelected; + } + + public void setTestMatchCaseSelected(int testMatchCaseSelected) { + this.testMatchCaseSelected = testMatchCaseSelected; + } + + public long getTestMatchStateTime() { + return testMatchStateTime; + } + + public void setTestMatchStateTime(long testMatchStateTime) { + this.testMatchStateTime = testMatchStateTime; + } + + public int getTestEstablishCaseSelected() { + return testEstablishCaseSelected; + } + + public void setTestEstablishCaseSelected(int testEstablishCaseSelected) { + this.testEstablishCaseSelected = testEstablishCaseSelected; + } + + public long getTestEstablishStateTime() { + return testEstablishStateTime; + } + + public void setTestEstablishStateTime(long testEstablishStateTime) { + this.testEstablishStateTime = testEstablishStateTime; + } + + public int getTestDecideCaseSelected() { + return testDecideCaseSelected; + } + + public void setTestDecideCaseSelected(int testDecideCaseSelected) { + this.testDecideCaseSelected = testDecideCaseSelected; + } + + public long getTestDecideStateTime() { + return testDecideStateTime; + } + + public void setTestDecideStateTime(long testDecideStateTime) { + this.testDecideStateTime = testDecideStateTime; + } + + public int getTestActCaseSelected() { + return testActCaseSelected; + } + + public void setTestActCaseSelected(int testActCaseSelected) { + this.testActCaseSelected = testActCaseSelected; + } + + public long getTestActStateTime() { + return testActStateTime; + } + + public void setTestActStateTime(long testActStateTime) { + this.testActStateTime = testActStateTime; + } + + public long getTestReceviedTimestamp() { + return testReceviedTimestamp; + } + + public void setTestReceviedTimestamp(long testReceviedTimestamp) { + this.testReceviedTimestamp = testReceviedTimestamp; + } + + public int findBatchNumber() { + return Integer.valueOf(getTestSlogan().substring(0, getTestSlogan().indexOf('-'))); + } + + public int findEventNumber() { + return Integer.valueOf( + getTestSlogan().substring(getTestSlogan().indexOf('-') + 1, getTestSlogan().indexOf(':'))); + } +} |