From 07fd8412da4931eeb9e239bde2946f0d1133519f Mon Sep 17 00:00:00 2001 From: Graham Whaley Date: Wed, 13 May 2020 17:33:35 +0100 Subject: [PATCH] metrics: report: Error more cleanly Clean up the rest of the report R files to allow them to quit cleanly when they find an error or missing data, so that the final PDF report gives meaninful errors such as 'No data found', rather than cryptic R errors. Signed-off-by: Graham Whaley --- .../report_dockerfile/collectd_scaling.R | 1348 +++++++++-------- .../report/report_dockerfile/dut-details.R | 172 ++- .../report/report_dockerfile/elasticsearchr.R | 26 + .../report_dockerfile/metrics_report.Rmd | 6 +- metrics/report/report_dockerfile/node-info.R | 151 +- metrics/report/report_dockerfile/parallel.R | 202 +-- metrics/report/report_dockerfile/test.R | 9 + .../report/report_dockerfile/tidy_scaling.R | 2 +- 8 files changed, 1001 insertions(+), 915 deletions(-) create mode 100644 metrics/report/report_dockerfile/elasticsearchr.R create mode 100644 metrics/report/report_dockerfile/test.R diff --git a/metrics/report/report_dockerfile/collectd_scaling.R b/metrics/report/report_dockerfile/collectd_scaling.R index 88e65bf..2365364 100755 --- a/metrics/report/report_dockerfile/collectd_scaling.R +++ b/metrics/report/report_dockerfile/collectd_scaling.R @@ -13,692 +13,710 @@ suppressMessages(library(jsonlite)) # to load the data. suppressMessages(library(scales)) # For de-science notation of axis library(tibble) # tibbles for tidy data -testnames=c( - "k8s-rapid.*" -) -podbootdata=c() # Track per-launch data -cpuidledata=c() # Track cpu idle data per nodes -memfreedata=c() # Track mem free data for nodes -inodefreedata=c() # Track inode free data for nodes -ifpacketdata=c() # Track interface packet data for nodes -ifoctetdata=c() # Track interface octets data for nodes -ifdropdata=c() # Track interface dropped data for nodes -iferrordata=c() # Track interface errors data for nodes -memstats=c() # Statistics for memory usage -cpustats=c() # Statistics for cpu usage -bootstats=c() # Statistics for boot (launch) times -inodestats=c() # Statistics for inode usage +render_collectd_scaling <- function() +{ + testnames=c( + "k8s-rapid.*" + ) -# iterate over every set of results (test run) -for (currentdir in resultdirs) { - # For every results file we are interested in evaluating - for (testname in testnames) { - matchdir=paste(inputdir, currentdir, sep="") - matchfile=paste(testname, '\\.json', sep="") - files=list.files(matchdir, pattern=matchfile) - if ( length(files) == 0 ) { - #warning(paste("Pattern [", matchdir, "/", matchfile, "] matched nothing")) - } + podbootdata=c() # Track per-launch data + cpuidledata=c() # Track cpu idle data per nodes + memfreedata=c() # Track mem free data for nodes + inodefreedata=c() # Track inode free data for nodes + ifpacketdata=c() # Track interface packet data for nodes + ifoctetdata=c() # Track interface octets data for nodes + ifdropdata=c() # Track interface dropped data for nodes + iferrordata=c() # Track interface errors data for nodes + memstats=c() # Statistics for memory usage + cpustats=c() # Statistics for cpu usage + bootstats=c() # Statistics for boot (launch) times + inodestats=c() # Statistics for inode usage - # For every matching results file - for (ffound in files) { - fname=paste(inputdir, currentdir, ffound, sep="") - if ( !file.exists(fname)) { - warning(paste("Skipping non-existent file: ", fname)) - next - } - # Derive the name from the test result dirname - datasetname=basename(currentdir) - - # Import the data - fdata=fromJSON(fname) - - # determine whether NoSchedule taint is present for each node - # from the 'kubectl get nodes' data dump in the json - node_sched_data=list() - nodes=c() - num_nodes=nrow(fdata$'kubectl-get-nodes'$items) - for(n in seq(num_nodes)){ - json_node=fdata$'kubectl-get-nodes'$items[n,] - json_node_name=json_node$metadata$name - taints=data.frame(json_node$spec$taints) - nosched = "false" - if(nrow(taints) > 0) { - for(t in seq(nrow(taints))){ - if(taints[t,]$effect == "NoSchedule") { - nosched = "true" - break - } - } - } - node_sched_data[json_node_name] = nosched + # iterate over every set of results (test run) + for (currentdir in resultdirs) { + # For every results file we are interested in evaluating + for (testname in testnames) { + matchdir=paste(inputdir, currentdir, sep="") + matchfile=paste(testname, '\\.json', sep="") + files=list.files(matchdir, pattern=matchfile) + if ( length(files) == 0 ) { + #warning(paste("Pattern [", matchdir, "/", matchfile, "] matched nothing")) } - # De-nest the test name specific data - shortname=substr(ffound, 1, nchar(ffound)-nchar(".json")) - fdata=fdata[[shortname]] - testname=datasetname - - # Most of the data we are looking for comes in BootResults, so pick it out to make - # referencing easier - br=fdata$BootResults - - ######################################################## - #### Now extract all the pod launch boot data items #### - ######################################################## - local_bootdata=tibble(launch_time=br$launch_time$Result) - local_bootdata=cbind(local_bootdata, n_pods=br$n_pods$Result) - local_bootdata=cbind(local_bootdata, testname=rep(testname, length(local_bootdata$n_pods))) - local_bootdata=cbind(local_bootdata, ns=br$date$ns) - # get the epoch time in seconds for the boot - local_bootdata$epoch = local_bootdata$ns/1000000000 - local_bootdata$s_offset = local_bootdata$epoch - local_bootdata[1,]$epoch - - # Now Calculate some stats. This gets more complicated as we may have n-nodes, - # and we want to show a 'pod average', so we try to assess for all nodes. If - # we have different 'size' nodes in a cluster, that could throw out the result, - # but the only other option would be to try and show every node separately in the - # table. - - # Get a list of all the nodes from the schedule data list - nodes=names(node_sched_data) - - memtotal=0 - cputotal=0 - inodetotal=0 - cpu_idle_data=c() - mem_free_data=c() - inode_free_data=c() - interface_packets_data=c() - interface_octets_data=c() - interface_dropped_data=c() - interface_errors_data=c() - # Calculate per-node totals, and tot them up to a global total. - for (n in nodes) { - # check if collectd node data has been untarred yet, if not untar - node_dir=paste(inputdir, currentdir, n, sep="") - if ( !file.exists(node_dir)) { - node_tar=paste(inputdir, currentdir, n, ".tar.gz", sep="") - system(paste("mkdir -p", node_dir)) - system(paste("tar -xzf", node_tar, "-C", node_dir)) - } - # all collectd data is under localhost/ - localhost_dir=paste(node_dir, "localhost", sep="/") - - # grab memory data - memory_dir=paste(localhost_dir, "memory", sep="/") - # filename has date on the end, so look for the right file name - freemem_pattern='^memory\\-free' - files=list.files(memory_dir, pattern=freemem_pattern) - # collectd csv plugin starts a new file for each day of data collected - for(file in files) { - mem_free_csv=paste(memory_dir, file, sep="/") - node_mem_free_data=read.csv(mem_free_csv, header=TRUE, sep=",") - node_mem_free_data=cbind(node_mem_free_data, - node=rep(n, length(node_mem_free_data$value))) - node_mem_free_data=cbind(node_mem_free_data, - testname=rep(testname, length(node_mem_free_data$value))) - node_mem_free_data$s_offset = node_mem_free_data$epoch - local_bootdata[1,]$epoch - - mem_free_data=rbind(mem_free_data, node_mem_free_data) - } - - # grab CPU data - cpu_dir=paste(localhost_dir, "aggregation-cpu-average", sep="/") - # filename has date on the end, so look for the right file name - percent_idle_pattern='^percent\\-idle' - files=list.files(cpu_dir, pattern=percent_idle_pattern) - for(file in files) { - cpu_idle_csv=paste(cpu_dir, file, sep="/") - node_cpu_idle_data=read.csv(cpu_idle_csv, header=TRUE, sep=",") - node_cpu_idle_data=cbind(node_cpu_idle_data, - node=rep(n, length(node_cpu_idle_data$value))) - node_cpu_idle_data=cbind(node_cpu_idle_data, - testname=rep(testname, length(node_cpu_idle_data$value))) - node_cpu_idle_data$s_offset = node_cpu_idle_data$epoch - local_bootdata[1,]$epoch - - cpu_idle_data=rbind(cpu_idle_data, node_cpu_idle_data) - } - - # grab inode data - inode_dir=paste(localhost_dir, "df-root", sep="/") - # filename has date on the end, so look for the right file name - inode_free_pattern='^df_inodes\\-free' - files=list.files(inode_dir, pattern=inode_free_pattern) - for(file in files) { - inode_free_csv=paste(inode_dir, file, sep="/") - node_inode_free_data=read.csv(inode_free_csv, header=TRUE, sep=",") - node_inode_free_data=cbind(node_inode_free_data, - node=rep(n, length(node_inode_free_data$value))) - node_inode_free_data=cbind(node_inode_free_data, - testname=rep(testname, length(node_inode_free_data$value))) - node_inode_free_data$s_offset = node_inode_free_data$epoch - local_bootdata[1,]$epoch - - inode_free_data=rbind(inode_free_data, node_inode_free_data) - } - - # grab interface data - interface_dir_pattern='^interface\\-' - files=list.files(localhost_dir, pattern=interface_dir_pattern) - for (file in files) { - interface_dir=paste(localhost_dir, file, sep="/") - interface_name=substr(file, nchar("interface-")+1, nchar(file)) - - # filename has date on the end, so look for the right file name - interface_packets_pattern='^if_packets' - int_files=list.files(interface_dir, pattern=interface_packets_pattern) - for (int_file in int_files) { - interface_packets_csv=paste(interface_dir, int_file, sep="/") - node_interface_packets_data=read.csv(interface_packets_csv, header=TRUE, sep=",") - node_interface_packets_data=cbind(node_interface_packets_data, - node=rep(n, length(node_interface_packets_data$epoch))) - node_interface_packets_data=cbind(node_interface_packets_data, - testname=rep(testname, - length(node_interface_packets_data$epoch))) - node_interface_packets_data=cbind(node_interface_packets_data, - name=rep(interface_name, - length(node_interface_packets_data$epoch))) - node_interface_packets_data$s_offset = node_interface_packets_data$epoch - local_bootdata[1,]$epoch - - interface_packets_data=rbind(interface_packets_data, node_interface_packets_data) - } - - # filename has date on the end, so look for the right file name - interface_octets_pattern='^if_octets' - int_files=list.files(interface_dir, pattern=interface_octets_pattern) - for (int_file in int_files) { - interface_octets_csv=paste(interface_dir, int_file, sep="/") - node_interface_octets_data=read.csv(interface_octets_csv, header=TRUE, sep=",") - node_interface_octets_data=cbind(node_interface_octets_data, - node=rep(n, length(node_interface_octets_data$epoch))) - node_interface_octets_data=cbind(node_interface_octets_data, - testname=rep(testname, - length(node_interface_octets_data$epoch))) - node_interface_octets_data=cbind(node_interface_octets_data, - name=rep(interface_name, - length(node_interface_octets_data$epoch))) - node_interface_octets_data$s_offset = node_interface_octets_data$epoch - local_bootdata[1,]$epoch - - interface_octets_data=rbind(interface_octets_data, node_interface_octets_data) - } - - # filename has date on the end, so look for the right file name - interface_dropped_pattern='^if_dropped' - int_files=list.files(interface_dir, pattern=interface_dropped_pattern) - for (int_file in int_files) { - interface_dropped_csv=paste(interface_dir, int_file, sep="/") - node_interface_dropped_data=read.csv(interface_dropped_csv, header=TRUE, sep=",") - node_interface_dropped_data=cbind(node_interface_dropped_data, - node=rep(n, length(node_interface_dropped_data$epoch))) - node_interface_dropped_data=cbind(node_interface_dropped_data, - testname=rep(testname, - length(node_interface_dropped_data$epoch))) - node_interface_dropped_data=cbind(node_interface_dropped_data, - name=rep(interface_name, - length(node_interface_dropped_data$epoch))) - node_interface_dropped_data$s_offset = node_interface_dropped_data$epoch - local_bootdata[1,]$epoch - - interface_dropped_data=rbind(interface_dropped_data, node_interface_dropped_data) - } - - # filename has date on the end, so look for the right file name - interface_errors_pattern='^if_errors' - int_files=list.files(interface_dir, pattern=interface_errors_pattern) - for (int_file in int_files) { - interface_errors_csv=paste(interface_dir, int_file, sep="/") - node_interface_errors_data=read.csv(interface_errors_csv, header=TRUE, sep=",") - node_interface_errors_data=cbind(node_interface_errors_data, - node=rep(n, length(node_interface_errors_data$epoch))) - node_interface_errors_data=cbind(node_interface_errors_data, - testname=rep(testname, - length(node_interface_errors_data$epoch))) - node_interface_errors_data=cbind(node_interface_errors_data, - name=rep(interface_name, - length(node_interface_errors_data$epoch))) - node_interface_errors_data$s_offset = node_interface_errors_data$epoch - local_bootdata[1,]$epoch - - interface_errors_data=rbind(interface_errors_data, node_interface_errors_data) - } - } - - # Do not use the master (non-schedulable) nodes to calculate - # launched pod metrics - if(node_sched_data[n] == "true") { + # For every matching results file + for (ffound in files) { + fname=paste(inputdir, currentdir, ffound, sep="") + if ( !file.exists(fname)) { + warning(paste("Skipping non-existent file: ", fname)) next } + # Derive the name from the test result dirname + datasetname=basename(currentdir) - # get the epoch time of first and last pod launch - start_time=local_bootdata$epoch[1] - end_time=local_bootdata$epoch[length(local_bootdata$epoch)] + # Import the data + fdata=fromJSON(fname) - # get value closest to first pod launch - mem_start_index=Position(function(x) x > start_time, node_mem_free_data$epoch) - # take the reading previous to the index as long as a valid index - if (is.na(mem_start_index)) { - mem_start_index = 1 - } else if (mem_start_index > 1) { - mem_start_index = mem_start_index - 1 + # determine whether NoSchedule taint is present for each node + # from the 'kubectl get nodes' data dump in the json + node_sched_data=list() + nodes=c() + num_nodes=nrow(fdata$'kubectl-get-nodes'$items) + for(n in seq(num_nodes)){ + json_node=fdata$'kubectl-get-nodes'$items[n,] + json_node_name=json_node$metadata$name + taints=data.frame(json_node$spec$taints) + nosched = "false" + if(nrow(taints) > 0) { + for(t in seq(nrow(taints))){ + if(taints[t,]$effect == "NoSchedule") { + nosched = "true" + break + } + } + } + node_sched_data[json_node_name] = nosched } - max_free_mem=node_mem_free_data$value[mem_start_index] - # get value closest to last pod launch - mem_end_index=Position(function(x) x > end_time, node_mem_free_data$epoch) - # take the reading previous to the index as long as a valid index - if (is.na(mem_end_index)) { - mem_end_index = length(node_mem_free_data$epoch) - } else if (mem_end_index > 1) { - mem_end_index = mem_end_index - 1 + # De-nest the test name specific data + shortname=substr(ffound, 1, nchar(ffound)-nchar(".json")) + fdata=fdata[[shortname]] + testname=datasetname + + # Most of the data we are looking for comes in BootResults, so pick it out to make + # referencing easier + br=fdata$BootResults + + ######################################################## + #### Now extract all the pod launch boot data items #### + ######################################################## + local_bootdata=tibble(launch_time=br$launch_time$Result) + local_bootdata=cbind(local_bootdata, n_pods=br$n_pods$Result) + local_bootdata=cbind(local_bootdata, testname=rep(testname, length(local_bootdata$n_pods))) + local_bootdata=cbind(local_bootdata, ns=br$date$ns) + # get the epoch time in seconds for the boot + local_bootdata$epoch = local_bootdata$ns/1000000000 + local_bootdata$s_offset = local_bootdata$epoch - local_bootdata[1,]$epoch + + # Now Calculate some stats. This gets more complicated as we may have n-nodes, + # and we want to show a 'pod average', so we try to assess for all nodes. If + # we have different 'size' nodes in a cluster, that could throw out the result, + # but the only other option would be to try and show every node separately in the + # table. + + # Get a list of all the nodes from the schedule data list + nodes=names(node_sched_data) + + memtotal=0 + cputotal=0 + inodetotal=0 + cpu_idle_data=c() + mem_free_data=c() + inode_free_data=c() + interface_packets_data=c() + interface_octets_data=c() + interface_dropped_data=c() + interface_errors_data=c() + # Calculate per-node totals, and tot them up to a global total. + for (n in nodes) { + # check if collectd node data has been untarred yet, if not untar + node_dir=paste(inputdir, currentdir, n, sep="") + if ( !file.exists(node_dir)) { + node_tar=paste(inputdir, currentdir, n, ".tar.gz", sep="") + system(paste("mkdir -p", node_dir)) + system(paste("tar -xzf", node_tar, "-C", node_dir)) + } + # all collectd data is under localhost/ + localhost_dir=paste(node_dir, "localhost", sep="/") + + # grab memory data + memory_dir=paste(localhost_dir, "memory", sep="/") + # filename has date on the end, so look for the right file name + freemem_pattern='^memory\\-free' + files=list.files(memory_dir, pattern=freemem_pattern) + # collectd csv plugin starts a new file for each day of data collected + for(file in files) { + mem_free_csv=paste(memory_dir, file, sep="/") + node_mem_free_data=read.csv(mem_free_csv, header=TRUE, sep=",") + node_mem_free_data=cbind(node_mem_free_data, + node=rep(n, length(node_mem_free_data$value))) + node_mem_free_data=cbind(node_mem_free_data, + testname=rep(testname, length(node_mem_free_data$value))) + node_mem_free_data$s_offset = node_mem_free_data$epoch - local_bootdata[1,]$epoch + + mem_free_data=rbind(mem_free_data, node_mem_free_data) + } + + # grab CPU data + cpu_dir=paste(localhost_dir, "aggregation-cpu-average", sep="/") + # filename has date on the end, so look for the right file name + percent_idle_pattern='^percent\\-idle' + files=list.files(cpu_dir, pattern=percent_idle_pattern) + for(file in files) { + cpu_idle_csv=paste(cpu_dir, file, sep="/") + node_cpu_idle_data=read.csv(cpu_idle_csv, header=TRUE, sep=",") + node_cpu_idle_data=cbind(node_cpu_idle_data, + node=rep(n, length(node_cpu_idle_data$value))) + node_cpu_idle_data=cbind(node_cpu_idle_data, + testname=rep(testname, length(node_cpu_idle_data$value))) + node_cpu_idle_data$s_offset = node_cpu_idle_data$epoch - local_bootdata[1,]$epoch + + cpu_idle_data=rbind(cpu_idle_data, node_cpu_idle_data) + } + + # grab inode data + inode_dir=paste(localhost_dir, "df-root", sep="/") + # filename has date on the end, so look for the right file name + inode_free_pattern='^df_inodes\\-free' + files=list.files(inode_dir, pattern=inode_free_pattern) + for(file in files) { + inode_free_csv=paste(inode_dir, file, sep="/") + node_inode_free_data=read.csv(inode_free_csv, header=TRUE, sep=",") + node_inode_free_data=cbind(node_inode_free_data, + node=rep(n, length(node_inode_free_data$value))) + node_inode_free_data=cbind(node_inode_free_data, + testname=rep(testname, length(node_inode_free_data$value))) + node_inode_free_data$s_offset = node_inode_free_data$epoch - local_bootdata[1,]$epoch + + inode_free_data=rbind(inode_free_data, node_inode_free_data) + } + + # grab interface data + interface_dir_pattern='^interface\\-' + files=list.files(localhost_dir, pattern=interface_dir_pattern) + for (file in files) { + interface_dir=paste(localhost_dir, file, sep="/") + interface_name=substr(file, nchar("interface-")+1, nchar(file)) + + # filename has date on the end, so look for the right file name + interface_packets_pattern='^if_packets' + int_files=list.files(interface_dir, pattern=interface_packets_pattern) + for (int_file in int_files) { + interface_packets_csv=paste(interface_dir, int_file, sep="/") + node_interface_packets_data=read.csv(interface_packets_csv, header=TRUE, sep=",") + node_interface_packets_data=cbind(node_interface_packets_data, + node=rep(n, length(node_interface_packets_data$epoch))) + node_interface_packets_data=cbind(node_interface_packets_data, + testname=rep(testname, + length(node_interface_packets_data$epoch))) + node_interface_packets_data=cbind(node_interface_packets_data, + name=rep(interface_name, + length(node_interface_packets_data$epoch))) + node_interface_packets_data$s_offset = node_interface_packets_data$epoch - local_bootdata[1,]$epoch + + interface_packets_data=rbind(interface_packets_data, node_interface_packets_data) + } + + # filename has date on the end, so look for the right file name + interface_octets_pattern='^if_octets' + int_files=list.files(interface_dir, pattern=interface_octets_pattern) + for (int_file in int_files) { + interface_octets_csv=paste(interface_dir, int_file, sep="/") + node_interface_octets_data=read.csv(interface_octets_csv, header=TRUE, sep=",") + node_interface_octets_data=cbind(node_interface_octets_data, + node=rep(n, length(node_interface_octets_data$epoch))) + node_interface_octets_data=cbind(node_interface_octets_data, + testname=rep(testname, + length(node_interface_octets_data$epoch))) + node_interface_octets_data=cbind(node_interface_octets_data, + name=rep(interface_name, + length(node_interface_octets_data$epoch))) + node_interface_octets_data$s_offset = node_interface_octets_data$epoch - local_bootdata[1,]$epoch + + interface_octets_data=rbind(interface_octets_data, node_interface_octets_data) + } + + # filename has date on the end, so look for the right file name + interface_dropped_pattern='^if_dropped' + int_files=list.files(interface_dir, pattern=interface_dropped_pattern) + for (int_file in int_files) { + interface_dropped_csv=paste(interface_dir, int_file, sep="/") + node_interface_dropped_data=read.csv(interface_dropped_csv, header=TRUE, sep=",") + node_interface_dropped_data=cbind(node_interface_dropped_data, + node=rep(n, length(node_interface_dropped_data$epoch))) + node_interface_dropped_data=cbind(node_interface_dropped_data, + testname=rep(testname, + length(node_interface_dropped_data$epoch))) + node_interface_dropped_data=cbind(node_interface_dropped_data, + name=rep(interface_name, + length(node_interface_dropped_data$epoch))) + node_interface_dropped_data$s_offset = node_interface_dropped_data$epoch - local_bootdata[1,]$epoch + + interface_dropped_data=rbind(interface_dropped_data, node_interface_dropped_data) + } + + # filename has date on the end, so look for the right file name + interface_errors_pattern='^if_errors' + int_files=list.files(interface_dir, pattern=interface_errors_pattern) + for (int_file in int_files) { + interface_errors_csv=paste(interface_dir, int_file, sep="/") + node_interface_errors_data=read.csv(interface_errors_csv, header=TRUE, sep=",") + node_interface_errors_data=cbind(node_interface_errors_data, + node=rep(n, length(node_interface_errors_data$epoch))) + node_interface_errors_data=cbind(node_interface_errors_data, + testname=rep(testname, + length(node_interface_errors_data$epoch))) + node_interface_errors_data=cbind(node_interface_errors_data, + name=rep(interface_name, + length(node_interface_errors_data$epoch))) + node_interface_errors_data$s_offset = node_interface_errors_data$epoch - local_bootdata[1,]$epoch + + interface_errors_data=rbind(interface_errors_data, node_interface_errors_data) + } + } + + # Do not use the master (non-schedulable) nodes to calculate + # launched pod metrics + if(node_sched_data[n] == "true") { + next + } + + # get the epoch time of first and last pod launch + start_time=local_bootdata$epoch[1] + end_time=local_bootdata$epoch[length(local_bootdata$epoch)] + + # get value closest to first pod launch + mem_start_index=Position(function(x) x > start_time, node_mem_free_data$epoch) + # take the reading previous to the index as long as a valid index + if (is.na(mem_start_index)) { + mem_start_index = 1 + } else if (mem_start_index > 1) { + mem_start_index = mem_start_index - 1 + } + max_free_mem=node_mem_free_data$value[mem_start_index] + + # get value closest to last pod launch + mem_end_index=Position(function(x) x > end_time, node_mem_free_data$epoch) + # take the reading previous to the index as long as a valid index + if (is.na(mem_end_index)) { + mem_end_index = length(node_mem_free_data$epoch) + } else if (mem_end_index > 1) { + mem_end_index = mem_end_index - 1 + } + min_free_mem=node_mem_free_data$value[mem_end_index] + + memtotal = memtotal + (max_free_mem - min_free_mem) + + # get value closest to first pod launch + cpu_start_index=Position(function(x) x > start_time, node_cpu_idle_data$epoch) + # take the reading previous to the index as long as a valid index + if (is.na(cpu_start_index)) { + cpu_start_index = 1 + } else if (cpu_start_index > 1) { + cpu_start_index = cpu_start_index - 1 + } + max_idle_cpu=node_cpu_idle_data$value[cpu_start_index] + + # get value closest to last pod launch + cpu_end_index=Position(function(x) x > end_time, node_cpu_idle_data$epoch) + # take the reading previous to the index as long as a valid index + if (is.na(cpu_end_index)) { + cpu_end_index = length(node_cpu_idle_data$epoch) + } else if (cpu_end_index > 1) { + cpu_end_index = cpu_end_index - 1 + } + min_idle_cpu=node_cpu_idle_data$value[cpu_end_index] + + cputotal = cputotal + (max_idle_cpu - min_idle_cpu) + + # get value closest to first pod launch + inode_start_index=Position(function(x) x > start_time, node_inode_free_data$epoch) + # take the reading previous to the index as long as a valid index + if (is.na(inode_start_index)) { + inode_start_index = 1 + } else if (inode_start_index > 1) { + inode_start_index = inode_start_index - 1 + } + max_free_inode=node_inode_free_data$value[inode_start_index] + + # get value closest to last pod launch + inode_end_index=Position(function(x) x > end_time, node_inode_free_data$epoch) + # take the reading previous to the index as long as a valid index + if (is.na(inode_end_index)) { + inode_end_index = length(node_cpu_idle_data$epoch) + } else if (inode_end_index > 1) { + inode_end_index = inode_end_index - 1 + } + min_free_inode=node_inode_free_data$value[inode_end_index] + + inodetotal = inodetotal + (max_free_inode - min_free_inode) } - min_free_mem=node_mem_free_data$value[mem_end_index] - memtotal = memtotal + (max_free_mem - min_free_mem) + num_pods = local_bootdata$n_pods[length(local_bootdata$n_pods)] - # get value closest to first pod launch - cpu_start_index=Position(function(x) x > start_time, node_cpu_idle_data$epoch) - # take the reading previous to the index as long as a valid index - if (is.na(cpu_start_index)) { - cpu_start_index = 1 - } else if (cpu_start_index > 1) { - cpu_start_index = cpu_start_index - 1 - } - max_idle_cpu=node_cpu_idle_data$value[cpu_start_index] + # We get data in b, but want the graphs in Gb. + memtotal = memtotal / (1024*1024*1024) + gb_per_pod = memtotal/num_pods + pod_per_gb = 1/gb_per_pod - # get value closest to last pod launch - cpu_end_index=Position(function(x) x > end_time, node_cpu_idle_data$epoch) - # take the reading previous to the index as long as a valid index - if (is.na(cpu_end_index)) { - cpu_end_index = length(node_cpu_idle_data$epoch) - } else if (cpu_end_index > 1) { - cpu_end_index = cpu_end_index - 1 - } - min_idle_cpu=node_cpu_idle_data$value[cpu_end_index] + # Memory usage stats. + local_mems = c( + "Test"=testname, + "n"=num_pods, + "Tot_Gb"=round(memtotal, 3), + "avg_Gb"=round(gb_per_pod, 4), + "n_per_Gb"=round(pod_per_gb, 2) + ) + memstats=rbind(memstats, local_mems) - cputotal = cputotal + (max_idle_cpu - min_idle_cpu) + # cpu usage stats + local_cpus = c( + "Test"=testname, + "n"=num_pods, + "Tot_CPU"=round(cputotal, 3), + "avg_CPU"=round(cputotal/num_pods, 4) + ) + cpustats=rbind(cpustats, local_cpus) - # get value closest to first pod launch - inode_start_index=Position(function(x) x > start_time, node_inode_free_data$epoch) - # take the reading previous to the index as long as a valid index - if (is.na(inode_start_index)) { - inode_start_index = 1 - } else if (inode_start_index > 1) { - inode_start_index = inode_start_index - 1 - } - max_free_inode=node_inode_free_data$value[inode_start_index] + # launch (boot) stats + local_boots = c( + "Test"=testname, + "n"=num_pods, + "median"=median(na.omit(local_bootdata)$launch_time)/1000, + "min"=min(na.omit(local_bootdata)$launch_time)/1000, + "max"=max(na.omit(local_bootdata)$launch_time)/1000, + "sd"=round(sd(na.omit(local_bootdata)$launch_time)/1000, 4) + ) - # get value closest to last pod launch - inode_end_index=Position(function(x) x > end_time, node_inode_free_data$epoch) - # take the reading previous to the index as long as a valid index - if (is.na(inode_end_index)) { - inode_end_index = length(node_cpu_idle_data$epoch) - } else if (inode_end_index > 1) { - inode_end_index = inode_end_index - 1 - } - min_free_inode=node_inode_free_data$value[inode_end_index] + bootstats=rbind(bootstats, local_boots) - inodetotal = inodetotal + (max_free_inode - min_free_inode) + # inode stats + local_inodes = c( + "Test"=testname, + "n"=num_pods, + "Tot_inode"=round(inodetotal, 3), + "avg_inode"=round(inodetotal/num_pods, 4) + ) + inodestats=rbind(inodestats, local_inodes) + + # And collect up our rows into our global table of all results + # These two tables *should* be the source of all the data we need to + # process and plot (apart from the stats....) + podbootdata=rbind(podbootdata, local_bootdata, make.row.names=FALSE) + cpuidledata=rbind(cpuidledata, cpu_idle_data) + memfreedata=rbind(memfreedata, mem_free_data) + inodefreedata=rbind(inodefreedata, inode_free_data) + ifpacketdata=rbind(ifpacketdata, interface_packets_data) + ifoctetdata=rbind(ifoctetdata, interface_octets_data) + ifdropdata=rbind(ifdropdata, interface_dropped_data) + iferrordata=rbind(iferrordata, interface_errors_data) } - - num_pods = local_bootdata$n_pods[length(local_bootdata$n_pods)] - - # We get data in b, but want the graphs in Gb. - memtotal = memtotal / (1024*1024*1024) - gb_per_pod = memtotal/num_pods - pod_per_gb = 1/gb_per_pod - - # Memory usage stats. - local_mems = c( - "Test"=testname, - "n"=num_pods, - "Tot_Gb"=round(memtotal, 3), - "avg_Gb"=round(gb_per_pod, 4), - "n_per_Gb"=round(pod_per_gb, 2) - ) - memstats=rbind(memstats, local_mems) - - # cpu usage stats - local_cpus = c( - "Test"=testname, - "n"=num_pods, - "Tot_CPU"=round(cputotal, 3), - "avg_CPU"=round(cputotal/num_pods, 4) - ) - cpustats=rbind(cpustats, local_cpus) - - # launch (boot) stats - local_boots = c( - "Test"=testname, - "n"=num_pods, - "median"=median(na.omit(local_bootdata)$launch_time)/1000, - "min"=min(na.omit(local_bootdata)$launch_time)/1000, - "max"=max(na.omit(local_bootdata)$launch_time)/1000, - "sd"=round(sd(na.omit(local_bootdata)$launch_time)/1000, 4) - ) - - bootstats=rbind(bootstats, local_boots) - - # inode stats - local_inodes = c( - "Test"=testname, - "n"=num_pods, - "Tot_inode"=round(inodetotal, 3), - "avg_inode"=round(inodetotal/num_pods, 4) - ) - inodestats=rbind(inodestats, local_inodes) } - - # And collect up our rows into our global table of all results - # These two tables *should* be the source of all the data we need to - # process and plot (apart from the stats....) - podbootdata=rbind(podbootdata, local_bootdata, make.row.names=FALSE) - cpuidledata=rbind(cpuidledata, cpu_idle_data) - memfreedata=rbind(memfreedata, mem_free_data) - inodefreedata=rbind(inodefreedata, inode_free_data) - ifpacketdata=rbind(ifpacketdata, interface_packets_data) - ifoctetdata=rbind(ifoctetdata, interface_octets_data) - ifdropdata=rbind(ifdropdata, interface_dropped_data) - iferrordata=rbind(iferrordata, interface_errors_data) } + + # Check we actually found some JSON results files. + if ( length(bootstats) == 0 ) { + cat("No results files found for rapid scaling tests\n\n") + return() + } + + # And then check we found at least some matching node collectd data. + if ( length(memfreedata) == 0 ) { + cat("No collectd data found for rapid scaling tests\n\n") + return() + } + + # It's nice to show the graphs in Gb, at least for any decent sized test + # run, so make a new column with that pre-divided data in it for us to use. + memfreedata$mem_free_gb = memfreedata$value/(1024*1024*1024) + # And show the boot times in seconds, not ms + podbootdata$launch_time_s = podbootdata$launch_time/1000.0 + + + ########### Output memory page ############## + mem_stats_plot = suppressWarnings(ggtexttable(data.frame(memstats), + theme=ttheme(base_size=10), + rows=NULL + )) + + mem_scale = (max(memfreedata$value) / (1024*1024*1024)) / max(podbootdata$n_pods) + mem_line_plot <- ggplot() + + geom_line(data=memfreedata, + aes(s_offset, mem_free_gb, colour=interaction(testname, node), + group=interaction(testname, node)), + alpha=0.3) + + geom_point(data=memfreedata, + aes(s_offset, mem_free_gb, colour=interaction(testname, node), + group=interaction(testname, node)), + alpha=0.5, size=0.5) + + geom_line(data=podbootdata, + aes(x=s_offset, y=n_pods*mem_scale, colour=interaction(testname,"pod count"), group=testname), + alpha=0.2) + + geom_point(data=podbootdata, + aes(x=s_offset, y=n_pods*mem_scale, colour=interaction(testname,"pod count"), group=testname), + alpha=0.3, size=0.5) + + labs(colour="") + + xlab("seconds") + + ylab("System Avail (Gb)") + + scale_y_continuous(labels=comma, sec.axis=sec_axis(~ ./mem_scale, name="pods")) + + ggtitle("System Memory free") + + theme(legend.position="bottom") + + theme(axis.text.x=element_text(angle=90)) + + page1 = grid.arrange( + mem_line_plot, + mem_stats_plot, + ncol=1 + ) + + # pagebreak, as the graphs overflow the page otherwise + cat("\n\n\\pagebreak\n") + + ########## Output cpu page ############## + cpu_stats_plot = suppressWarnings(ggtexttable(data.frame(cpustats), + theme=ttheme(base_size=10), + rows=NULL + )) + + cpu_scale = max(cpuidledata$value) / max(podbootdata$n_pods) + cpu_line_plot <- ggplot() + + geom_line(data=cpuidledata, + aes(x=s_offset, y=value, colour=interaction(testname, node), + group=interaction(testname, node)), + alpha=0.3) + + geom_point(data=cpuidledata, + aes(x=s_offset, y=value, colour=interaction(testname, node), + group=interaction(testname, node)), + alpha=0.5, size=0.5) + + geom_line(data=podbootdata, + aes(x=s_offset, y=n_pods*cpu_scale, colour=interaction(testname,"pod count"), group=testname), + alpha=0.2) + + geom_point(data=podbootdata, + aes(x=s_offset, y=n_pods*cpu_scale, colour=interaction(testname,"pod count"), group=testname), + alpha=0.3, size=0.5) + + labs(colour="") + + scale_y_continuous(labels=comma, sec.axis=sec_axis(~ ./cpu_scale, name="pods")) + + xlab("seconds") + + ylab("System CPU Idle (%)") + + ggtitle("System CPU usage") + + theme(legend.position="bottom") + + theme(axis.text.x=element_text(angle=90)) + + page2 = grid.arrange( + cpu_line_plot, + cpu_stats_plot, + ncol=1 + ) + + # pagebreak, as the graphs overflow the page otherwise + cat("\n\n\\pagebreak\n") + + ########## Output boot page ############## + boot_stats_plot = suppressWarnings(ggtexttable(data.frame(bootstats), + theme=ttheme(base_size=10), + rows=NULL + )) + + boot_line_plot <- ggplot() + + geom_line(data=podbootdata, + aes(n_pods, launch_time_s, colour=testname, group=testname), + alpha=0.2) + + xlab("pods") + + ylab("Boot time (s)") + + ggtitle("Pod boot time") + + theme(legend.position="bottom") + + theme(axis.text.x=element_text(angle=90)) + + page3 = grid.arrange( + boot_line_plot, + boot_stats_plot, + ncol=1 + ) + + # pagebreak, as the graphs overflow the page otherwise + cat("\n\n\\pagebreak\n") + + ########## Output inode page ############## + inode_stats_plot = suppressWarnings(ggtexttable(data.frame(inodestats), + theme=ttheme(base_size=10), + rows=NULL + )) + + inode_scale = max(inodefreedata$value) / max(podbootdata$n_pods) + inode_line_plot <- ggplot() + + geom_line(data=inodefreedata, + aes(x=s_offset, y=value, colour=interaction(testname, node), + group=interaction(testname, node)), + alpha=0.2) + + geom_point(data=inodefreedata, + aes(x=s_offset, y=value, colour=interaction(testname, node), + group=interaction(testname, node)), + alpha=0.5, size=0.5) + + geom_line(data=podbootdata, + aes(x=s_offset, y=n_pods*inode_scale, colour=interaction(testname,"pod count"), group=testname), + alpha=0.2) + + geom_point(data=podbootdata, + aes(x=s_offset, y=n_pods*inode_scale, colour=interaction(testname,"pod count"), group=testname), + alpha=0.3, size=0.5) + + labs(colour="") + + xlab("seconds") + + ylab("inodes free") + + scale_y_continuous(labels=comma, sec.axis=sec_axis(~ ./inode_scale, name="pods")) + + ggtitle("inodes free") + + theme(legend.position="bottom") + + theme(axis.text.x=element_text(angle=90)) + + page4 = grid.arrange( + inode_line_plot, + inode_stats_plot, + ncol=1 + ) + + # pagebreak, as the graphs overflow the page otherwise + cat("\n\n\\pagebreak\n") + + ########## Output interface page packets and octets ############## + ip_scale = max(c(max(ifpacketdata$tx, na.rm=TRUE), + max(ifpacketdata$rx, na.rm=TRUE))) / max(podbootdata$n_pods) + interface_packet_line_plot <- ggplot() + + geom_line(data=ifpacketdata, + aes(x=s_offset, y=tx, colour=interaction(testname, node, name, "tx"), + group=interaction(testname, node, name, "tx")), + alpha=0.2, na.rm=TRUE) + + geom_point(data=ifpacketdata, + aes(x=s_offset, y=tx, colour=interaction(testname, node, name, "tx"), + group=interaction(testname, node, name, "tx")), + alpha=0.5, size=0.5, na.rm=TRUE) + + geom_line(data=ifpacketdata, + aes(x=s_offset, y=rx, colour=interaction(testname, node, name, "rx"), + group=interaction(testname, node, name, "rx")), + alpha=0.2, na.rm=TRUE) + + geom_point(data=ifpacketdata, + aes(x=s_offset, y=rx, colour=interaction(testname, node, name, "rx"), + group=interaction(testname, node, name, "rx")), + alpha=0.5, size=0.5, na.rm=TRUE) + + geom_line(data=podbootdata, + aes(x=s_offset, y=n_pods*ip_scale, colour=interaction(testname,"pod count"), group=testname), + alpha=0.2) + + geom_point(data=podbootdata, + aes(x=s_offset, y=n_pods*ip_scale, colour=interaction(testname,"pod count"), group=testname), + alpha=0.3, size=0.5) + + labs(colour="") + + xlab("seconds") + + ylab("packets") + + scale_y_continuous(labels=comma, sec.axis=sec_axis(~ ./ip_scale, name="pods")) + + ggtitle("interface packets") + + theme(legend.position="bottom") + + theme(axis.text.x=element_text(angle=90)) + + oct_scale = max(c(max(ifoctetdata$tx, na.rm=TRUE), + max(ifoctetdata$rx, na.rm=TRUE))) / max(podbootdata$n_pods) + interface_octet_line_plot <- ggplot() + + geom_line(data=ifoctetdata, + aes(x=s_offset, y=tx, colour=interaction(testname, node, name, "tx"), + group=interaction(testname, node, name, "tx")), + alpha=0.2, na.rm=TRUE) + + geom_point(data=ifoctetdata, + aes(x=s_offset, y=tx, colour=interaction(testname, node, name, "tx"), + group=interaction(testname, node, name, "tx")), + alpha=0.5, size=0.5, na.rm=TRUE) + + geom_line(data=ifoctetdata, + aes(x=s_offset, y=rx, colour=interaction(testname, node, name, "rx"), + group=interaction(testname, node, name, "rx")), + alpha=0.2, na.rm=TRUE) + + geom_point(data=ifoctetdata, + aes(x=s_offset, y=rx, colour=interaction(testname, node, name, "rx"), + group=interaction(testname, node, name, "rx")), + alpha=0.5, size=0.5, na.rm=TRUE) + + geom_line(data=podbootdata, + aes(x=s_offset, y=n_pods*oct_scale, colour=interaction(testname,"pod count"), group=testname), + alpha=0.2) + + geom_point(data=podbootdata, + aes(x=s_offset, y=n_pods*oct_scale, colour=interaction(testname,"pod count"), group=testname), + alpha=0.3, size=0.5) + + labs(colour="") + + xlab("seconds") + + ylab("octets") + + scale_y_continuous(labels=comma, sec.axis=sec_axis(~ ./oct_scale, name="pods")) + + ggtitle("interface octets") + + theme(legend.position="bottom") + + theme(axis.text.x=element_text(angle=90)) + + page5 = grid.arrange( + interface_packet_line_plot, + interface_octet_line_plot, + ncol=1 + ) + + # pagebreak, as the graphs overflow the page otherwise + cat("\n\n\\pagebreak\n") + + ########## Output interface page drops and errors ############## + # drops are often 0, so providing 1 so we won't scale by infinity + drop_scale = max(c(1, + max(ifdropdata$tx, na.rm=TRUE), + max(ifdropdata$rx, na.rm=TRUE))) / max(podbootdata$n_pods) + interface_drop_line_plot <- ggplot() + + geom_line(data=ifdropdata, + aes(x=s_offset, y=tx, colour=interaction(testname, node, name, "tx"), + group=interaction(testname, node, name, "tx")), + alpha=0.2, na.rm=TRUE) + + geom_point(data=ifdropdata, + aes(x=s_offset, y=tx, colour=interaction(testname, node, name, "tx"), + group=interaction(testname, node, name, "tx")), + alpha=0.5, size=0.5, na.rm=TRUE) + + geom_line(data=ifdropdata, + aes(x=s_offset, y=rx, colour=interaction(testname, node, name, "rx"), + group=interaction(testname, node, name, "rx")), + alpha=0.2, na.rm=TRUE) + + geom_point(data=ifdropdata, + aes(x=s_offset, y=rx, colour=interaction(testname, node, name, "rx"), + group=interaction(testname, node, name, "rx")), + alpha=0.5, size=0.5, na.rm=TRUE) + + geom_line(data=podbootdata, + aes(x=s_offset, y=n_pods*drop_scale, colour=interaction(testname,"pod count"), group=testname), + alpha=0.2) + + geom_point(data=podbootdata, + aes(x=s_offset, y=n_pods*drop_scale, colour=interaction(testname,"pod count"), group=testname), + alpha=0.3, size=0.5) + + labs(colour="") + + xlab("seconds") + + ylab("drops") + + scale_y_continuous(breaks=pretty_breaks(), sec.axis=sec_axis(~ ./drop_scale, name="pods", labels=comma)) + + ggtitle("interface drops") + + theme(legend.position="bottom") + + theme(axis.text.x=element_text(angle=90)) + + # errors are often 0, so providing 1 so we won't scale by infinity + error_scale = max(c(1, + max(iferrordata$tx, na.rm=TRUE), + max(iferrordata$rx, na.rm=TRUE))) / max(podbootdata$n_pods) + interface_error_line_plot <- ggplot() + + geom_line(data=iferrordata, + aes(x=s_offset, y=tx, colour=interaction(testname, node, name, "tx"), + group=interaction(testname, node, name, name, "tx")), + alpha=0.2, na.rm=TRUE) + + geom_point(data=iferrordata, + aes(x=s_offset, y=tx, colour=interaction(testname, node, name, "tx"), + group=interaction(testname, node, name, "tx")), + alpha=0.5, size=0.5, na.rm=TRUE) + + geom_line(data=iferrordata, + aes(x=s_offset, y=rx, colour=interaction(testname, node, name, "rx"), + group=interaction(testname, node, name, "rx")), + alpha=0.2, na.rm=TRUE) + + geom_point(data=iferrordata, + aes(x=s_offset, y=rx, colour=interaction(testname, node, name, "rx"), + group=interaction(testname, node, name, "rx")), + alpha=0.5, size=0.5, na.rm=TRUE) + + geom_line(data=podbootdata, + aes(x=s_offset, y=n_pods*error_scale, colour=interaction(testname,"pod count"), group=testname), + alpha=0.2) + + geom_point(data=podbootdata, + aes(x=s_offset, y=n_pods*error_scale, colour=interaction(testname,"pod count"), group=testname), + alpha=0.3, size=0.5) + + labs(colour="") + + xlab("seconds") + + ylab("errors") + + scale_y_continuous(breaks=pretty_breaks(), sec.axis=sec_axis(~ ./error_scale, name="pods", labels=comma)) + + ggtitle("interface errors") + + theme(legend.position="bottom") + + theme(axis.text.x=element_text(angle=90)) + + page6 = grid.arrange( + interface_drop_line_plot, + interface_error_line_plot, + ncol=1 + ) } -# It's nice to show the graphs in Gb, at least for any decent sized test -# run, so make a new column with that pre-divided data in it for us to use. -memfreedata$mem_free_gb = memfreedata$value/(1024*1024*1024) -# And show the boot times in seconds, not ms -podbootdata$launch_time_s = podbootdata$launch_time/1000.0 - - -########### Output memory page ############## -mem_stats_plot = suppressWarnings(ggtexttable(data.frame(memstats), - theme=ttheme(base_size=10), - rows=NULL - )) - -mem_scale = (max(memfreedata$value) / (1024*1024*1024)) / max(podbootdata$n_pods) -mem_line_plot <- ggplot() + - geom_line(data=memfreedata, - aes(s_offset, mem_free_gb, colour=interaction(testname, node), - group=interaction(testname, node)), - alpha=0.3) + - geom_point(data=memfreedata, - aes(s_offset, mem_free_gb, colour=interaction(testname, node), - group=interaction(testname, node)), - alpha=0.5, size=0.5) + - geom_line(data=podbootdata, - aes(x=s_offset, y=n_pods*mem_scale, colour=interaction(testname,"pod count"), group=testname), - alpha=0.2) + - geom_point(data=podbootdata, - aes(x=s_offset, y=n_pods*mem_scale, colour=interaction(testname,"pod count"), group=testname), - alpha=0.3, size=0.5) + - labs(colour="") + - xlab("seconds") + - ylab("System Avail (Gb)") + - scale_y_continuous(labels=comma, sec.axis=sec_axis(~ ./mem_scale, name="pods")) + - ggtitle("System Memory free") + - theme(legend.position="bottom") + - theme(axis.text.x=element_text(angle=90)) - -page1 = grid.arrange( - mem_line_plot, - mem_stats_plot, - ncol=1 - ) - -# pagebreak, as the graphs overflow the page otherwise -cat("\n\n\\pagebreak\n") - -########## Output cpu page ############## -cpu_stats_plot = suppressWarnings(ggtexttable(data.frame(cpustats), - theme=ttheme(base_size=10), - rows=NULL - )) - -cpu_scale = max(cpuidledata$value) / max(podbootdata$n_pods) -cpu_line_plot <- ggplot() + - geom_line(data=cpuidledata, - aes(x=s_offset, y=value, colour=interaction(testname, node), - group=interaction(testname, node)), - alpha=0.3) + - geom_point(data=cpuidledata, - aes(x=s_offset, y=value, colour=interaction(testname, node), - group=interaction(testname, node)), - alpha=0.5, size=0.5) + - geom_line(data=podbootdata, - aes(x=s_offset, y=n_pods*cpu_scale, colour=interaction(testname,"pod count"), group=testname), - alpha=0.2) + - geom_point(data=podbootdata, - aes(x=s_offset, y=n_pods*cpu_scale, colour=interaction(testname,"pod count"), group=testname), - alpha=0.3, size=0.5) + - labs(colour="") + - scale_y_continuous(labels=comma, sec.axis=sec_axis(~ ./cpu_scale, name="pods")) + - xlab("seconds") + - ylab("System CPU Idle (%)") + - ggtitle("System CPU usage") + - theme(legend.position="bottom") + - theme(axis.text.x=element_text(angle=90)) - -page2 = grid.arrange( - cpu_line_plot, - cpu_stats_plot, - ncol=1 - ) - -# pagebreak, as the graphs overflow the page otherwise -cat("\n\n\\pagebreak\n") - -########## Output boot page ############## -boot_stats_plot = suppressWarnings(ggtexttable(data.frame(bootstats), - theme=ttheme(base_size=10), - rows=NULL - )) - -boot_line_plot <- ggplot() + - geom_line(data=podbootdata, - aes(n_pods, launch_time_s, colour=testname, group=testname), - alpha=0.2) + - xlab("pods") + - ylab("Boot time (s)") + - ggtitle("Pod boot time") + - theme(legend.position="bottom") + - theme(axis.text.x=element_text(angle=90)) - -page3 = grid.arrange( - boot_line_plot, - boot_stats_plot, - ncol=1 - ) - -# pagebreak, as the graphs overflow the page otherwise -cat("\n\n\\pagebreak\n") - -########## Output inode page ############## -inode_stats_plot = suppressWarnings(ggtexttable(data.frame(inodestats), - theme=ttheme(base_size=10), - rows=NULL - )) - -inode_scale = max(inodefreedata$value) / max(podbootdata$n_pods) -inode_line_plot <- ggplot() + - geom_line(data=inodefreedata, - aes(x=s_offset, y=value, colour=interaction(testname, node), - group=interaction(testname, node)), - alpha=0.2) + - geom_point(data=inodefreedata, - aes(x=s_offset, y=value, colour=interaction(testname, node), - group=interaction(testname, node)), - alpha=0.5, size=0.5) + - geom_line(data=podbootdata, - aes(x=s_offset, y=n_pods*inode_scale, colour=interaction(testname,"pod count"), group=testname), - alpha=0.2) + - geom_point(data=podbootdata, - aes(x=s_offset, y=n_pods*inode_scale, colour=interaction(testname,"pod count"), group=testname), - alpha=0.3, size=0.5) + - labs(colour="") + - xlab("seconds") + - ylab("inodes free") + - scale_y_continuous(labels=comma, sec.axis=sec_axis(~ ./inode_scale, name="pods")) + - ggtitle("inodes free") + - theme(legend.position="bottom") + - theme(axis.text.x=element_text(angle=90)) - -page4 = grid.arrange( - inode_line_plot, - inode_stats_plot, - ncol=1 - ) - -# pagebreak, as the graphs overflow the page otherwise -cat("\n\n\\pagebreak\n") - -########## Output interface page packets and octets ############## -ip_scale = max(c(max(ifpacketdata$tx, na.rm=TRUE), - max(ifpacketdata$rx, na.rm=TRUE))) / max(podbootdata$n_pods) -interface_packet_line_plot <- ggplot() + - geom_line(data=ifpacketdata, - aes(x=s_offset, y=tx, colour=interaction(testname, node, name, "tx"), - group=interaction(testname, node, name, "tx")), - alpha=0.2, na.rm=TRUE) + - geom_point(data=ifpacketdata, - aes(x=s_offset, y=tx, colour=interaction(testname, node, name, "tx"), - group=interaction(testname, node, name, "tx")), - alpha=0.5, size=0.5, na.rm=TRUE) + - geom_line(data=ifpacketdata, - aes(x=s_offset, y=rx, colour=interaction(testname, node, name, "rx"), - group=interaction(testname, node, name, "rx")), - alpha=0.2, na.rm=TRUE) + - geom_point(data=ifpacketdata, - aes(x=s_offset, y=rx, colour=interaction(testname, node, name, "rx"), - group=interaction(testname, node, name, "rx")), - alpha=0.5, size=0.5, na.rm=TRUE) + - geom_line(data=podbootdata, - aes(x=s_offset, y=n_pods*ip_scale, colour=interaction(testname,"pod count"), group=testname), - alpha=0.2) + - geom_point(data=podbootdata, - aes(x=s_offset, y=n_pods*ip_scale, colour=interaction(testname,"pod count"), group=testname), - alpha=0.3, size=0.5) + - labs(colour="") + - xlab("seconds") + - ylab("packets") + - scale_y_continuous(labels=comma, sec.axis=sec_axis(~ ./ip_scale, name="pods")) + - ggtitle("interface packets") + - theme(legend.position="bottom") + - theme(axis.text.x=element_text(angle=90)) - -oct_scale = max(c(max(ifoctetdata$tx, na.rm=TRUE), - max(ifoctetdata$rx, na.rm=TRUE))) / max(podbootdata$n_pods) -interface_octet_line_plot <- ggplot() + - geom_line(data=ifoctetdata, - aes(x=s_offset, y=tx, colour=interaction(testname, node, name, "tx"), - group=interaction(testname, node, name, "tx")), - alpha=0.2, na.rm=TRUE) + - geom_point(data=ifoctetdata, - aes(x=s_offset, y=tx, colour=interaction(testname, node, name, "tx"), - group=interaction(testname, node, name, "tx")), - alpha=0.5, size=0.5, na.rm=TRUE) + - geom_line(data=ifoctetdata, - aes(x=s_offset, y=rx, colour=interaction(testname, node, name, "rx"), - group=interaction(testname, node, name, "rx")), - alpha=0.2, na.rm=TRUE) + - geom_point(data=ifoctetdata, - aes(x=s_offset, y=rx, colour=interaction(testname, node, name, "rx"), - group=interaction(testname, node, name, "rx")), - alpha=0.5, size=0.5, na.rm=TRUE) + - geom_line(data=podbootdata, - aes(x=s_offset, y=n_pods*oct_scale, colour=interaction(testname,"pod count"), group=testname), - alpha=0.2) + - geom_point(data=podbootdata, - aes(x=s_offset, y=n_pods*oct_scale, colour=interaction(testname,"pod count"), group=testname), - alpha=0.3, size=0.5) + - labs(colour="") + - xlab("seconds") + - ylab("octets") + - scale_y_continuous(labels=comma, sec.axis=sec_axis(~ ./oct_scale, name="pods")) + - ggtitle("interface octets") + - theme(legend.position="bottom") + - theme(axis.text.x=element_text(angle=90)) - -page5 = grid.arrange( - interface_packet_line_plot, - interface_octet_line_plot, - ncol=1 - ) - -# pagebreak, as the graphs overflow the page otherwise -cat("\n\n\\pagebreak\n") - -########## Output interface page drops and errors ############## -# drops are often 0, so providing 1 so we won't scale by infinity -drop_scale = max(c(1, - max(ifdropdata$tx, na.rm=TRUE), - max(ifdropdata$rx, na.rm=TRUE))) / max(podbootdata$n_pods) -interface_drop_line_plot <- ggplot() + - geom_line(data=ifdropdata, - aes(x=s_offset, y=tx, colour=interaction(testname, node, name, "tx"), - group=interaction(testname, node, name, "tx")), - alpha=0.2, na.rm=TRUE) + - geom_point(data=ifdropdata, - aes(x=s_offset, y=tx, colour=interaction(testname, node, name, "tx"), - group=interaction(testname, node, name, "tx")), - alpha=0.5, size=0.5, na.rm=TRUE) + - geom_line(data=ifdropdata, - aes(x=s_offset, y=rx, colour=interaction(testname, node, name, "rx"), - group=interaction(testname, node, name, "rx")), - alpha=0.2, na.rm=TRUE) + - geom_point(data=ifdropdata, - aes(x=s_offset, y=rx, colour=interaction(testname, node, name, "rx"), - group=interaction(testname, node, name, "rx")), - alpha=0.5, size=0.5, na.rm=TRUE) + - geom_line(data=podbootdata, - aes(x=s_offset, y=n_pods*drop_scale, colour=interaction(testname,"pod count"), group=testname), - alpha=0.2) + - geom_point(data=podbootdata, - aes(x=s_offset, y=n_pods*drop_scale, colour=interaction(testname,"pod count"), group=testname), - alpha=0.3, size=0.5) + - labs(colour="") + - xlab("seconds") + - ylab("drops") + - scale_y_continuous(breaks=pretty_breaks(), sec.axis=sec_axis(~ ./drop_scale, name="pods", labels=comma)) + - ggtitle("interface drops") + - theme(legend.position="bottom") + - theme(axis.text.x=element_text(angle=90)) - -# errors are often 0, so providing 1 so we won't scale by infinity -error_scale = max(c(1, - max(iferrordata$tx, na.rm=TRUE), - max(iferrordata$rx, na.rm=TRUE))) / max(podbootdata$n_pods) -interface_error_line_plot <- ggplot() + - geom_line(data=iferrordata, - aes(x=s_offset, y=tx, colour=interaction(testname, node, name, "tx"), - group=interaction(testname, node, name, name, "tx")), - alpha=0.2, na.rm=TRUE) + - geom_point(data=iferrordata, - aes(x=s_offset, y=tx, colour=interaction(testname, node, name, "tx"), - group=interaction(testname, node, name, "tx")), - alpha=0.5, size=0.5, na.rm=TRUE) + - geom_line(data=iferrordata, - aes(x=s_offset, y=rx, colour=interaction(testname, node, name, "rx"), - group=interaction(testname, node, name, "rx")), - alpha=0.2, na.rm=TRUE) + - geom_point(data=iferrordata, - aes(x=s_offset, y=rx, colour=interaction(testname, node, name, "rx"), - group=interaction(testname, node, name, "rx")), - alpha=0.5, size=0.5, na.rm=TRUE) + - geom_line(data=podbootdata, - aes(x=s_offset, y=n_pods*error_scale, colour=interaction(testname,"pod count"), group=testname), - alpha=0.2) + - geom_point(data=podbootdata, - aes(x=s_offset, y=n_pods*error_scale, colour=interaction(testname,"pod count"), group=testname), - alpha=0.3, size=0.5) + - labs(colour="") + - xlab("seconds") + - ylab("errors") + - scale_y_continuous(breaks=pretty_breaks(), sec.axis=sec_axis(~ ./error_scale, name="pods", labels=comma)) + - ggtitle("interface errors") + - theme(legend.position="bottom") + - theme(axis.text.x=element_text(angle=90)) - -page6 = grid.arrange( - interface_drop_line_plot, - interface_error_line_plot, - ncol=1 - ) +render_collectd_scaling() diff --git a/metrics/report/report_dockerfile/dut-details.R b/metrics/report/report_dockerfile/dut-details.R index 7a707e5..bd4a23a 100755 --- a/metrics/report/report_dockerfile/dut-details.R +++ b/metrics/report/report_dockerfile/dut-details.R @@ -13,95 +13,105 @@ library(gridExtra) # together. suppressMessages(suppressWarnings(library(ggpubr))) # for ggtexttable. suppressMessages(library(jsonlite)) # to load the data. -# A list of all the known results files we might find the information inside. -resultsfiles=c( - "k8s-parallel.json", - "k8s-scaling.json", - "k8s-rapid.json" - ) +render_dut_details <- function() +{ + # A list of all the known results files we might find the information inside. + resultsfiles=c( + "k8s-parallel.json", + "k8s-scaling.json", + "k8s-rapid.json" + ) -data=c() -stats=c() -stats_names=c() + data=c() + stats=c() + stats_names=c() -# For each set of results -for (currentdir in resultdirs) { - count=1 - dirstats=c() - for (resultsfile in resultsfiles) { - fname=paste(inputdir, currentdir, resultsfile, sep="/") - if ( !file.exists(fname)) { - #warning(paste("Skipping non-existent file: ", fname)) - next - } - - # Derive the name from the test result dirname - datasetname=basename(currentdir) - - # Import the data - fdata=fromJSON(fname) - - if (length(fdata$'kubectl-version') != 0 ) { - # We have kata-runtime data - dirstats=tibble("Client Ver"=as.character(fdata$'kubectl-version'$clientVersion$gitVersion)) - dirstats=cbind(dirstats, "Server Ver"=as.character(fdata$'kubectl-version'$serverVersion$gitVersion)) - numnodes= nrow(fdata$'kubectl-get-nodes'$items) - dirstats=cbind(dirstats, "No. nodes"=as.character(numnodes)) - - if (numnodes != 0) { - first_node=fdata$'kubectl-get-nodes'$items[1,] - dirstats=cbind(dirstats, "- Node0 name"=as.character(first_node$metadata$name)) - - havekata=first_node$metadata$labels$'katacontainers.io/kata-runtime' - if ( is.null(havekata) ) { - dirstats=cbind(dirstats, " Have Kata"=as.character('false')) - } else { - dirstats=cbind(dirstats, " Have Kata"=as.character(havekata)) - } - - dirstats=cbind(dirstats, " CPUs"=as.character(first_node$status$capacity$cpu)) - dirstats=cbind(dirstats, " Memory"=as.character(first_node$status$capacity$memory)) - dirstats=cbind(dirstats, " MaxPods"=as.character(first_node$status$capacity$pods)) - dirstats=cbind(dirstats, " PodCIDR"=as.character(first_node$spec$podCIDR)) - - dirstats=cbind(dirstats, " runtime"=as.character(first_node$status$nodeInfo$containerRuntimeVersion)) - dirstats=cbind(dirstats, " kernel"=as.character(first_node$status$nodeInfo$kernelVersion)) - dirstats=cbind(dirstats, " kubeProxy"=as.character(first_node$status$nodeInfo$kubeProxyVersion)) - dirstats=cbind(dirstats, " Kubelet"=as.character(first_node$status$nodeInfo$kubeletVersion)) - dirstats=cbind(dirstats, " OS"=as.character(first_node$status$nodeInfo$osImage)) + # For each set of results + for (currentdir in resultdirs) { + count=1 + dirstats=c() + datasetname=c() + for (resultsfile in resultsfiles) { + fname=paste(inputdir, currentdir, resultsfile, sep="/") + if ( !file.exists(fname)) { + #warning(paste("Skipping non-existent file: ", fname)) + next } - break + # Derive the name from the test result dirname + datasetname=basename(currentdir) + + # Import the data + fdata=fromJSON(fname) + + if (length(fdata$'kubectl-version') != 0 ) { + # We have kata-runtime data + dirstats=tibble("Client Ver"=as.character(fdata$'kubectl-version'$clientVersion$gitVersion)) + dirstats=cbind(dirstats, "Server Ver"=as.character(fdata$'kubectl-version'$serverVersion$gitVersion)) + numnodes= nrow(fdata$'kubectl-get-nodes'$items) + dirstats=cbind(dirstats, "No. nodes"=as.character(numnodes)) + + if (numnodes != 0) { + first_node=fdata$'kubectl-get-nodes'$items[1,] + dirstats=cbind(dirstats, "- Node0 name"=as.character(first_node$metadata$name)) + + havekata=first_node$metadata$labels$'katacontainers.io/kata-runtime' + if ( is.null(havekata) ) { + dirstats=cbind(dirstats, " Have Kata"=as.character('false')) + } else { + dirstats=cbind(dirstats, " Have Kata"=as.character(havekata)) + } + + dirstats=cbind(dirstats, " CPUs"=as.character(first_node$status$capacity$cpu)) + dirstats=cbind(dirstats, " Memory"=as.character(first_node$status$capacity$memory)) + dirstats=cbind(dirstats, " MaxPods"=as.character(first_node$status$capacity$pods)) + dirstats=cbind(dirstats, " PodCIDR"=as.character(first_node$spec$podCIDR)) + + dirstats=cbind(dirstats, " runtime"=as.character(first_node$status$nodeInfo$containerRuntimeVersion)) + dirstats=cbind(dirstats, " kernel"=as.character(first_node$status$nodeInfo$kernelVersion)) + dirstats=cbind(dirstats, " kubeProxy"=as.character(first_node$status$nodeInfo$kubeProxyVersion)) + dirstats=cbind(dirstats, " Kubelet"=as.character(first_node$status$nodeInfo$kubeletVersion)) + dirstats=cbind(dirstats, " OS"=as.character(first_node$status$nodeInfo$osImage)) + } + + break + } } + + if ( length(dirstats) == 0 ) { + cat(paste("No valid data found for directory ", currentdir, "\n\n")) + } + + # use plyr rbind.fill so we can combine disparate version info frames + stats=rbind.fill(stats, dirstats) + stats_names=rbind(stats_names, datasetname) } - if ( length(dirstats) == 0 ) { - warning(paste("No valid data found for directory ", currentdir)) + if ( length(stats_names) == 0 ) { + cat("No system details found\n\n") + return() } - # use plyr rbind.fill so we can combine disparate version info frames - stats=rbind.fill(stats, dirstats) - stats_names=rbind(stats_names, datasetname) + rownames(stats) = stats_names + + # Rotate the tibble so we get data dirs as the columns + spun_stats = as_tibble(cbind(What=names(stats), t(stats))) + + # Build us a text table of numerical results + # Set up as left hand justify, so the node data indent renders. + tablefontsize=8 + tbody.style = tbody_style(hjust=0, x=0.1, size=tablefontsize) + stats_plot = suppressWarnings(ggtexttable(data.frame(spun_stats, check.names=FALSE), + theme=ttheme(base_size=tablefontsize, tbody.style=tbody.style), + rows=NULL + )) + + # It may seem odd doing a grid of 1x1, but it should ensure we get a uniform format and + # layout to match the other charts and tables in the report. + master_plot = grid.arrange( + stats_plot, + nrow=1, + ncol=1 ) } -rownames(stats) = stats_names - -# Rotate the tibble so we get data dirs as the columns -spun_stats = as_tibble(cbind(What=names(stats), t(stats))) - -# Build us a text table of numerical results -# Set up as left hand justify, so the node data indent renders. -tablefontsize=8 -tbody.style = tbody_style(hjust=0, x=0.1, size=tablefontsize) -stats_plot = suppressWarnings(ggtexttable(data.frame(spun_stats, check.names=FALSE), - theme=ttheme(base_size=tablefontsize, tbody.style=tbody.style), - rows=NULL - )) - -# It may seem odd doing a grid of 1x1, but it should ensure we get a uniform format and -# layout to match the other charts and tables in the report. -master_plot = grid.arrange( - stats_plot, - nrow=1, - ncol=1 ) - +render_dut_details() diff --git a/metrics/report/report_dockerfile/elasticsearchr.R b/metrics/report/report_dockerfile/elasticsearchr.R new file mode 100644 index 0000000..9a55e48 --- /dev/null +++ b/metrics/report/report_dockerfile/elasticsearchr.R @@ -0,0 +1,26 @@ + +library('elasticsearchr') + +for_scaling <- query('{ +"bool": { + "must": [ + { "match": + { + "test.testname": "k8s scaling" + } + } + ] +} +}') + +these_fields <- select_fields('{ + "includes": [ + "date.Date", + "k8s-scaling.BootResults.launch_time.Result", + "k8s-scaling.BootResults.n_pods.Result" + ] +}') + +sort_by_date <- sort_on('[{"date.Date": {"order": "asc"}}]') + +x=elastic("http://192.168.0.111:9200", "logtest") %search% (for_scaling + sort_by_date + these_fields) diff --git a/metrics/report/report_dockerfile/metrics_report.Rmd b/metrics/report/report_dockerfile/metrics_report.Rmd index 630365a..e715ad1 100644 --- a/metrics/report/report_dockerfile/metrics_report.Rmd +++ b/metrics/report/report_dockerfile/metrics_report.Rmd @@ -32,7 +32,7 @@ This [test](https://github.com/clearlinux/cloud-native-setup/metrics/scaling/k8s measures the time taken to launch and delete pods in parallel using a deployment. The times are how long it takes for the whole deployment operation to complete. -```{r parallel, echo=FALSE, fig.cap="K8S parallel pods"} +```{r parallel, echo=FALSE, fig.cap="K8S parallel pods", results='asis'} source('parallel.R') ``` @@ -57,7 +57,7 @@ This table describes the test system details, as derived from the information co in the test results files. -```{r dut, echo=FALSE, fig.cap="System configuration details"} +```{r dut, echo=FALSE, fig.cap="System configuration details", results='asis'} source('dut-details.R') ``` @@ -67,6 +67,6 @@ source('dut-details.R') This table describes node details within the Kubernetes cluster that have been used for test. -```{r node, echo=FALSE, fig.cap="Node information within Kubernetes cluster"} +```{r node, echo=FALSE, fig.cap="Node information within Kubernetes cluster", results='asis'} source('node-info.R') ``` diff --git a/metrics/report/report_dockerfile/node-info.R b/metrics/report/report_dockerfile/node-info.R index eeaa9f8..9f98a84 100755 --- a/metrics/report/report_dockerfile/node-info.R +++ b/metrics/report/report_dockerfile/node-info.R @@ -13,83 +13,96 @@ library(gridExtra) # together. suppressMessages(suppressWarnings(library(ggpubr))) # for ggtexttable. suppressMessages(library(jsonlite)) # to load the data. -# A list of all the known results files we might find the information inside. -resultsfiles=c( - "k8s-scaling.json" - ) +render_node_info <- function() +{ + # A list of all the known results files we might find the information inside. + resultsfiles=c( + "k8s-scaling.json" + ) -stats=c() -stats_names=c() -max_char_name_node=18 + stats=c() + stats_names=c() + datasetname=c() + complete_data=c() + max_char_name_node=18 -# list for each dirstats -dirstats_list=list() -j=1 + # list for each dirstats + dirstats_list=list() + j=1 -# For each set of results -for (currentdir in resultdirs) { - dirstats=c() - for (resultsfile in resultsfiles) { - fname=paste(inputdir, currentdir, resultsfile, sep="/") - if ( !file.exists(fname)) { - next - } - - # Derive the name from the test result dirname - datasetname=basename(currentdir) - - # Import the data - fdata=fromJSON(fname) - - if (length(fdata$'kubectl-version') != 0 ) { - numnodes= nrow(fdata$'kubectl-get-nodes'$items) - for (i in 1:numnodes) { - node_i=fdata$'kubectl-get-nodes'$items[i,] - node_info=fdata$'socketsPerNode'[i,] - - # Substring node name so it fits properly into final table - node_name=node_i$metadata$name - if ( nchar(node_name) >= max_char_name_node) { - dirstats=tibble("Node \nname"=as.character(substring(node_name, 1, max_char_name_node))) - } else { - dirstats=tibble("Node \nname"=as.character(node_name)) - } - - dirstats=cbind(dirstats, "CPUs"=as.character(node_i$status$capacity$cpu)) - dirstats=cbind(dirstats, "Memory"=as.character(node_i$status$capacity$memory)) - dirstats=cbind(dirstats, "Max \nPods"=as.character(node_i$status$capacity$pods)) - dirstats=cbind(dirstats, "Count \nsockets"=as.character(node_info$num_sockets)) - dirstats=cbind(dirstats, "Have \nhypervisor"=as.character(node_info$hypervisor)) - - dirstats=cbind(dirstats, "kernel"=as.character(node_i$status$nodeInfo$kernelVersion)) - dirstats=cbind(dirstats, "OS"=as.character(node_i$status$nodeInfo$osImage)) - dirstats=cbind(dirstats, "Test"=as.character(datasetname)) - - dirstats_list[[j]]=dirstats - j=j+1 + # For each set of results + for (currentdir in resultdirs) { + dirstats=c() + for (resultsfile in resultsfiles) { + fname=paste(inputdir, currentdir, resultsfile, sep="/") + if ( !file.exists(fname)) { + next + } + + # Derive the name from the test result dirname + datasetname=basename(currentdir) + + # Import the data + fdata=fromJSON(fname) + + if (length(fdata$'kubectl-version') != 0 ) { + numnodes= nrow(fdata$'kubectl-get-nodes'$items) + for (i in 1:numnodes) { + node_i=fdata$'kubectl-get-nodes'$items[i,] + node_info=fdata$'socketsPerNode'[i,] + + # Substring node name so it fits properly into final table + node_name=node_i$metadata$name + if ( nchar(node_name) >= max_char_name_node) { + dirstats=tibble("Node \nname"=as.character(substring(node_name, 1, max_char_name_node))) + } else { + dirstats=tibble("Node \nname"=as.character(node_name)) + } + + dirstats=cbind(dirstats, "CPUs"=as.character(node_i$status$capacity$cpu)) + dirstats=cbind(dirstats, "Memory"=as.character(node_i$status$capacity$memory)) + dirstats=cbind(dirstats, "Max \nPods"=as.character(node_i$status$capacity$pods)) + dirstats=cbind(dirstats, "Count \nsockets"=as.character(node_info$num_sockets)) + dirstats=cbind(dirstats, "Have \nhypervisor"=as.character(node_info$hypervisor)) + + dirstats=cbind(dirstats, "kernel"=as.character(node_i$status$nodeInfo$kernelVersion)) + dirstats=cbind(dirstats, "OS"=as.character(node_i$status$nodeInfo$osImage)) + dirstats=cbind(dirstats, "Test"=as.character(datasetname)) + + dirstats_list[[j]]=dirstats + j=j+1 + } + complete_data = do.call(rbind, dirstats_list) } - complete_data = do.call(rbind, dirstats_list) } + + if ( length(complete_data) == 0 ) { + cat(paste("No valid data found for directory ", currentdir, "\n\n")) + } + + # use plyr rbind.fill so we can combine disparate version info frames + stats=rbind.fill(stats, complete_data) + stats_names=rbind(stats_names, datasetname) } - if ( length(complete_data) == 0 ) { - warning(paste("No valid data found for directory ", currentdir)) + if ( length(stats_names) == 0 ) { + cat("No node stats found\n\n"); + return() } - # use plyr rbind.fill so we can combine disparate version info frames - stats=rbind.fill(stats, complete_data) - stats_names=rbind(stats_names, datasetname) + # Build us a text table of numerical results + # Set up as left hand justify, so the node data indent renders. + tablefontsize=8 + tbody.style = tbody_style(hjust=0, x=0.1, size=tablefontsize) + stats_plot = suppressWarnings(ggtexttable(data.frame(complete_data, check.names=FALSE), + theme=ttheme(base_size=tablefontsize, tbody.style=tbody.style), + rows=NULL)) + + # It may seem odd doing a grid of 1x1, but it should ensure we get a uniform format and + # layout to match the other charts and tables in the report. + master_plot = grid.arrange(stats_plot, + nrow=1, + ncol=1 ) } -# Build us a text table of numerical results -# Set up as left hand justify, so the node data indent renders. -tablefontsize=8 -tbody.style = tbody_style(hjust=0, x=0.1, size=tablefontsize) -stats_plot = suppressWarnings(ggtexttable(data.frame(complete_data, check.names=FALSE), - theme=ttheme(base_size=tablefontsize, tbody.style=tbody.style), - rows=NULL)) -# It may seem odd doing a grid of 1x1, but it should ensure we get a uniform format and -# layout to match the other charts and tables in the report. -master_plot = grid.arrange(stats_plot, - nrow=1, - ncol=1 ) +render_node_info() diff --git a/metrics/report/report_dockerfile/parallel.R b/metrics/report/report_dockerfile/parallel.R index 7a778d9..a6d03ba 100755 --- a/metrics/report/report_dockerfile/parallel.R +++ b/metrics/report/report_dockerfile/parallel.R @@ -13,113 +13,123 @@ suppressMessages(suppressWarnings(library(ggpubr))) # for ggtexttable. suppressMessages(library(jsonlite)) # to load the data. suppressMessages(library(scales)) # For de-science notation of axis -testnames=c( - "k8s-parallel*" -) +render_parallel <- function() +{ + testnames=c( + "k8s-parallel*" + ) -data=c() -stats=c() -rstats=c() -rstats_names=c() -cstats=c() -cstats_names=c() + data=c() + stats=c() + rstats=c() + rstats_names=c() + cstats=c() + cstats_names=c() -skip_points_enable_smooth=0 # Should we draw the points as well as lines on the graphs. + skip_points_enable_smooth=0 # Should we draw the points as well as lines on the graphs. -for (currentdir in resultdirs) { - dirstats=c() - for (testname in testnames) { - matchdir=paste(inputdir, currentdir, sep="") - matchfile=paste(testname, '\\.json', sep="") - files=list.files(matchdir, pattern=matchfile) - if ( length(files) == 0 ) { - #warning(paste("Pattern [", matchdir, "/", matchfile, "] matched nothing")) - } - for (ffound in files) { - fname=paste(inputdir, currentdir, ffound, sep="") - if ( !file.exists(fname)) { - warning(paste("Skipping non-existent file: ", fname)) - next + for (currentdir in resultdirs) { + dirstats=c() + for (testname in testnames) { + matchdir=paste(inputdir, currentdir, sep="") + matchfile=paste(testname, '\\.json', sep="") + files=list.files(matchdir, pattern=matchfile) + if ( length(files) == 0 ) { + #warning(paste("Pattern [", matchdir, "/", matchfile, "] matched nothing")) } + for (ffound in files) { + fname=paste(inputdir, currentdir, ffound, sep="") + if ( !file.exists(fname)) { + warning(paste("Skipping non-existent file: ", fname)) + next + } - # Derive the name from the test result dirname - datasetname=basename(currentdir) + # Derive the name from the test result dirname + datasetname=basename(currentdir) - # Import the data - fdata=fromJSON(fname) - # De-nest the test name specific data - shortname=substr(ffound, 1, nchar(ffound)-nchar(".json")) - fdata=fdata[[shortname]] + # Import the data + fdata=fromJSON(fname) + # De-nest the test name specific data + shortname=substr(ffound, 1, nchar(ffound)-nchar(".json")) + fdata=fdata[[shortname]] - testname=datasetname + testname=datasetname - # convert ms to seconds - cdata=data.frame(boot_time=as.numeric(fdata$BootResults$launch_time$Result)/1000) - cdata=cbind(cdata, delete_time=as.numeric(fdata$BootResults$delete_time$Result)/1000) - cdata=cbind(cdata, npod=as.numeric(fdata$BootResults$n_pods$Result)) + # convert ms to seconds + cdata=data.frame(boot_time=as.numeric(fdata$BootResults$launch_time$Result)/1000) + cdata=cbind(cdata, delete_time=as.numeric(fdata$BootResults$delete_time$Result)/1000) + cdata=cbind(cdata, npod=as.numeric(fdata$BootResults$n_pods$Result)) - # If we have more than 20 items to draw, then do not draw the points on - # the graphs, as they are then too noisy to read. - # But, do draw the smoothed lines to help read the now dense and potentially - # noisy graphs. - if (length(cdata[, "boot_time"]) > 20) { - skip_points_enable_smooth=1 + # If we have more than 20 items to draw, then do not draw the points on + # the graphs, as they are then too noisy to read. + # But, do draw the smoothed lines to help read the now dense and potentially + # noisy graphs. + if (length(cdata[, "boot_time"]) > 20) { + skip_points_enable_smooth=1 + } + + cdata=cbind(cdata, testname=rep(testname, length(cdata[, "boot_time"]) )) + cdata=cbind(cdata, dataset=rep(datasetname, length(cdata[, "boot_time"]) )) + + # Store away as a single set + data=rbind(data, cdata) } - - cdata=cbind(cdata, testname=rep(testname, length(cdata[, "boot_time"]) )) - cdata=cbind(cdata, dataset=rep(datasetname, length(cdata[, "boot_time"]) )) - - # Store away as a single set - data=rbind(data, cdata) } } + + # If we found nothing to process, quit early and nicely + if ( length(data) == 0 ) { + cat("No results files found for parallel tests\n\n") + return() + } + + # Show how boot time changed + boot_line_plot <- ggplot( data=data, aes(npod, boot_time, colour=testname, group=dataset)) + + geom_line( alpha=0.2) + + xlab("parallel pods") + + ylab("Boot time (s)") + + ggtitle("Deployment boot time (detail)") + + #ylim(0, NA) + # For big machines, better to not 0-index + theme(axis.text.x=element_text(angle=90)) + + if ( skip_points_enable_smooth == 0 ) { + boot_line_plot = boot_line_plot + geom_point(alpha=0.3) + } else { + boot_line_plot = bool_line_plot + geom_smooth(se=FALSE, method="loess", size=0.3) + } + + # And get a zero Y index plot. + boot_line_plot_zero = boot_line_plot + ylim(0, NA) + + ggtitle("Deployment boot time (0 index)") + + # Show how boot time changed + delete_line_plot <- ggplot( data=data, aes(npod, delete_time, colour=testname, group=dataset)) + + geom_line(alpha=0.2) + + xlab("parallel pods") + + ylab("Delete time (s)") + + ggtitle("Deployment deletion time (detail)") + + #ylim(0, NA) + # For big machines, better to not 0-index + theme(axis.text.x=element_text(angle=90)) + + if ( skip_points_enable_smooth == 0 ) { + delete_line_plot = delete_line_plot + geom_point(alpha=0.3) + } else { + delete_line_plot = delete_line_plot + geom_smooth(se=FALSE, method="loess", size=0.3) + } + + # And get a 0 indexed Y axis plot + delete_line_plot_zero = delete_line_plot + ylim(0, NA) + + ggtitle("Deployment deletion time (0 index)") + + # See https://www.r-bloggers.com/ggplot2-easy-way-to-mix-multiple-graphs-on-the-same-page/ for + # excellent examples + master_plot = grid.arrange( + boot_line_plot_zero, + delete_line_plot_zero, + boot_line_plot, + delete_line_plot, + nrow=2, + ncol=2 ) } -# Show how boot time changed -boot_line_plot <- ggplot( data=data, aes(npod, boot_time, colour=testname, group=dataset)) + - geom_line( alpha=0.2) + - xlab("parallel pods") + - ylab("Boot time (s)") + - ggtitle("Deployment boot time (detail)") + - #ylim(0, NA) + # For big machines, better to not 0-index - theme(axis.text.x=element_text(angle=90)) - - if ( skip_points_enable_smooth == 0 ) { - boot_line_plot = boot_line_plot + geom_point(alpha=0.3) - } else { - boot_line_plot = bool_line_plot + geom_smooth(se=FALSE, method="loess", size=0.3) - } - - # And get a zero Y index plot. - boot_line_plot_zero = boot_line_plot + ylim(0, NA) + - ggtitle("Deployment boot time (0 index)") - -# Show how boot time changed -delete_line_plot <- ggplot( data=data, aes(npod, delete_time, colour=testname, group=dataset)) + - geom_line(alpha=0.2) + - xlab("parallel pods") + - ylab("Delete time (s)") + - ggtitle("Deployment deletion time (detail)") + - #ylim(0, NA) + # For big machines, better to not 0-index - theme(axis.text.x=element_text(angle=90)) - - if ( skip_points_enable_smooth == 0 ) { - delete_line_plot = delete_line_plot + geom_point(alpha=0.3) - } else { - delete_line_plot = delete_line_plot + geom_smooth(se=FALSE, method="loess", size=0.3) - } - - # And get a 0 indexed Y axis plot - delete_line_plot_zero = delete_line_plot + ylim(0, NA) + - ggtitle("Deployment deletion time (0 index)") - -# See https://www.r-bloggers.com/ggplot2-easy-way-to-mix-multiple-graphs-on-the-same-page/ for -# excellent examples -master_plot = grid.arrange( - boot_line_plot_zero, - delete_line_plot_zero, - boot_line_plot, - delete_line_plot, - nrow=2, - ncol=2 ) - +render_parallel() diff --git a/metrics/report/report_dockerfile/test.R b/metrics/report/report_dockerfile/test.R new file mode 100644 index 0000000..8c9dabe --- /dev/null +++ b/metrics/report/report_dockerfile/test.R @@ -0,0 +1,9 @@ + +suppressMessages(library(jsonlite)) # to load the data. + +options(digits=22) + +x=fromJSON('{"ns": 1567002188374607769}') + +print(x) +print(fromJSON('{"ns": 1567002188374607769}'), digits=22) diff --git a/metrics/report/report_dockerfile/tidy_scaling.R b/metrics/report/report_dockerfile/tidy_scaling.R index ff5dbc2..6a4157e 100755 --- a/metrics/report/report_dockerfile/tidy_scaling.R +++ b/metrics/report/report_dockerfile/tidy_scaling.R @@ -205,7 +205,7 @@ render_tidy_scaling <- function() # Check if we got any stats at all by checking the memstats data. If we found no data, # abort early and nicely if ( length(memstats) == 0 ) { - cat("No results files found for scaling tests\n") + cat("No results files found for scaling tests\n\n") return() }