diff options
author | 2018-11-20 15:59:50 +0000 | |
---|---|---|
committer | 2018-11-20 16:56:18 +0000 | |
commit | 1f7c861c65aa8689f11196581b8ca5e79d7e4e26 (patch) | |
tree | b48c3dc3bec93dd5e52c9a45cb84b88c61d1843f /testsuites/performance/performance-benchmark-test/src/main/java/org | |
parent | e8b197315437cac84872752e2ea090d8fb233941 (diff) |
Refactor Apex benchmark tests
Apex performance tests are refactored and reorganised
to be all in the one place and to be coherent.
A test REST server that can generate events in batches
is introduced and this can be configured to respond to REST
calls from Apex with various batch sizes that can be varied
for performance tests.
Actual performance tests are added for the various executors
in Apex.
Change-Id: I692b41e1e4451e821a618c64dead5cc8e9685d08
Issue-ID: POLICY-812
Signed-off-by: liamfallon <liam.fallon@ericsson.com>
Diffstat (limited to 'testsuites/performance/performance-benchmark-test/src/main/java/org')
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(':'))); + } +} |