diff --git a/examples/test_performance/REAME.md b/examples/test_performance/REAME.md new file mode 100644 index 000000000..4d053e0e1 --- /dev/null +++ b/examples/test_performance/REAME.md @@ -0,0 +1,33 @@ +TEST PERFORMANCE +== +This directory contains a script that generates a graph of how the latency and throughput of an NF chain changes based on its length + +Optional Libraries +-- +matplotlib library is required for graph generation. +If libpcap is not installed run (might need to update/upgrade): +``` +sudo apt-get install python3-matplotlib +``` + +Compilation and Execution +-- +``` +cd examples/test_performance + ./test_performanmce.sh -s [specific lengths of NFs] + + OR + + ./test_performanmce.sh -i [one int specifying the max # of NFs] + + ``` + +SUMMARY: + +test_performance.sh -- a script that generates data based on a changing # of NFs + +make_data.py -- parses through NF data and outputs it to a data.txt + +make_graph.py -- uses the appropriate data in the data.txt file to make a graph + +clear_files.py -- resets data.txt file so that the script can run again diff --git a/examples/test_performance/change_nfs.py b/examples/test_performance/change_nfs.py new file mode 100755 index 000000000..6ce11e31d --- /dev/null +++ b/examples/test_performance/change_nfs.py @@ -0,0 +1,59 @@ +#!/usr/bin/python + +### This script modifies the example_nf_deploy.json file so that it runs (input - 1) simple_forward NFs +### It runs (input -1) simple_forward NFs, 1 speed tester, or (input) total nfs + +import json +import sys + +num_nfs = 1 + +if len(sys.argv) == 2: + num_nfs = int(sys.argv[1]) + +print(f"did it work-change nfs: {num_nfs}") + +with open("../example_nf_deploy.json", "r+") as file: + json_data = file.read() +data = json.loads(json_data) + +### finds the last NF, deletes it, replaces it with modified parameters, adds another NF +while(num_nfs - 1 > len(data["simple_forward"])): + nf_index_to_change = len(data["simple_forward"]) - 1 + + simple_forward_json = data["simple_forward"][nf_index_to_change]["parameters"] + # print(len(data["simple_forward"])) + # print(f"simple_forward_json: {simple_forward_json}") + if (len(data["simple_forward"]) < 8): + start_nf = simple_forward_json[:-1] + else: + start_nf = simple_forward_json[:-1] + # print(f"start_nf: {start_nf}") + # print(f"start_nf[0]: {start_nf[0]} is type {type(start_nf[0])}") + + # print(f"str((int(start_nf[0]) +1 ) : {(int(start_nf[0]) +1 )}" ) + if (len(data["simple_forward"]) < 9): + start_nf = start_nf + str((int(start_nf[0]) +1 ) ) #makes new destination by concatenating the ID of the last nf (to be added) + else: + start_nf = start_nf + str((int(start_nf[0:2]) +1 ) ) + # print(f"start nf after concatenation: {start_nf}") + + + if (len(data["simple_forward"]) < 8): + end_nf = start_nf[-1] + " -d 1" # causes the last NF to route back to the speed_tester + else: + end_nf = start_nf[-2:] + " -d 1" # causes the last NF to route back to the speed_tester + + # print(f"end_nf: {end_nf}") + start_nf_dict = {"parameters":start_nf} + end_nf_dict = {"parameters":end_nf} + + del data["simple_forward"][nf_index_to_change] + + data["simple_forward"].append(start_nf_dict) + data["simple_forward"].append(end_nf_dict) + +new_data = json.dumps(data, indent=2) + +with open("../example_nf_deploy_test_p_template.json", "w") as file: + json.dump(data, file, indent=8) diff --git a/examples/test_performance/clear_files.py b/examples/test_performance/clear_files.py new file mode 100755 index 000000000..cc48523b1 --- /dev/null +++ b/examples/test_performance/clear_files.py @@ -0,0 +1,32 @@ +#!/usr/bin/python +# This script resets the data.txt and example_nf_deploy scripts and returns them to their original state + +import json + +with open("data.txt", 'r+') as f: + with open("data-copy.txt", "w") as file: + file.write(f.read()) + f.truncate(0) + +with open("../example_nf_deploy_test_p_template.json", "w") as f: + f.write("") + data = { + "globals": [ + { + "TTL": 1 + }, + { + "directory": "output_files" + } + ], + "simple_forward": [ + { + "parameters": "2 -d 1" + } + ], + "speed_tester": [ + { + "parameters": "1 -d 2 -l -c 16000" + } + ]} + json.dump(data, f, indent=8) diff --git a/examples/test_performance/data-copy.txt b/examples/test_performance/data-copy.txt new file mode 100644 index 000000000..a85bcdc95 --- /dev/null +++ b/examples/test_performance/data-copy.txt @@ -0,0 +1,3 @@ +5,8254222,1997083, +10,4521436,3337751, +15,2104461,6486186, diff --git a/examples/test_performance/data.txt b/examples/test_performance/data.txt new file mode 100644 index 000000000..e69de29bb diff --git a/examples/test_performance/graph.png b/examples/test_performance/graph.png new file mode 100644 index 000000000..132064b5a Binary files /dev/null and b/examples/test_performance/graph.png differ diff --git a/examples/test_performance/make_data.py b/examples/test_performance/make_data.py new file mode 100755 index 000000000..4f0b8cc29 --- /dev/null +++ b/examples/test_performance/make_data.py @@ -0,0 +1,65 @@ +#!/usr/bin/python + +### This script creates the data necessary to make a graph of how +### the latency and throughput of an NF chain changes based on length +### outputs 3 numbers to 'data.txt': [#nfs, TX average, and latency average] + +import sys + +num_nfs = 1 + +if len(sys.argv) == 2: + num_nfs = sys.argv[1] + +print(f"Did it work? (make_data.py): {num_nfs}") + +file1 = open("../output_files/log-speed_tester-1.txt", "r") + +TX_data = [] +latency_data = [] + +### adds the correct values from the log-speed_tester file +for line in file1.readlines(): + if ('TX pkts per second: ' in line): + index = line.index(': ') + 3 # spacing is weird in the data file + TX_data.append(line[index:]) + if ('Avg latency nanoseconds: ' in line): + index = line.index(': ') + 2 + latency_data.append(line[index:]) + # if ('TX pkts per second: ' in line): + # datapoint = line.split(" ") + # print(f"datapoint[5] is {datapoint[5]} with type {type(datapoint[5])}") + # TX_data.append(datapoint[5]) + # if ('Avg latency nanoseconds: ' in line): + # datapoint = line.split(" ") + # latency_data.append(datapoint[3]) + + ### alternative way of adding data that uses exact spacing + + +file1.close() + +### find the averages of the data + +TX_average = 0 + +for x in TX_data: + TX_average+=int(x) + +TX_average /= len(TX_data) + +latency_average = 0 + +for x in latency_data: + latency_average+=int(x) +latency_average /= len(latency_data) + +##writes the data out as 3 values, all seperated by commas, to the data.txt file + +f = open("data.txt", "a") +f.write(str(num_nfs)) +f.write(',') +f.write(str(int(TX_average))) +f.write(',') +f.write(str(int(latency_average))) +f.write(',\n') diff --git a/examples/test_performance/make_graph.py b/examples/test_performance/make_graph.py new file mode 100755 index 000000000..b6bc1c60c --- /dev/null +++ b/examples/test_performance/make_graph.py @@ -0,0 +1,110 @@ +#!/usr/bin/python + +### This script creates a graph, using data from the "data.txt" file, measuring +### how the latency and throughput of an NF chain changes based on length + +from matplotlib import pyplot as plt + +x_axis = [] + +y_axis_latency = [] + +y_axis_throughput = [] + +file = open("data.txt", "r") + +for line in file.readlines(): + linedata = line.split(",") + x_axis.append(int(linedata[0])) + y_axis_latency.append(int(linedata[1])) + y_axis_throughput.append(int(linedata[2])) + + +# x_axis.sort() +# y_axis_latency.sort() +# y_axis_throughput.sort() + +fig,ax = plt.subplots() +# make a plot +ax.plot(x_axis, y_axis_latency, color="red", marker="o") +# set x-axis label +ax.set_xlabel("NFs",fontsize=14) +# set y-axis label +ax.set_ylabel("Latency (ns)",color="red",fontsize=14) + +ax2=ax.twinx() +# make a plot with different y-axis using second axis object +ax2.plot(x_axis, y_axis_throughput,color="blue",marker="o") +ax2.set_ylabel("TX pkts per second",color="blue",fontsize=14) +plt.show() +# save the plot as a file +fig.savefig('graph.png', + format='png', + dpi=100, + bbox_inches='tight') + + + + + + + +# from matplotlib import pyplot as plt + +# ### we need to make a 2nd y-axis, so we use the twinx() fnx +# ax = plt.subplot() +# twin1 = ax.twinx() + +# ### create empty lists to store in future data +# x_axis = [] + +# y_axis_latency = [] + +# y_axis_throughput = [] + +# ###reads in data from file, adds them to empty variables +# file = open("data.txt", "r") + +# for line in file.readlines(): +# linedata = line.split(",") +# x_axis.append(int(linedata[0])) +# y_axis_latency.append(int(linedata[1])) +# y_axis_throughput.append(int(linedata[2])) + +# # for datapoint in x_axis: +# # print(f"x value: {datapoint}") + +# # for datapoint in y_axis_latency: +# # print(f"y value: {datapoint}") + +# # for datapoint in y_axis_throughput: +# # print(f"y value: {datapoint}") + +# #ax.set_ylim(30000, 100000) +# #twin1.set_ylim(15000000, 40000000) + +# ### plot the data +# p1, = ax.plot(x_axis, y_axis_latency,"-b", label="Latency (ns)") +# ax.set_xlabel("NF Chain Length") +# ax.set_ylabel("Latency (ns)") + +# ax.autoscale(enable=False, axis='both', tight=None) + +# p2, = twin1.plot(x_axis, y_axis_throughput, "-r", label="TX pkts per second") +# twin1.set_ylabel("TX pkts per second") + +# ###creates the legend and sets colors +# ax.yaxis.label.set_color(p1.get_color()) +# twin1.yaxis.label.set_color(p2.get_color()) + +# tkw = dict(size=4, width=1.5) +# ax.tick_params(axis='y', colors=p1.get_color(), **tkw) +# twin1.tick_params(axis='y', colors=p2.get_color(), **tkw) +# ax.tick_params(axis='x', **tkw) + +# ax.legend(handles=[p1, p2]) + + + +# ### saves the .png file in test_performance directory +# plt.savefig('performance_graph.png') diff --git a/examples/test_performance/test_performance.sh b/examples/test_performance/test_performance.sh new file mode 100755 index 000000000..c21c8aad6 --- /dev/null +++ b/examples/test_performance/test_performance.sh @@ -0,0 +1,112 @@ +#!/bin/bash + +echo "Type of script (iterative (i) or selective (s) ) = $1" + +python3 ./clear_files.py + +if [[ $1 = "-s" ]] +then + echo "Running selective version" + NUM_NFS_LIST=( "$@" ) + + # check that inputs are valid (positive) integers + + VALID_INPUT=true + + for (( i=1; i<${#NUM_NFS_LIST[@]}; i++)); + do + if [[ ${NUM_NFS_LIST[$i]} =~ ^[+]?[0-9]+$ ]]; + then + : + else + VALID_INPUT=false + fi + done + + if [[ "$VALID_INPUT" != true ]]; + then + echo "Error: your inputs (desired chain lengths) must be positive integers" + exit 0 + fi + + # run manager at each chain length + for (( i=1; i<${#NUM_NFS_LIST[@]}; i++)); + do + echo "Running ${NUM_NFS_LIST[$i]} NFs" + python3 change_nfs.py ${NUM_NFS_LIST[$i]} # adds an additional simple_forward NF for the next loop iteration + + echo "Running onvm mgr" + cd ../../ + ./onvm/go.sh -k 1 -n 0xFFFFF -s stdout -t 30 & + + sleep 15s + + echo "Running ${NUM_NFS_LIST[$i]} NFs" + cd examples + python3 run_group.py example_nf_deploy_test_p_template.json # starts running the set NFs for 30sec, enough time to collect ample data + + wait + + cd test_performance + echo "Making data" + python3 make_data.py ${NUM_NFS_LIST[$i]} # adds the data and averages it + + done + + python3 ./make_graph.py # makes the graph + + python3 ./clear_files.py # resets data files and nf_deploy json files + +elif [[ $1 = "-i" ]] +then + echo "Running iterative version" + NUM_NFS=2 + + # check if the input is valid + VALID_INPUT=true + + if [[ $2 =~ ^[+]?[0-9]+$ ]]; + then + : + else + VALID_INPUT=false + fi + + if [[ "$VALID_INPUT" != true ]]; + then + echo "Error: your input (max NFs) must be a positive integer" + exit 0 + fi + + # iterate through each version of the NF + while [ $NUM_NFS -le $2 ] + do + echo "Running $NUM_NFS NFs" + echo "Running onvm mgr" + cd ../../ + ./onvm/go.sh -k 1 -n 0xFFFFF -s stdout -t 30 & + + sleep 15s + + echo "Running $NUM_NFS NFs" + cd examples + python3 ./run_group.py ./example_nf_deploy_test_p_template.json # starts running the set NFs for 30sec, enough time to collect ample data + + sleep 30s + + cd test_performance + echo "Making data" + python3 ./make_data.py $NUM_NFS # adds the data and averages it + + NUM_NFS=$((NUM_NFS+1)) + + echo "Changing NFs" + python3 ./change_nfs.py $NUM_NFS # adds an additional simple_forward NF for the next loop iteration + done + + python3 ./make_graph.py # makes the graph + + python3 ./clear_files.py # resets data files +else + echo "Error: incorrect 1st input; must be either \"-i\" or \"-s\"" +fi