diff options
Diffstat (limited to 'aai-resources')
-rw-r--r-- | aai-resources/src/test/java/org/onap/aai/it/performance/K6ReadTest.java | 183 | ||||
-rw-r--r-- | aai-resources/src/test/java/org/onap/aai/it/performance/K6WriteTest.java (renamed from aai-resources/src/test/java/org/onap/aai/it/performance/K6PerformanceTest.java) | 58 | ||||
-rw-r--r-- | aai-resources/src/test/resources/k6/readWithoutRelations.js (renamed from aai-resources/src/test/resources/k6/test.js) | 0 | ||||
-rw-r--r-- | aai-resources/src/test/resources/k6/writeWithoutRelations.js | 111 |
4 files changed, 326 insertions, 26 deletions
diff --git a/aai-resources/src/test/java/org/onap/aai/it/performance/K6ReadTest.java b/aai-resources/src/test/java/org/onap/aai/it/performance/K6ReadTest.java new file mode 100644 index 0000000..0dee841 --- /dev/null +++ b/aai-resources/src/test/java/org/onap/aai/it/performance/K6ReadTest.java @@ -0,0 +1,183 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2024 Deutsche Telekom. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.aai.it.performance; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; + +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.janusgraph.core.JanusGraph; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.onap.aai.db.props.AAIProperties; +import org.onap.aai.dbmap.AAIGraph; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration; +import org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.web.server.LocalServerPort; +import org.testcontainers.containers.output.WaitingConsumer; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.k6.K6Container; +import org.testcontainers.utility.MountableFile; + +import lombok.SneakyThrows; + +@Testcontainers +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@EnableAutoConfiguration(exclude={CassandraDataAutoConfiguration.class, CassandraAutoConfiguration.class}) // there is no running cassandra instance for the test +public class K6ReadTest { + + private static final Logger logger = LoggerFactory.getLogger(K6ReadTest.class); + private static final long nPservers = 10; + + @LocalServerPort + private int port; + + @BeforeEach + public void setup() { + if (!AAIGraph.isInit()) { + AAIGraph.getInstance(); + + long startTime = System.currentTimeMillis(); + logger.info("Creating pserver nodes"); + loadPerformanceTestData(); + long endTime = System.currentTimeMillis(); + logger.info("Created pserver nodes in {} seconds", (endTime - startTime) / 1000); + } + } + + @AfterAll + public static void cleanup() { + JanusGraph graph = AAIGraph.getInstance().getGraph(); + graph.traversal().V().has("aai-node-type", "pserver").drop().iterate(); + graph.tx().commit(); + } + + @Test + public void readWithoutRelationsTest() throws Exception { + int testDuration = 5; + + K6Container container = new K6Container("grafana/k6:0.49.0") + .withNetworkMode("host") + .withAccessToHost(true) + .withTestScript(MountableFile.forClasspathResource("k6/readWithoutRelations.js")) + .withScriptVar("API_PORT", String.valueOf(port)) + .withScriptVar("API_VERSION", "v29") + .withScriptVar("DURATION_SECONDS", String.valueOf(testDuration)) + .withScriptVar("N_PSERVERS", String.valueOf(nPservers)) + .withCmdOptions("--quiet", "--no-usage-report"); + container.start(); + + WaitingConsumer consumer = new WaitingConsumer(); + container.followOutput(consumer); + + // Wait for test script results to be collected + try { + consumer.waitUntil( + frame -> { + return frame.getUtf8String().contains("iteration_duration"); + }, + testDuration + 10, + TimeUnit.SECONDS); + } catch (Exception e) { + logger.error(container.getLogs()); + } + + logger.debug(container.getLogs()); + assertThat(container.getLogs(), containsString("✓ status was 200")); + assertThat(container.getLogs(), containsString("✓ returned correct number of results")); + assertThat(container.getLogs(), containsString("✓ http_req_duration")); + assertThat(container.getLogs(), containsString("✓ http_req_failed")); + container.stop(); + + } + + @Test + @Disabled + public void writeWithoutRelations() throws Exception { + int testDuration = 5; + + K6Container container = new K6Container("grafana/k6:0.49.0") + .withNetworkMode("host") + .withAccessToHost(true) + .withTestScript(MountableFile.forClasspathResource("k6/writeWithoutRelations.js")) + .withScriptVar("API_PORT", String.valueOf(port)) + .withScriptVar("API_VERSION", "v29") + .withScriptVar("DURATION_SECONDS", String.valueOf(testDuration)) + .withScriptVar("N_PSERVERS", String.valueOf(nPservers)) + .withCmdOptions("--quiet", "--no-usage-report"); + container.start(); + + WaitingConsumer consumer = new WaitingConsumer(); + container.followOutput(consumer); + + // Wait for test script results to be collected + try { + consumer.waitUntil( + frame -> { + return frame.getUtf8String().contains("iteration_duration"); + }, + testDuration + 10, + TimeUnit.SECONDS); + } catch (Exception e) { + // log the container stdout in case of failure in the test script + logger.error(container.getLogs()); + } + + String report = container.getLogs().substring(container.getLogs().indexOf("✓ status was 201")); + logger.info(report); + assertThat(report, containsString("✓ status was 201")); + assertThat(report, containsString("✓ http_req_duration")); + assertThat(report, containsString("✓ http_req_failed")); + container.stop(); + } + + @SneakyThrows + public static void loadPerformanceTestData() { + JanusGraph graph = AAIGraph.getInstance().getGraph(); + GraphTraversalSource g = graph.traversal(); + long n = nPservers; + for (long i = 0; i < n; i++) { + createPServer(g, i); + } + graph.tx().commit(); + } + + private static void createPServer(GraphTraversalSource g, long i) { + String hostname = "hostname" + i; + String uri = "/cloud-infrastructure/pservers/pserver/" + hostname; + g.addV() + .property("aai-node-type", "pserver") + .property("hostname", hostname) + .property("resource-version", UUID.randomUUID().toString()) + .property(AAIProperties.AAI_URI, uri) + .next(); + } +} diff --git a/aai-resources/src/test/java/org/onap/aai/it/performance/K6PerformanceTest.java b/aai-resources/src/test/java/org/onap/aai/it/performance/K6WriteTest.java index f7d1cc6..b4a495c 100644 --- a/aai-resources/src/test/java/org/onap/aai/it/performance/K6PerformanceTest.java +++ b/aai-resources/src/test/java/org/onap/aai/it/performance/K6WriteTest.java @@ -30,6 +30,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSo import org.janusgraph.core.JanusGraph; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.onap.aai.ResourcesApp; import org.onap.aai.db.props.AAIProperties; @@ -52,9 +53,9 @@ import lombok.SneakyThrows; @Testcontainers @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) @EnableAutoConfiguration(exclude={CassandraDataAutoConfiguration.class, CassandraAutoConfiguration.class}) // there is no running cassandra instance for the test -public class K6PerformanceTest { +public class K6WriteTest { - private static final Logger logger = LoggerFactory.getLogger(ResourcesApp.class.getName()); + private static final Logger logger = LoggerFactory.getLogger(K6ReadTest.class); private static final long nPservers = 10; @LocalServerPort @@ -81,38 +82,43 @@ public class K6PerformanceTest { } @Test - public void k6StandardTest() throws Exception { + @Disabled + public void writeWithoutRelations() throws Exception { int testDuration = 5; - try ( - K6Container container = new K6Container("grafana/k6:0.49.0") - .withNetworkMode("host") - .withAccessToHost(true) - .withTestScript(MountableFile.forClasspathResource("k6/test.js")) - .withScriptVar("API_PORT", String.valueOf(port)) - .withScriptVar("API_VERSION", "v29") - .withScriptVar("DURATION_SECONDS", String.valueOf(testDuration)) - .withScriptVar("N_PSERVERS", String.valueOf(nPservers)) - .withCmdOptions("--quiet", "--no-usage-report");) { - container.start(); - - WaitingConsumer consumer = new WaitingConsumer(); - container.followOutput(consumer); - - // Wait for test script results to be collected + K6Container container = new K6Container("grafana/k6:0.49.0") + .withNetworkMode("host") + .withAccessToHost(true) + .withTestScript(MountableFile.forClasspathResource("k6/writeWithoutRelations.js")) + .withScriptVar("API_PORT", String.valueOf(port)) + .withScriptVar("API_VERSION", "v29") + .withScriptVar("DURATION_SECONDS", String.valueOf(testDuration)) + .withScriptVar("N_PSERVERS", String.valueOf(nPservers)) + .withCmdOptions("--quiet", "--no-usage-report"); + container.start(); + + WaitingConsumer consumer = new WaitingConsumer(); + container.followOutput(consumer); + + // Wait for test script results to be collected + try { consumer.waitUntil( frame -> { return frame.getUtf8String().contains("iteration_duration"); }, - testDuration + 30, + testDuration + 10, TimeUnit.SECONDS); - - logger.debug(container.getLogs()); - assertThat(container.getLogs(), containsString("✓ status was 200")); - assertThat(container.getLogs(), containsString("✓ returned correct number of results")); - assertThat(container.getLogs(), containsString("✓ http_req_duration")); - assertThat(container.getLogs(), containsString("✓ http_req_failed")); + } catch (Exception e) { + // log the container stdout in case of failure in the test script + logger.error(container.getLogs()); } + + String report = container.getLogs().substring(container.getLogs().indexOf("✓ status was 201")); + logger.info(report); + assertThat(report, containsString("✓ status was 201")); + assertThat(report, containsString("✓ http_req_duration")); + assertThat(report, containsString("✓ http_req_failed")); + container.stop(); } @SneakyThrows diff --git a/aai-resources/src/test/resources/k6/test.js b/aai-resources/src/test/resources/k6/readWithoutRelations.js index 8e55214..8e55214 100644 --- a/aai-resources/src/test/resources/k6/test.js +++ b/aai-resources/src/test/resources/k6/readWithoutRelations.js diff --git a/aai-resources/src/test/resources/k6/writeWithoutRelations.js b/aai-resources/src/test/resources/k6/writeWithoutRelations.js new file mode 100644 index 0000000..bb4b839 --- /dev/null +++ b/aai-resources/src/test/resources/k6/writeWithoutRelations.js @@ -0,0 +1,111 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2024 Deutsche Telekom. 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. + * ============LICENSE_END========================================================= + */ +import http from "k6/http"; +import { check } from "k6"; +import { randomIntBetween } from 'https://jslib.k6.io/k6-utils/1.3.0/index.js'; + +export const options = { + vus: 3, + duration: `${__ENV.DURATION_SECONDS}s`, + thresholds: { + http_req_failed: ["rate<0.01"], // http errors should be less than 1% + http_req_duration: [ + "p(99)<3000", + "p(90)<2000", + "avg<70", + "med<70", + "min<1000", + ], + }, + insecureSkipTLSVerify: true, +}; + +function generatePServer(someInt) { + return JSON.stringify({ + 'hostname': someInt, + 'ptnii-equip-name': `example-ptnii-equip-name-val-${someInt}`, + 'number-of-cpus': someInt, + 'disk-in-gigabytes': someInt, + 'ram-in-megabytes': someInt, + 'equip-type': `example-equip-type-val-${someInt}`, + 'equip-vendor': `example-equip-vendor-val-${someInt}`, + 'equip-model': `example-equip-model-val-${someInt}`, + 'fqdn': `example-fqdn-val-${someInt}`, + 'pserver-selflink': `example-pserver-selflink-val-${someInt}`, + 'ipv4-oam-address': `example-ipv4-oam-address-val-${someInt}`, + 'serial-number': `example-serial-number-val-${someInt}`, + 'ipaddress-v4-loopback-0': `example-ipaddress-v4-loopback0-val-${someInt}`, + 'ipaddress-v6-loopback-0': `example-ipaddress-v6-loopback0-val-${someInt}`, + 'ipaddress-v4-aim': `example-ipaddress-v4-aim-val-${someInt}`, + 'ipaddress-v6-aim': `example-ipaddress-v6-aim-val-${someInt}`, + 'ipaddress-v6-oam': `example-ipaddress-v6-oam-val-${someInt}`, + 'inv-status': `example-inv-status-val-${someInt}`, + 'pserver-id': `example-pserver-id-val-${someInt}`, + 'internet-topology': `example-internet-topology-val-${someInt}`, + 'in-maint': true, + 'pserver-name2': `example-pserver-name2-val-${someInt}`, + 'purpose': `example-purpose-val-${someInt}`, + 'prov-status': `example-prov-status-val-${someInt}`, + 'management-option': `example-management-option-val-${someInt}`, + 'host-profile': `example-host-profile-val-${someInt}` + }); +} + +const baseUrl = `http://localhost:${__ENV.API_PORT}/aai/${__ENV.API_VERSION}`; +const path = `/cloud-infrastructure/pservers/pserver`; +const url = baseUrl + path; +const encodedCredentials = 'QUFJOkFBSQ=='; +const httpOpts = { + headers: { + Accept: "application/json", + Authorization: `Basic ${encodedCredentials}`, + "X-FromAppId": "k6", + "X-TransactionId": "someTransaction", + }, +}; + +export function setup() { + // Perform a warmup with 100 requests + for (let i = 0; i < 100; i++) { + const someInt = randomIntBetween(10000, 1000000); + const payload = generatePServer(someInt); + const pserverUrl = url + `/${someInt}` + const res = http.put(pserverUrl, payload, httpOpts); + + if (res.status != 201) { + console.error(res); + } + } +} + +export default function () { + const someInt = randomIntBetween(10000, 1000000); + const pserverUrl = url + `/${someInt}` + const payload = generatePServer(); + const res = http.put(pserverUrl, payload, httpOpts); + + if (res.status != 201) { + console.error(res); + } + + check(res, { + "status was 201": (r) => r.status == 201, + }); +} |