Add passthrough read operation (synchronous) to DMI stub 67/137367/2
authordanielhanrahan <daniel.hanrahan@est.tech>
Wed, 29 Nov 2023 18:51:16 +0000 (18:51 +0000)
committerDaniel Hanrahan <daniel.hanrahan@est.tech>
Mon, 26 Feb 2024 10:38:57 +0000 (10:38 +0000)
- Implement DMI stub endpoint needed for for NCMP passthrough data
  operation, returning a fixed JSON response and adding delay.
    POST /dmi/v1/ch/{cmHandleId}/data/ds/{datastoreName}
- Add script to measure average overhead for many requests

Test results indicate the NCMP adds up to 150ms per request.

Issue-ID: CPS-2099
Signed-off-by: danielhanrahan <daniel.hanrahan@est.tech>
Change-Id: I2aba8285f8a52f3570fc1699a8687caffa9d9c77

dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/DmiRestStubController.java
test-tools/perf-test-ncmp-passthrough-read.sh [new file with mode: 0755]

index d77cbcc..1819dcc 100644 (file)
@@ -117,6 +117,30 @@ public class DmiRestStubController {
         return ResponseEntity.ok(moduleResourcesResponseContent);
     }
 
+    /**
+     * Get resource data from passthrough operational or running for a cm handle.
+     *
+     * @param cmHandleId              The identifier for a network function, network element, subnetwork,
+     *                                or any other cm object by managed Network CM Proxy
+     * @param datastoreName           datastore name
+     * @param resourceIdentifier      resource identifier
+     * @param options                 options
+     * @param topic                   client given topic name
+     * @return (@ code ResponseEntity) response entity
+     */
+    @PostMapping("/v1/ch/{cmHandleId}/data/ds/{datastoreName}")
+    public ResponseEntity<String> getResourceDataForCmHandle(
+            @PathVariable("cmHandleId") final String cmHandleId,
+            @PathVariable("datastoreName") final String datastoreName,
+            @RequestParam(value = "resourceIdentifier") final String resourceIdentifier,
+            @RequestParam(value = "options", required = false) final String options,
+            @RequestParam(value = "topic", required = false) final String topic) {
+        delay(dataForCmHandleDelayMs);
+        final String sampleJson = ResourceFileReaderUtil.getResourceFileContent(applicationContext.getResource(
+                ResourceLoader.CLASSPATH_URL_PREFIX + "data/operational/ietf-network-topology-sample-rfc8345.json"));
+        return ResponseEntity.ok(sampleJson);
+    }
+
     /**
      * This method is not implemented for ONAP DMI plugin.
      *
@@ -126,12 +150,10 @@ public class DmiRestStubController {
      * @return (@ code ResponseEntity) response entity
      */
     @PostMapping("/v1/data")
-    public ResponseEntity<Void> getResourceDataForCmHandleDataOperation(@RequestParam(value = "topic")
-                                                                            final String topic,
-                                                                        @RequestParam(value = "requestId")
-                                                                        final String requestId,
-                                                                        @RequestBody final DmiDataOperationRequest
-                                                                                    dmiDataOperationRequest) {
+    public ResponseEntity<Void> getResourceDataForCmHandleDataOperation(
+            @RequestParam(value = "topic") final String topic,
+            @RequestParam(value = "requestId") final String requestId,
+            @RequestBody final DmiDataOperationRequest dmiDataOperationRequest) {
         delay(dataForCmHandleDelayMs);
         try {
             log.info("Request received from the NCMP to DMI Plugin: {}",
diff --git a/test-tools/perf-test-ncmp-passthrough-read.sh b/test-tools/perf-test-ncmp-passthrough-read.sh
new file mode 100755 (executable)
index 0000000..21b031c
--- /dev/null
@@ -0,0 +1,90 @@
+#!/bin/bash
+#
+# Copyright 2024 Nordix Foundation.
+#
+# 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.
+#
+
+# This script measures the performance of NCMP data passthrough operations:
+# NCMP endpoint tested: /ncmp/v1/ch/{cmHandleId}/data/ds/{datastoreName}
+
+set -o errexit  # Exit on most errors
+set -o nounset  # Disallow expansion of unset variables
+set -o pipefail # Use last non-zero exit code in a pipeline
+#set -o xtrace   # Uncomment for debugging
+
+############################
+# Configuration parameters #
+############################
+CPS_HOST=localhost
+CPS_PORT=8883
+CPS_USERNAME=cpsuser
+CPS_PASSWORD=cpsr0cks!
+PARALLEL_REQUESTS=12
+WARMUP_REQUESTS=600
+MEASUREMENT_REQUESTS=240
+
+SCRIPT_DIR=$(dirname -- "${BASH_SOURCE[0]}")
+# Read DMI delay from docker-compose.yml
+DMI_DATA_DELAY=$(grep 'DATA_FOR_CM_HANDLE_DELAY_MS:' "$SCRIPT_DIR"/../docker-compose/docker-compose.yml | grep -oE '[0-9]+')
+
+function cmHandleExists() {
+  local cmHandleId=$1
+  curl --silent --fail --output /dev/null --user "$CPS_USERNAME:$CPS_PASSWORD" --basic "http://$CPS_HOST:$CPS_PORT/ncmp/v1/ch/$cmHandleId"
+}
+
+function failIfCmHandlesNotFound() {
+  # Just check to see if last needed CM-handle exists
+  local MAX_NEEDED_CM_HANDLES=$((WARMUP_REQUESTS > MEASUREMENT_REQUESTS ? WARMUP_REQUESTS : MEASUREMENT_REQUESTS))
+  local cmHandleId="ch-$MAX_NEEDED_CM_HANDLES"
+  if ! cmHandleExists "$cmHandleId"; then
+    echo "ERROR: CM-handles not registered ($cmHandleId not found)" >&2
+    echo "Note: this test assumes CM-handles have IDs ch-1, ch-2... ch-$MAX_NEEDED_CM_HANDLES" >&2
+    exit 1
+  fi
+}
+
+function warnIfLessThan20kCmHandlesFound() {
+  local cmHandleId='ch-20000'
+  if ! cmHandleExists "$cmHandleId"; then
+    echo "WARNING: testing with less than 20,000 CM-handles is not recommended ($cmHandleId not found)" >&2
+  fi
+}
+
+function measureAverageResponseTimeInMillis() {
+  local totalRequests=$1
+  curl --show-error --fail --fail-early \
+    --output /dev/null --write-out '%{time_total}\n' \
+    --parallel --parallel-max $PARALLEL_REQUESTS --parallel-immediate \
+    --user "$CPS_USERNAME:$CPS_PASSWORD" --basic \
+    --request POST "http://$CPS_HOST:$CPS_PORT/ncmp/v1/ch/ch-[1-$totalRequests]/data/ds/ncmp-datastore%3Apassthrough-operational?resourceIdentifier=x&include-descendants=true" |
+    awk '{ sum += $1; n++ } END { if (n > 0) print (sum / n) * 1000; }'
+}
+
+# Sanity checks
+failIfCmHandlesNotFound
+warnIfLessThan20kCmHandlesFound
+
+# Do JVM warmup
+echo "Warming up ($WARMUP_REQUESTS requests, ignoring results)"
+measureAverageResponseTimeInMillis "$WARMUP_REQUESTS" > /dev/null
+
+# Measure performance
+echo "Measuring average time of $MEASUREMENT_REQUESTS total requests, sending $PARALLEL_REQUESTS requests in parallel"
+ncmpResponseTime=$(measureAverageResponseTimeInMillis "$MEASUREMENT_REQUESTS")
+ncmpOverhead=$(echo "$ncmpResponseTime - $DMI_DATA_DELAY" | bc)
+
+# Report performance
+echo "Average response time from NCMP: $ncmpResponseTime ms"
+echo "Average response time from DMI: $DMI_DATA_DELAY ms"
+echo "NCMP overhead: $ncmpOverhead ms"